kamailio-4.0.4/0000755000000000000000000000000012223032463012000 5ustar rootrootkamailio-4.0.4/xavp.c0000644000000000000000000003217412223032461013127 0ustar rootroot/* * $Id$ * * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com) * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * History: * -------- * 2009-05-20 created by daniel */ #ifdef WITH_XAVP #include #include #include "mem/shm_mem.h" #include "dprint.h" #include "hashes.h" #include "xavp.h" /*! XAVP list head */ static sr_xavp_t *_xavp_list_head = 0; /*! Pointer to XAVP current list */ static sr_xavp_t **_xavp_list_crt = &_xavp_list_head; /*! Helper functions */ static sr_xavp_t *xavp_get_internal(str *name, sr_xavp_t **list, int idx, sr_xavp_t **prv); static int xavp_rm_internal(str *name, sr_xavp_t **head, int idx); void xavp_shm_free(void *p) { shm_free(p); } void xavp_shm_free_unsafe(void *p) { shm_free_unsafe(p); } void xavp_free(sr_xavp_t *xa) { if(xa->val.type == SR_XTYPE_DATA) { if(xa->val.v.data!=NULL && xa->val.v.data->pfree!=NULL) { xa->val.v.data->pfree(xa->val.v.data->p, xavp_shm_free); shm_free(xa->val.v.data); } } else if(xa->val.type == SR_XTYPE_XAVP) { xavp_destroy_list(&xa->val.v.xavp); } shm_free(xa); } void xavp_free_unsafe(sr_xavp_t *xa) { if(xa->val.type == SR_XTYPE_DATA) { if(xa->val.v.data!=NULL && xa->val.v.data->pfree!=NULL) { xa->val.v.data->pfree(xa->val.v.data->p, xavp_shm_free_unsafe); shm_free_unsafe(xa->val.v.data); } } else if(xa->val.type == SR_XTYPE_XAVP) { xavp_destroy_list_unsafe(&xa->val.v.xavp); } shm_free_unsafe(xa); } static sr_xavp_t *xavp_new_value(str *name, sr_xval_t *val) { sr_xavp_t *avp; int size; unsigned int id; if(name==NULL || name->s==NULL || val==NULL) return NULL; id = get_hash1_raw(name->s, name->len); size = sizeof(sr_xavp_t) + name->len + 1; if(val->type == SR_XTYPE_STR) size += val->v.s.len + 1; avp = (sr_xavp_t*)shm_malloc(size); if(avp==NULL) return NULL; memset(avp, 0, size); avp->id = id; avp->name.s = (char*)avp + sizeof(sr_xavp_t); memcpy(avp->name.s, name->s, name->len); avp->name.s[name->len] = '\0'; avp->name.len = name->len; memcpy(&avp->val, val, sizeof(sr_xval_t)); if(val->type == SR_XTYPE_STR) { avp->val.v.s.s = avp->name.s + avp->name.len + 1; memcpy(avp->val.v.s.s, val->v.s.s, val->v.s.len); avp->val.v.s.s[val->v.s.len] = '\0'; avp->val.v.s.len = val->v.s.len; } return avp; } int xavp_add(sr_xavp_t *xavp, sr_xavp_t **list) { if (xavp==NULL) return -1; /* Prepend new xavp to the list */ if(list) { xavp->next = *list; *list = xavp; } else { xavp->next = *_xavp_list_crt; *_xavp_list_crt = xavp; } return 0; } int xavp_add_last(sr_xavp_t *xavp, sr_xavp_t **list) { sr_xavp_t *prev; sr_xavp_t *crt; if (xavp==NULL) return -1; crt = xavp_get_internal(&xavp->name, list, 0, 0); prev = NULL; while(crt) { prev = crt; crt = xavp_get_next(prev); } if(prev==NULL) { /* Prepend new xavp to the list */ if(list) { xavp->next = *list; *list = xavp; } else { xavp->next = *_xavp_list_crt; *_xavp_list_crt = xavp; } } else { xavp->next = prev->next; prev->next = xavp; } return 0; } sr_xavp_t *xavp_add_value(str *name, sr_xval_t *val, sr_xavp_t **list) { sr_xavp_t *avp=0; avp = xavp_new_value(name, val); if (avp==NULL) return NULL; /* Prepend new value to the list */ if(list) { avp->next = *list; *list = avp; } else { avp->next = *_xavp_list_crt; *_xavp_list_crt = avp; } return avp; } sr_xavp_t *xavp_add_xavp_value(str *rname, str *name, sr_xval_t *val, sr_xavp_t **list) { sr_xavp_t *ravp=0; sr_xavp_t *cavp=0; sr_xval_t rval; cavp = xavp_new_value(name, val); if (cavp==NULL) return NULL; memset(&rval, 0, sizeof(sr_xval_t)); rval.type = SR_XTYPE_XAVP; rval.v.xavp = cavp; ravp = xavp_new_value(rname, &rval); if (ravp==NULL) { xavp_destroy_list(&cavp); return NULL; } /* Prepend new value to the list */ if(list) { ravp->next = *list; *list = ravp; } else { ravp->next = *_xavp_list_crt; *_xavp_list_crt = ravp; } return ravp; } sr_xavp_t *xavp_set_value(str *name, int idx, sr_xval_t *val, sr_xavp_t **list) { sr_xavp_t *avp; sr_xavp_t *cur; sr_xavp_t *prv=0; if(val==NULL) return NULL; /* Find the current value */ cur = xavp_get_internal(name, list, idx, &prv); if(cur==NULL) return NULL; avp = xavp_new_value(name, val); if (avp==NULL) return NULL; /* Replace the current value with the new */ avp->next = cur->next; if(prv) prv->next = avp; else if(list) *list = avp; else *_xavp_list_crt = avp; xavp_free(cur); return avp; } static sr_xavp_t *xavp_get_internal(str *name, sr_xavp_t **list, int idx, sr_xavp_t **prv) { sr_xavp_t *avp; unsigned int id; int n = 0; if(name==NULL || name->s==NULL) return NULL; id = get_hash1_raw(name->s, name->len); if(list && *list) avp = *list; else avp = *_xavp_list_crt; while(avp) { if(avp->id==id && avp->name.len==name->len && strncmp(avp->name.s, name->s, name->len)==0) { if(idx==n) return avp; n++; } if(prv) *prv = avp; avp = avp->next; } return NULL; } sr_xavp_t *xavp_get(str *name, sr_xavp_t *start) { return xavp_get_internal(name, (start)?&start:NULL, 0, NULL); } sr_xavp_t *xavp_get_by_index(str *name, int idx, sr_xavp_t **start) { return xavp_get_internal(name, start, idx, NULL); } sr_xavp_t *xavp_get_next(sr_xavp_t *start) { sr_xavp_t *avp; if(start==NULL) return NULL; avp = start->next; while(avp) { if(avp->id==start->id && avp->name.len==start->name.len && strncmp(avp->name.s, start->name.s, start->name.len)==0) return avp; avp=avp->next; } return NULL; } int xavp_rm(sr_xavp_t *xa, sr_xavp_t **head) { sr_xavp_t *avp; sr_xavp_t *prv=0; if(head!=NULL) avp = *head; else avp=*_xavp_list_crt; while(avp) { if(avp==xa) { if(prv) prv->next=avp->next; else if(head!=NULL) *head = avp->next; else *_xavp_list_crt = avp->next; xavp_free(avp); return 1; } prv=avp; avp=avp->next; } return 0; } /* Remove xavps * idx: <0 remove all xavps with the same name * >=0 remove only the specified index xavp * Returns number of xavps that were deleted */ static int xavp_rm_internal(str *name, sr_xavp_t **head, int idx) { sr_xavp_t *avp; sr_xavp_t *foo; sr_xavp_t *prv=0; unsigned int id; int n=0; int count=0; if(name==NULL || name->s==NULL) return 0; id = get_hash1_raw(name->s, name->len); if(head!=NULL) avp = *head; else avp = *_xavp_list_crt; while(avp) { foo = avp; avp=avp->next; if(foo->id==id && foo->name.len==name->len && strncmp(foo->name.s, name->s, name->len)==0) { if(idx<0 || idx==n) { if(prv!=NULL) prv->next=foo->next; else if(head!=NULL) *head = foo->next; else *_xavp_list_crt = foo->next; xavp_free(foo); if(idx>=0) return 1; count++; } n++; } else { prv = foo; } } return count; } int xavp_rm_by_name(str *name, int all, sr_xavp_t **head) { return xavp_rm_internal(name, head, -1*all); } int xavp_rm_by_index(str *name, int idx, sr_xavp_t **head) { if (idx<0) return 0; return xavp_rm_internal(name, head, idx); } int xavp_count(str *name, sr_xavp_t **start) { sr_xavp_t *avp; unsigned int id; int n = 0; if(name==NULL || name->s==NULL) return -1; id = get_hash1_raw(name->s, name->len); if(start) avp = *start; else avp=*_xavp_list_crt; while(avp) { if(avp->id==id && avp->name.len==name->len && strncmp(avp->name.s, name->s, name->len)==0) { n++; } avp=avp->next; } return n; } void xavp_destroy_list_unsafe(sr_xavp_t **head) { sr_xavp_t *avp, *foo; avp = *head; while(avp) { foo = avp; avp = avp->next; xavp_free_unsafe(foo); } *head = 0; } void xavp_destroy_list(sr_xavp_t **head) { sr_xavp_t *avp, *foo; LM_DBG("destroying xavp list %p\n", *head); avp = *head; while(avp) { foo = avp; avp = avp->next; xavp_free(foo); } *head = 0; } void xavp_reset_list(void) { assert(_xavp_list_crt!=0 ); if (_xavp_list_crt!=&_xavp_list_head) _xavp_list_crt=&_xavp_list_head; xavp_destroy_list(_xavp_list_crt); } sr_xavp_t **xavp_set_list(sr_xavp_t **head) { sr_xavp_t **avp; assert(_xavp_list_crt!=0); avp = _xavp_list_crt; _xavp_list_crt = head; return avp; } sr_xavp_t **xavp_get_crt_list(void) { assert(_xavp_list_crt!=0); return _xavp_list_crt; } void xavp_print_list_content(sr_xavp_t **head, int level) { sr_xavp_t *avp=0; sr_xavp_t *start=0; if(head!=NULL) start = *head; else start=*_xavp_list_crt; LM_INFO("+++++ start XAVP list: %p (level=%d)\n", start, level); avp = start; while(avp) { LM_INFO(" *** XAVP name: %s\n", avp->name.s); LM_INFO(" XAVP id: %u\n", avp->id); LM_INFO(" XAVP value type: %d\n", avp->val.type); switch(avp->val.type) { case SR_XTYPE_NULL: LM_INFO(" XAVP value: \n"); break; case SR_XTYPE_INT: LM_INFO(" XAVP value: %d\n", avp->val.v.i); break; case SR_XTYPE_STR: LM_INFO(" XAVP value: %s\n", avp->val.v.s.s); break; case SR_XTYPE_TIME: LM_INFO(" XAVP value: %lu\n", (long unsigned int)avp->val.v.t); break; case SR_XTYPE_LONG: LM_INFO(" XAVP value: %ld\n", avp->val.v.l); break; case SR_XTYPE_LLONG: LM_INFO(" XAVP value: %lld\n", avp->val.v.ll); break; case SR_XTYPE_XAVP: LM_INFO(" XAVP value: \n", avp->val.v.xavp); xavp_print_list_content(&avp->val.v.xavp, level+1); break; case SR_XTYPE_DATA: LM_INFO(" XAVP value: \n", avp->val.v.data); break; } avp = avp->next; } LM_INFO("----- end XAVP list: %p (level=%d)\n", start, level); } void xavp_print_list(sr_xavp_t **head) { xavp_print_list_content(head, 0); } /** * clone the xavp without values that are custom data * - only one list level is cloned, other sublists are ignored */ sr_xavp_t *xavp_clone_level_nodata(sr_xavp_t *xold) { sr_xavp_t *xnew = NULL; sr_xavp_t *navp = NULL; sr_xavp_t *oavp = NULL; sr_xavp_t *pavp = NULL; if(xold == NULL) { return NULL; } if(xold->val.type==SR_XTYPE_DATA) { LM_INFO("xavp value type is 'data' - ignoring in clone\n"); return NULL; } xnew = xavp_new_value(&xold->name, &xold->val); if(xnew==NULL) { LM_ERR("cannot create cloned root xavp\n"); return NULL; } if(xold->val.type!=SR_XTYPE_XAVP) { return xnew; } xnew->val.v.xavp = NULL; oavp = xold->val.v.xavp; while(oavp) { if(oavp->val.type!=SR_XTYPE_DATA && oavp->val.type!=SR_XTYPE_XAVP) { navp = xavp_new_value(&oavp->name, &oavp->val); if(navp==NULL) { LM_ERR("cannot create cloned embedded xavp\n"); if(xnew->val.v.xavp == NULL) { shm_free(xnew); return NULL; } else { xavp_destroy_list(&navp); return NULL; } } if(xnew->val.v.xavp == NULL) { /* link to val in head xavp */ xnew->val.v.xavp = navp; pavp = navp; } else { /* link to prev xavp in the list */ pavp->next = navp; } } oavp = oavp->next; } if(xnew->val.v.xavp == NULL) { shm_free(xnew); return NULL; } return xnew; } int xavp_insert(sr_xavp_t *xavp, int idx, sr_xavp_t **list) { sr_xavp_t *crt = 0; sr_xavp_t *fst = 0; sr_xavp_t *lst = 0; sr_xval_t val; int n = 0; int i = 0; if(idx==0) return xavp_add(xavp, list); crt = xavp_get_internal(&xavp->name, list, 0, 0); while(crt!=NULL && nname, &val, list); if(crt==NULL) return -1; if(fst==NULL) fst = crt; if(lst==NULL) { if(xavp_add(crt, list)<0) return -1; } else { crt->next = lst->next; lst->next = crt; } } if(fst==NULL) { return xavp_add(xavp, list); } else { xavp->next = fst->next; fst->next = xavp; } return 0; } sr_xavp_t *xavp_extract(str *name, sr_xavp_t **list) { sr_xavp_t *avp = 0; sr_xavp_t *foo; sr_xavp_t *prv = 0; unsigned int id; if(name==NULL || name->s==NULL) { if(list!=NULL) { avp = *list; if(avp!=NULL) { *list = avp->next; avp->next = NULL; } } else { avp = *_xavp_list_crt; if(avp!=NULL) { *_xavp_list_crt = avp->next; avp->next = NULL; } } return avp; } id = get_hash1_raw(name->s, name->len); if(list!=NULL) avp = *list; else avp = *_xavp_list_crt; while(avp) { foo = avp; avp=avp->next; if(foo->id==id && foo->name.len==name->len && strncmp(foo->name.s, name->s, name->len)==0) { if(prv!=NULL) prv->next=foo->next; else if(list!=NULL) *list = foo->next; else *_xavp_list_crt = foo->next; foo->next = NULL; return foo; } else { prv = foo; } } return NULL; } #endif kamailio-4.0.4/obsolete/0000755000000000000000000000000012223032460013611 5ustar rootrootkamailio-4.0.4/obsolete/permissions/0000755000000000000000000000000012223032461016165 5ustar rootrootkamailio-4.0.4/obsolete/permissions/ip_set_rpc.c0000644000000000000000000001625612223032461020472 0ustar rootroot/* * $Id$ * * allow_trusted related functions * * Copyright (C) 2003 Juha Heinanen * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2008-08-01: released */ #include "../../dprint.h" #include "../../lib/srdb2/db.h" #include "../../mem/shm_mem.h" #include "permissions.h" #include "ip_set.h" #include "ip_set_rpc.h" static struct ip_set_list_item *ip_set_list = NULL; static int ip_set_list_count = 0; int ip_set_list_malloc(int num, str* names) { int i, j; if (num) { ip_set_list = shm_malloc(num*sizeof(*ip_set_list)); if (!ip_set_list) return -1; ip_set_list_count = num; for (i=0; irefcnt, 1); ip_set_add_list(&ip_set_list[i].ip_set->ip_set, s); /* allow pass even in case of error */ } } } } return 0; } void ip_set_list_free() { int i; if (!ip_set_list) return; for (i=0; irefcnt)) { /* do not destroy cloned sets because if they can live only in local copy after commit, they must be deleted separately in local copy before this procedure is called*/ ip_set_destroy(&ip_set_list[i].ip_set->ip_set); shm_free(ip_set_list[i].ip_set); } } ip_set_destroy(&ip_set_list[i].ip_set_pending); } shm_free(ip_set_list); ip_set_list = NULL; } struct ip_set_list_item* ip_set_list_find_by_name(str name) { int i; for (i=0; iscan(ctx, "S", &name) < 1) { return; } p = ip_set_list_find_by_name(name); if (!p) { rpc->fault(ctx, 400, "Ip set not found"); return; } lock_get(&p->write_lock); ip_set_destroy(&p->ip_set_pending); ip_set_init(&p->ip_set_pending, 1); lock_release(&p->write_lock); } const char* rpc_ip_set_add_doc[] = { "Add IP/mask in ip set.", 0 }; void rpc_ip_set_add(rpc_t* rpc, void* ctx) { str name, ip, mask; struct ip_set_list_item* p; if (rpc->scan(ctx, "SSS", &name, &ip, &mask) < 1) { return; } while (mask.len && mask.s[0]=='/') { /* rpc cannot read plain number as string, adding '/' helps */ mask.s++; mask.len--; } p = ip_set_list_find_by_name(name); if (!p) { rpc->fault(ctx, 400, "Ip set not found"); return; } lock_get(&p->write_lock); if (ip_set_add_ip_s(&p->ip_set_pending, ip, mask) < 0) { lock_release(&p->write_lock); rpc->fault(ctx, 400, "Cannot add ip/mask into ip set"); return; } lock_release(&p->write_lock); } const char* rpc_ip_set_commit_doc[] = { "Commit changes in ip set.", 0 }; void rpc_ip_set_commit(rpc_t* rpc, void* ctx) { str name; struct ip_set_list_item *p; struct ip_set_ref *new_ip_set; if (rpc->scan(ctx, "S", &name) < 1) { return; } p = ip_set_list_find_by_name(name); if (!p) { rpc->fault(ctx, 400, "Ip set not found"); return; } lock_get(&p->write_lock); lock_get(&p->read_lock); new_ip_set = shm_malloc(sizeof(*new_ip_set)); if (!new_ip_set) { rpc->fault(ctx, 500, "Not enough memory"); goto err; } if (p->ip_set) { if (atomic_dec_and_test(&p->ip_set->refcnt)) { ip_set_destroy(&p->ip_set->ip_set); /* not used in local copy */ shm_free(p->ip_set); } } new_ip_set->ip_set = p->ip_set_pending; atomic_set(&new_ip_set->refcnt, 1); p->ip_set = new_ip_set; ip_set_init(&p->ip_set_pending, 1); err: lock_release(&p->read_lock); lock_release(&p->write_lock); } const char* rpc_ip_set_list_doc[] = { "List ip set names.", 0 }; void rpc_ip_set_list(rpc_t* rpc, void* ctx) { int i; void *c; rpc->add(ctx, "{", &c); for (i=0; istruct_add(c, "S", "Name", &ip_set_list[i].name) < 0) { rpc->fault(ctx, 500, "Error when listing ip sets"); } } } const char* rpc_ip_set_print_doc[] = { "Print ip set.", 0 }; static int rpc_ip_tree_print(rpc_t* rpc, void *ctx, char *prefix, struct ip_tree_leaf *tree, unsigned int indent) { int j; if (!tree) { if (rpc->struct_printf(ctx, "", "%*snil", indent, prefix) < 0) return -1; } else { str s; s = ip_tree_mask_to_str(tree->prefix_match, tree->prefix_match_len); if (rpc->struct_printf(ctx, "", "%*smatch %d bits {%.*s}", indent, prefix, tree->prefix_match_len, s.len, s.s) < 0) return -1; for (j=0; j<=1; j++) { if (rpc_ip_tree_print(rpc, ctx, j==0?"0:":"1:", tree->next[j], indent+2) < 0) return -1; } } return 0; } void rpc_ip_set_print(rpc_t* rpc, void* ctx) { struct ip_set_list_item *p; struct ip_set *ip_set, ip_set2; void *c; str name; int pending; if (rpc->scan(ctx, "Sd", &name, &pending) < 1) { return; } p = ip_set_list_find_by_name(name); if (!p) { rpc->fault(ctx, 400, "Ip set not found"); return; } if (pending) { lock_get(&p->write_lock); ip_set = &p->ip_set_pending; } else { lock_get(&p->read_lock); if (!p->ip_set) { ip_set_init(&ip_set2, 1); /* dummy to return empty ip set */ ip_set = &ip_set2; } else ip_set = &p->ip_set->ip_set; } /* nested array/struct not supported */ rpc->add(ctx, "{", &c); if (rpc->struct_add(c, "s", "IPv", "4") < 0) goto err; if (rpc_ip_tree_print(rpc, c, "", ip_set->ipv4_tree, 0) < 0) goto err; rpc->add(ctx, "{", &c); if (rpc->struct_add(c, "s", "IPv", "6") < 0) goto err; #ifdef USE_IPV6 if (rpc_ip_tree_print(rpc, c, "", ip_set->ipv6_tree, 0) < 0) goto err; #endif err: if (pending) lock_release(&p->write_lock); else lock_release(&p->read_lock); } kamailio-4.0.4/obsolete/permissions/ip_tree.c0000644000000000000000000001561112223032460017763 0ustar rootroot/* $Id$ * * Copyright (C) 2008 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "ip_tree.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include #include void ip_tree_init(struct ip_tree_leaf **tree) { *tree = NULL; } void ip_tree_destroy(struct ip_tree_leaf **tree, int leaves_only, int use_shm) { int i; if (*tree) { for (i=0; i<=1; i++) { if ((*tree)->next[i]) ip_tree_destroy(&(*tree)->next[i], 0, use_shm); } if (!leaves_only) { if (use_shm) shm_free(*tree); else pkg_free(*tree); *tree = NULL; } } } int ip_tree_find_ip(struct ip_tree_leaf *tree, unsigned char *ip, unsigned int ip_len, struct ip_tree_find *h) { h->leaf = tree; h->ip = ip; h->ip_len = ip_len; h->ip_mask = 0x80; if (!tree) return IP_TREE_FIND_NOT_FOUND; do { h->leaf_prefix_match_mask = 0x80; h->leaf_prefix_match = &h->leaf->prefix_match[0]; h->leaf_prefix_match_len = 0; if (h->ip_len == 0) return IP_TREE_FIND_FOUND_UPPER_SET; while (h->leaf_prefix_match_len < h->leaf->prefix_match_len) { if (((*(h->ip) & h->ip_mask) == 0) != ((*(h->leaf_prefix_match) & h->leaf_prefix_match_mask) == 0)) { return IP_TREE_FIND_NOT_FOUND; } h->leaf_prefix_match_len++; h->ip_len--; if (unlikely(h->ip_len == 0)) return IP_TREE_FIND_FOUND_UPPER_SET; if (unlikely(h->ip_mask == 0x01)) { h->ip_mask = 0x80; h->ip++; } else { h->ip_mask /= 2; /* >> 1 */ } if (unlikely(h->leaf_prefix_match_mask == 0x01)) { h->leaf_prefix_match_mask = 0x80; h->leaf_prefix_match++; } else { h->leaf_prefix_match_mask /= 2; /* >> 1 */ } } h->leaf = h->leaf->next[(*(h->ip) & h->ip_mask) != 0]; if (unlikely(h->ip_mask == 0x01)) { h->ip_mask = 0x80; h->ip++; } else { h->ip_mask /= 2; /* >> 1 */ } h->ip_len--; } while (h->leaf); return IP_TREE_FIND_FOUND; } static inline struct ip_tree_leaf *ip_tree_malloc_leaf(unsigned int ip_len, int use_shm) { int n; n = sizeof(struct ip_tree_leaf)+((ip_len==0)?0:((ip_len-1)/8 +1)); if (use_shm) return shm_malloc(n); else return pkg_malloc(n); } int ip_tree_add_ip(struct ip_tree_leaf **tree, unsigned char *ip, unsigned int ip_len, int use_shm) { struct ip_tree_find h; struct ip_tree_leaf *l0, *l1; int ret, i, n; unsigned char mask, *pm; ret = ip_tree_find_ip(*tree, ip, ip_len, &h); switch (ret) { case IP_TREE_FIND_FOUND_UPPER_SET: /* ip covers wider subnet range than already defined range, we can delete all subleaves and reduce prefix match match */ h.leaf->prefix_match_len = h.leaf_prefix_match_len; ip_tree_destroy(&h.leaf, 1, use_shm); break; case IP_TREE_FIND_FOUND: /* ip is already in set */ break; case IP_TREE_FIND_NOT_FOUND: if (h.leaf) { /* split leaf into two leaves */ n = h.ip_len - 1; l1 = ip_tree_malloc_leaf(n, use_shm); if (!l1) return -1; l1->prefix_match_len = n; for (i=0; i<=1; i++) l1->next[i] = NULL; n = h.leaf->prefix_match_len - h.leaf_prefix_match_len - 1; l0 = ip_tree_malloc_leaf(n, use_shm); if (!l0) { ip_tree_destroy(&l1, 0, use_shm); return -1; } l0->prefix_match_len = n; for (i=0; i<=1; i++) l0->next[i] = h.leaf->next[i]; i = (*h.leaf_prefix_match & h.leaf_prefix_match_mask) != 0; h.leaf->next[i] = l0; h.leaf->next[!i] = l1; n = h.leaf_prefix_match_len; /* copy remaining leaf prefix match bits, first non matched bit is treated in next decision therefore is skipped */ mask = 0x80; pm = l0->prefix_match; while (1) { h.leaf_prefix_match_len++; if (h.leaf_prefix_match_len >= h.leaf->prefix_match_len) break; if (unlikely(h.leaf_prefix_match_mask == 0x01)) { h.leaf_prefix_match_mask = 0x80; h.leaf_prefix_match++; } else { h.leaf_prefix_match_mask /= 2; /* >> 1 */ } if (mask == 0x80) *pm = 0x00; if ((*h.leaf_prefix_match) & h.leaf_prefix_match_mask) *pm |= mask; if (mask == 0x01) { mask = 0x80; pm++; } else { mask /= 2; /* >> 1 */ } } h.leaf->prefix_match_len = n; /* copy remaining ip bits, first non matched bit is treated in next decision therefore is skipped */ mask = 0x80; pm = l1->prefix_match; while (1) { h.ip_len--; if (h.ip_len <= 0) break; if (unlikely(h.ip_mask == 0x01)) { h.ip_mask = 0x80; h.ip++; } else { h.ip_mask /= 2; /* >> 1 */ } if (mask == 0x80) *pm = 0x00; if ((*h.ip) & h.ip_mask) *pm |= mask; if (mask == 0x01) { mask = 0x80; pm++; } else { mask /= 2; /* >> 1 */ } } } else { /* it's first leaf in tree */ *tree = ip_tree_malloc_leaf(ip_len, use_shm); if (!*tree) return -1; (*tree)->prefix_match_len = ip_len; if (ip_len > 0) { for (i = 0; i <= (ip_len -1) / 8; i++) { (*tree)->prefix_match[i] = ip[i]; } } for (i=0; i<=1; i++) (*tree)->next[i] = NULL; } break; default: /* BUG */ ret = -1; } return ret; } void ip_tree_print(FILE *stream, struct ip_tree_leaf *tree, unsigned int indent) { unsigned int i, j; if (!tree) { fprintf(stream, "nil\n"); } else { str s; s = ip_tree_mask_to_str(tree->prefix_match, tree->prefix_match_len); fprintf(stream, "match %d bits {%.*s}\n", tree->prefix_match_len, s.len, s.s); for (j=0; j<=1; j++) { for (i=0; inext[j], indent+2); } } } str ip_tree_mask_to_str(unsigned char *pm, unsigned int len) { unsigned char mask; unsigned int i; static char buf[129]; str s; s.s = buf; if (len>=sizeof(buf)) len = sizeof(buf)-1; s.len = len; buf[len] = '\0'; for (i=0, mask=0x80; i #include #include "../../mem/shm_mem.h" #include "../../parser/parse_from.h" #include "../../ut.h" #include "trusted_hash.h" /* * Create and initialize a hash table */ struct trusted_list** new_hash_table(void) { struct trusted_list** ptr; /* Initializing hash tables and hash table variable */ ptr = (struct trusted_list **)shm_malloc(sizeof(struct trusted_list*) * HASH_SIZE); if (!ptr) { LOG(L_ERR, "new_hash_table(): No memory for hash table\n"); return 0; } memset(ptr, 0, sizeof(struct trusted_list*) * HASH_SIZE); return ptr; } /* * Release all memory allocated for a hash table */ void free_hash_table(struct trusted_list** table) { if (table) { empty_hash_table(table); } shm_free(table); } /* * String hash function */ static unsigned int hash(str* src_ip) { char *p; unsigned int h = 0; unsigned int len; unsigned int i; p = src_ip->s; len = src_ip->len; for (i = 0; i < len; i++) { h = ( h << 5 ) - h + *(p + i); } return h % HASH_SIZE; } /* * Add into hash table, where proto is integer * representation of string argument proto. */ int hash_table_insert(struct trusted_list** hash_table, char* src_ip, char* proto, char* pattern) { struct trusted_list *np; unsigned int hash_val; np = (struct trusted_list *) shm_malloc(sizeof(*np)); if (np == NULL) { LOG(L_CRIT, "hash_table_insert(): Cannot allocate memory for table entry\n"); return -1; } np->src_ip.len = strlen(src_ip); np->src_ip.s = (char *) shm_malloc(np->src_ip.len); if (np->src_ip.s == NULL) { LOG(L_CRIT, "hash_table_insert(): Cannot allocate memory for src_ip string\n"); return -1; } (void) strncpy(np->src_ip.s, src_ip, np->src_ip.len); if (strcmp(proto, "any") == 0) { np->proto = PROTO_NONE; } else if (strcmp(proto, "udp") == 0) { np->proto = PROTO_UDP; } else if (strcmp(proto, "tcp") == 0) { np->proto = PROTO_TCP; } else if (strcmp(proto, "tls") == 0) { np->proto = PROTO_TLS; } else if (strcmp(proto, "sctp") == 0) { np->proto = PROTO_SCTP; } else { LOG(L_CRIT, "hash_table_insert(): Unknown protocol '%s'\n", proto); return -1; } np->pattern = (char *) shm_malloc(strlen(pattern)+1); if (np->pattern == NULL) { LOG(L_CRIT, "hash_table_insert(): Cannot allocate memory for pattern string\n"); return -1; } (void) strcpy(np->pattern, pattern); hash_val = hash(&(np->src_ip)); np->next = hash_table[hash_val]; hash_table[hash_val] = np; return 1; } /* * Check if an entry exists in hash table that has given src_ip and protocol * value and pattern that matches to From URI. */ int match_hash_table(struct trusted_list** table, struct sip_msg* msg) { str uri; char uri_string[MAX_URI_SIZE + 1]; regex_t preg; struct trusted_list *np; str src_ip; src_ip.s = ip_addr2a(&msg->rcv.src_ip); src_ip.len = strlen(src_ip.s); if (parse_from_header(msg) < 0) return -1; uri = get_from(msg)->uri; if (uri.len > MAX_URI_SIZE) { LOG(L_ERR, "match_hash_table(): From URI too large\n"); return -1; } memcpy(uri_string, uri.s, uri.len); uri_string[uri.len] = (char)0; for (np = table[hash(&src_ip)]; np != NULL; np = np->next) { if ((np->src_ip.len == src_ip.len) && (strncasecmp(np->src_ip.s, src_ip.s, src_ip.len) == 0) && ((np->proto == PROTO_NONE) || (np->proto == msg->rcv.proto))) { if (regcomp(&preg, np->pattern, REG_NOSUB)) { LOG(L_ERR, "match_hash_table(): Error in regular expression\n"); return -1; } if (regexec(&preg, uri_string, 0, (regmatch_t *)0, 0)) { regfree(&preg); } else { regfree(&preg); return 1; } } } return -1; } /* * Print domains stored in hash table */ void hash_table_print(struct trusted_list** hash_table, rpc_t* rpc, void* c) { void* st; int i; struct trusted_list *np; for (i = 0; i < HASH_SIZE; i++) { np = hash_table[i]; while (np) { if (rpc->add(c, "{", &st) < 0) return; rpc->struct_add(st, "Sds", "src_ip", &np->src_ip, "proto", np->proto, "pattern", np->pattern); np = np->next; } } } /* * Free contents of hash table, it doesn't destroy the * hash table itself */ void empty_hash_table(struct trusted_list **hash_table) { int i; struct trusted_list *np, *next; for (i = 0; i < HASH_SIZE; i++) { np = hash_table[i]; while (np) { shm_free(np->src_ip.s); shm_free(np->pattern); next = np->next; shm_free(np); np = next; } hash_table[i] = 0; } } kamailio-4.0.4/obsolete/permissions/doc/0000755000000000000000000000000012223032460016731 5ustar rootrootkamailio-4.0.4/obsolete/permissions/doc/permissions.xml0000644000000000000000000001357212223032460022036 0ustar rootroot
Miklos Tirpak
mtirpak@sztaki.hu
Juha Heinanen
jh@tutpro.com
2003 Miklos Tirpak Juha Heinanen 2006 iptelorg GmbH
Permissions Module
Overview
Call Routing The module can be used to determine if a call has appropriate permission to be established. Permission rules are stored in plaintext configuration files similar to hosts.allow and hosts.deny files used by tcpd. When allow_routing function is called it tries to find a rule that matches selected fields of the message. SER is a forking proxy and therefore a single message can be sent to different destinations simultaneously. When checking permissions all the destinations must be checked and if one of them fails, the forwarding will fail. The matching algorithm is as follows, first match wins: Create a set of pairs of form (From, R-URI of branch 1), (From, R-URI of branch 2), etc. Routing will be allowed when all pairs match an entry in the allow file. Otherwise routing will be denied when one of pairs matches an entry in the deny file. Otherwise, routing will be allowed. A non-existing permission control file is treated as if it were an empty file. Thus, permission control can be turned off by providing no permission control files. From header field and Request-URIs are always compared with regular expressions! For the syntax see the sample file: config/permissions.allow.
Registration Permissions In addition to call routing it is also possible to check REGISTER messages and decide--based on the configuration files--whether the message should be allowed and the registration accepted or not. Main purpose of the function is to prevent registration of "prohibited" IP addresses. One example, when a malicious user registers a contact containing IP address of a PSTN gateway, he might be able to bypass authorization checks performed by the SIP proxy. That is undesirable and therefore attempts to register IP address of a PSTN gateway should be rejected. Files config/register.allow and config/register.deny contain an example configuration. Function for registration checking is called allow_register and the algorithm is very similar to the algorithm described in . The only difference is in the way how pairs are created. Instead of From header field the function uses To header field because To header field in REGISTER messages contains the URI of the person being registered. Instead of the Request-URI of branches the function uses Contact header field. Thus, pairs used in matching will look like this: (To, Contact 1), (To, Contact 2), (To, Contact 3), and so on.. The algorithm of matching is same as described in .
Refer-To Permissions In addition to call routing and REGISTER it is also possible to check REFER messages and decide--based on the configuration files-- whether or not the message should be accepted for forwarding. Main purpose of the function is to prevent referring a SIP UA to "prohibited" IP addresses. One example is user sending a REFER request to PSTN gateway trying to refer it to an expensive phone number. Function for Refer-To checking is called allow_refer_to and the algorithm is very similar to the algorithm described in Section 1.1.1. The only difference is in the way how pairs are created. Instead of Request-URI the function uses Refer-To header field because Refer-To header field in REFER messages contains the URI to which the UA is being referred to. The algorithm of matching is same as described in Section 1.1.1.
Messages catching The module can be also used for catching messages coming from or going to specific network elements, for example gateways or peering partners. Users can register or forward the calls to the address of a gateway resulting unauthorized access to them. Such calls must be catched and dropped, see ipmatch functions for details.
kamailio-4.0.4/obsolete/permissions/doc/xmlrpc.xml0000644000000000000000000000311012223032460020753 0ustar rootroot
XMLRPC Interface Some functionality may be controled using RPC commands. ipmatch.reload - Reloads the cached ipmatch table. The original table remains active in case of any failure. ipset.clean(ipset_name) - Clear all entries in "pending" ipset. ipset.add(ipset_name, ip, netmask) - Add ip and mask into ipset. IPv6 should should be enclosed in brackets. Netmask may be identified as number or in IP form. Note that number requires leading slash, e.g. "/24" or "255.255.255.0". ipset.commit(ipset_name) - Makes pending ip set usable by ip_is_trusted. Pending ip set is cleared. ipset.list() - List declared ip sets. ipset.print(ipset_name, pending) - Dump ipset trees. If pending non zero then pending ipset is dumped.
kamailio-4.0.4/obsolete/permissions/doc/functions.xml0000644000000000000000000004666612223032460021505 0ustar rootroot
Functions
<function>allow_routing()</function> Returns true if all pairs constructed as described in have appropriate permissions according to the configuration files. This function uses default configuration files specified in default_allow_file and default_deny_file. <function>allow_routing</function> usage ... if (allow_routing()) { t_relay(); }; ...
<function>allow_routing(basename)</function> Returns true if all pairs constructed as described in have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. <function>allow_routing(basename)</function> usage ... if (allow_routing("basename")) { t_relay(); }; ...
<function>allow_routing(allow_file, deny_file)</function> Returns true if all pairs constructed as described in have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: allow_file - File containing allow rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. deny_file - File containing deny rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. <function>allow_routing(allow_file, deny_file)</function> usage ... if (allow_routing("rules.allow", "rules.deny")) { t_relay(); }; ...
<function>allow_register(basename)</function> The function returns true if all pairs constructed as described in have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. <function>allow_register(basename)</function> usage ... if (method=="REGISTER") { if (allow_register("register")) { save("location"); break; } else { sl_send_reply("403", "Forbidden"); }; }; ...
<function>allow_register(allow_file, deny_file)</function> The function returns true if all pairs constructed as described in have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: allow_file - File containing allow rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. deny_file - File containing deny rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. <function>allow_register(allow_file, deny_file)</function> usage ... if (method=="REGISTER") { if (allow_register("register.allow", "register.deny")) { save("location"); break; } else { sl_send_reply("403", "Forbidden"); }; }; ...
<function>allow_refer_to(basename)</function> The function returns true if all pairs constructed as described in have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. <function>allow_register(basename)</function> usage ... if (method=="REGISTER") { if (allow_register("register")) { save("location"); break; } else { sl_send_reply("403", "Forbidden"); }; }; ...
<function>allow_refer_to(allow_file, deny_file)</function> The function returns true if all pairs constructed as described in Section 1.1.2 have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: allow_file - File containing allow rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. deny_file - File containing deny rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. <function>allow_register(allow_file, deny_file)</function> usage ... if (method=="REFER") { if (allow_register("refer.allow", "refer.deny")) { ... } else { sl_send_reply("403", "Forbidden"); }; }; ...
<function>ipmatch (string/AVP/select, [avp])</function> The function tries to find an IP address and port pair (defined by the first function parameter) in the cached database table. Port is optional, it is compared only if both the function parameter and the database entry contain it. Meaning of the parameters is as follows: string/AVP/select - File containing allow rules. string: "src": the source address of the packet is used "via2": the ip address of the 2nd via line is used other values are not defined currently AVP: e.g. "$myavp" select call: e.g. "@via[0].host" The second parameter is optional, it is used to set an AVP value from the database. Suitable for assigning logical identifiers to gateways. Note that IPv6 addresses must be enclosed in square brackets in case of port is defined: [1111:2222::3333]:5060
<function>ipmatch_onsend (string)</function> ipmatch() function replacement for onsend_route block. The function accepts only string parameter, because even AVP reading is unsafe in onsend_route. Meaning of the parameter: "dst": the destination address is used "ruri": the ip:port pair is extracted from the Request URI The function can be used for example to catch unauthorized requests going to gateways: Use a flag to mark the call as PSTN either in request or in failure_route, and do not touch it in onreply_route. See the examples below.
<function>ipmatch_filter (unsigned int)</function> Entries in the database can be marked to group the different kind of network elements. The function sets the filter which is used on the mark while comparing the IP addresses. The mark must be the power of 2 in the database! Note that ipmatch() and ipmatch_onsend() functions reset the filter! <function>ipmatch_filter (unsigned int)</function> usage +-----------------+-------------------+------+------+ | ip | avp_val | mark | flag | +-----------------+-------------------+------+------+ | 1111:2222::1001 | first_gw | 1 | 1 | | 10.38.2.10:5060 | second_gw | 1 | 1 | | 10.0.0.10 | first_peering | 2 | 1 | +-----------------+-------------------+------+------+ route[0] { # is this a request from a GW? ipmatch_filter("1"); if (ipmatch("src", "$gw_id")) { # yes, it is from a GW ... } else { # is this a request from a peering partner? ipmatch_filter("2"); if (ipmatch("src", "$peering_id")) { # yes, it is from a peering partner ... }; } ... # request goes to PSTN setflag(PSTN); } onsend_route[0] { if (method == "INVITE" && (!isflagset(PSTN))) { # is this a request to a GW? ipmatch_filter("1"); if (ipmatch_onsend("dst")) { # request is not marked with PSTN flag, but it goes to a gateway drop; } } ... }
<function>ip_is_trusted(ip_set, ip)</function> The function returns true if ip is contained in ip_set. Both IPv4 and IPv6 are supported. Meaning of the parameters is as follows: ip_set is identified by comma/space/semicolon delimited list of IP addresses or subnet masks. The subnet mask is written in IP_slash_number form or as IP. If the mask is not mentioned then default value is taken, the default value is number of bits of particular IP address, i.e. 32 in case of IPv4, 128 in case of IPv6. Besides list also ip set identifier declared by declare_ipset may be provided. In this case ip is checked against set maintainded by RPC commands. ip to test. Besides direct address in string form there are extra identifiers to force IP related to current message: Source, Destination, Received. Note that only the first character is essential. <function>ip_is_trusted</function> usage modparam("permissions", "declare_ipset", "my_ipset1"); modparam("permissions", "declare_ipset", "my_ipset2=127.0.0.0/24;10.0.0.0/255.255.255.0"); route[TT2] { if (ip_is_trusted("$net", "$ip")) { xplog("L_E", "'%$ip' - TRUE\n"); } else { xplog("L_E", "'%$ip' - FALSE\n"); } } route[TT1] { xplog("L_E", "Testing netmask '%$net'\n"); $ip = "s"; # source address route(TT2); $ip = "127.0.0.1"; route(TT2); $ip = "127.0.0.2"; route(TT2); $ip = "10.0.0.1"; route(TT2); $ip = "11.0.0.1"; route(TT2); $ip = "172.1.8.1"; route(TT2); $ip = "192.168.1.1"; route(TT2); $ip = "192.168.1.255"; route(TT2); $ip = "192.168.2.1"; route(TT2); $ip = "192.168.3.1"; route(TT2); $ip = "192.168.4.97"; route(TT2); $ip = "192.168.4.100"; route(TT2); $ip = "[0:2:4:A:B:D:E:F301]"; route(TT2); $ip = "[0:2:4:A:B:D:E:F401]"; route(TT2); $ip = "[0:0:0:0:0:0:0:0]"; route(TT2); } route[TEST] { $net = "0.0.0.0 128.2.3.4/1 127.0.128.16 [0:2:4:A:B:D:E:F301]"; route(TT1); $net = "255.255.255.255/0"; # all IPv4 addresses, dentical to 0.0.0.0/0 route(TT1); $net = "127.0.0.1/255.255.255.0"; route(TT1); $net = "10.0.0.0/8"; # All type A addresses route(TT1); $net = "192.168.1.0/24"; route(TT1); $net = "192.168.4.96/27"; route(TT1); $net = "192.168.1.1/32"; # only one IP matches route(TT1); $net = "192.168.1.0/24,192.168.2.0/24"; route(TT1); $net = "192.168.1.0/24,192.168.2.0/24,127.0.0.1/31"; route(TT1); $net = "[0:0:0:0:0:0:0:0]/0"; # all IPv6 addresses route(TT1); $net = "[0:2:4:A:B:D:E:f300]/120"; route(TT1); $net = "my_ipset1"; route(TT1); } # the result is: Testing netmask '0.0.0.0 128.2.3.4/1 127.0.128.16 [0:2:4:A:B:D:E:F301]' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - TRUE '192.168.1.1' - TRUE '192.168.1.255' - TRUE '192.168.2.1' - TRUE '192.168.3.1' - TRUE '192.168.4.97' - TRUE '192.168.4.100' - TRUE '[0:2:4:A:B:D:E:F301]' - TRUE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '255.255.255.255/0' 's' - TRUE '127.0.0.1' - TRUE '127.0.0.2' - TRUE '10.0.0.1' - TRUE '11.0.0.1' - TRUE '172.1.8.1' - TRUE '192.168.1.1' - TRUE '192.168.1.255' - TRUE '192.168.2.1' - TRUE '192.168.3.1' - TRUE '192.168.4.97' - TRUE '192.168.4.100' - TRUE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '127.0.0.1/24' 's' - FALSE '127.0.0.1' - TRUE '127.0.0.2' - TRUE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - FALSE '192.168.1.255' - FALSE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '10.0.0.0/8' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - TRUE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - FALSE '192.168.1.255' - FALSE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '192.168.1.0/24' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - TRUE '192.168.1.255' - TRUE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '192.168.4.96/27' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - FALSE '192.168.1.255' - FALSE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - TRUE '192.168.4.100' - TRUE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '192.168.1.1/32' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - TRUE '192.168.1.255' - FALSE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '192.168.1.0/24,192.168.2.0/24' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - TRUE '192.168.1.255' - TRUE '192.168.2.1' - TRUE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '192.168.1.0/24,192.168.2.0/24,127.0.0.1/31' 's' - FALSE '127.0.0.1' - TRUE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - TRUE '192.168.1.255' - TRUE '192.168.2.1' - TRUE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '[0:0:0:0:0:0:0:0]/0' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - FALSE '192.168.1.255' - FALSE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - TRUE '[0:2:4:A:B:D:E:F401]' - TRUE '[0:0:0:0:0:0:0:0]' - TRUE Testing netmask '[0:2:4:A:B:D:E:f300]/120' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - FALSE '192.168.1.255' - FALSE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - TRUE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE }
kamailio-4.0.4/obsolete/permissions/doc/Makefile0000644000000000000000000000013412223032460020367 0ustar rootrootdocs = permissions.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/permissions/doc/params.xml0000644000000000000000000001206212223032460020737 0ustar rootroot
Parameters
<varname>default_allow_file</varname> (string) Default allow file used by functions without parameters. If you don't specify full pathname then the directory in which is the main config file is located will be used. Default value is permissions.allow. Set <varname>default_allow_file</varname> parameter ... modparam("permissions", "default_allow_file", "/etc/permissions.allow") ...
<varname>default_deny_file</varname> (string) Default file containing deny rules. The file is used by functions without parameters. If you don't specify full pathname then the directory in which the main config file is located will be used. Default value is permissions.deny. Set <varname>default_deny_file</varname> parameter ... modparam("permissions", "default_deny_file", "/etc/permissions.deny") ... If both of the default file parameters are set to "" the module does not try to load them.
<varname>check_all_branches</varname> (integer) If set then allow_routing functions will check Request-URI of all branches (default). If disabled then only Request-URI of the first branch will be checked. Do not disable this parameter unless you really know what you are doing. Default value is 1. Set <varname>check_all_branches</varname> parameter ... modparam("permissions", "check_all_branches", 0) ...
<varname>allow_suffix</varname> (string) Suffix to be appended to basename to create filename of the allow file when version with one parameter of either allow_routing or allow_register is used. Including leading dot. Default value is ".allow". Set <varname>allow_suffix</varname> parameter ... modparam("permissions", "allow_suffix", ".allow") ...
<varname>deny_suffix</varname> (string) Suffix to be appended to basename to create filename of the deny file when version with one parameter of either allow_routing or allow_register is used. Including leading dot. Default value is ".deny". Set <varname>deny_suffix</varname> parameter ... modparam("permissions", "deny_suffix", ".deny") ...
<varname>max_rule_files</varname> (int) Maximum number of allow/deny file pairs. Default value is 64.
<varname>safe_file_load</varname> (int) Module initialization fails in case of a missing config file if safe_file_load is true. Default value is 0 (false).
<varname>db_url</varname> (string) URL of the database to be used.
<varname>db_mode</varname> (int) Disables/enables database cache. Default value is 0 (cache is disabled) NOTE: ipmatch functions can operate only in cache mode, set db_mode to 1 if you want to use them.
<varname>ipmatch_table</varname> (string) Name of the table containing ipmatch entries. Default value is "ipmatch".
<varname>declare_ipset</varname> (string) Declares name of ip set which can be manipulated via RPC commands and tested using ip_is_trusted. Identifier must start with letter or underscore. IP mask may follow equal sign to initialize ipset on startup.
kamailio-4.0.4/obsolete/permissions/trusted.c0000644000000000000000000002115512223032460020026 0ustar rootroot/* * $Id$ * * allow_trusted related functions * * Copyright (C) 2003 Juha Heinanen * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2004-06-07 updated to the new DB api, moved reload_trusted_table (andrei) * 2006-08-14: DB handlers are moved to permission.c (Miklos) */ #include #include #include #include "trusted.h" #include "permissions.h" #include "trusted_hash.h" #include "../../config.h" #include "../../lib/srdb2/db.h" #include "../../ip_addr.h" #include "../../mem/shm_mem.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" struct trusted_list ***hash_table = NULL; /* Pointer to current hash table pointer */ struct trusted_list **hash_table_1 = NULL; /* Pointer to hash table 1 */ struct trusted_list **hash_table_2 = NULL;; /* Pointer to hash table 2 */ /* DB commands to query and load the table */ static db_cmd_t *cmd_load_trusted = NULL; static db_cmd_t *cmd_query_trusted = NULL; /* * Initialize data structures */ int init_trusted(void) { if (db_mode == ENABLE_CACHE) { hash_table_1 = new_hash_table(); if (!hash_table_1) return -1; hash_table_2 = new_hash_table(); if (!hash_table_2) goto error; hash_table = (struct trusted_list ***)shm_malloc(sizeof(struct trusted_list **)); if (!hash_table) goto error; *hash_table = hash_table_1; if (reload_trusted_table() == -1) { LOG(L_CRIT, "init_trusted(): Reload of trusted table failed\n"); goto error; } } return 0; error: clean_trusted(); return -1; } /* * Close connections and release memory */ void clean_trusted(void) { if (hash_table_1) { free_hash_table(hash_table_1); hash_table_1 = NULL; } if (hash_table_2) { free_hash_table(hash_table_2); hash_table_2 = NULL; } if (hash_table) { shm_free(hash_table); hash_table = NULL; } } /* prepare the DB cmds */ int init_trusted_db(void) { db_fld_t load_res_cols[] = { {.name = source_col, .type = DB_CSTR}, {.name = proto_col, .type = DB_CSTR}, {.name = from_col, .type = DB_CSTR}, {.name = NULL} }; db_fld_t query_match[] = { {.name = source_col, .type = DB_CSTR}, {.name = NULL} }; db_fld_t query_res_cols[] = { {.name = proto_col, .type = DB_CSTR}, {.name = from_col, .type = DB_CSTR}, {.name = NULL} }; if (!db_conn) return -1; if (db_mode == ENABLE_CACHE) { cmd_load_trusted = db_cmd(DB_GET, db_conn, trusted_table, load_res_cols, NULL, NULL); if (!cmd_load_trusted) goto error; } else { cmd_query_trusted = db_cmd(DB_GET, db_conn, trusted_table, query_res_cols, query_match, NULL); if (!cmd_query_trusted) goto error; } return 0; error: LOG(L_ERR, "init_trusted_db(): failed to prepare DB commands\n"); return -1; } /* destroy the DB cmds */ void destroy_trusted_db(void) { if (cmd_load_trusted) { db_cmd_free(cmd_load_trusted); cmd_load_trusted = NULL; } if (cmd_query_trusted) { db_cmd_free(cmd_query_trusted); cmd_query_trusted = NULL; } } /* * Matches protocol string against the protocol of the request. Returns 1 on * success and 0 on failure. */ static inline int match_proto(char *proto_string, int proto_int) { if (strcasecmp(proto_string, "any") == 0) return 1; if (proto_int == PROTO_UDP) { if (strcasecmp(proto_string, "udp") == 0) { return 1; } else { return 0; } } if (proto_int == PROTO_TCP) { if (strcasecmp(proto_string, "tcp") == 0) { return 1; } else { return 0; } } if (proto_int == PROTO_TLS) { if (strcasecmp(proto_string, "tls") == 0) { return 1; } else { return 0; } } if (proto_int == PROTO_SCTP) { if (strcasecmp(proto_string, "sctp") == 0) { return 1; } else { return 0; } } LOG(L_ERR, "match_proto(): Unknown request protocol\n"); return 0; } #define VAL_NULL_STR(fld) ( \ ((fld).flags & DB_NULL) \ || (((fld).type == DB_CSTR) && ((fld).v.cstr[0] == '\0')) \ || (((fld).type == DB_STR) && \ (((fld).v.lstr.len == 0) || ((fld).v.lstr.s[0] == '\0'))) \ ) /* * Matches from uri against patterns returned from database. Returns 1 when * first pattern matches and -1 if none of the patterns match. */ static int match_res(struct sip_msg* msg, db_res_t* _r) { str uri; char uri_string[MAX_URI_SIZE+1]; db_rec_t *rec; regex_t preg; if (!_r) return -1; if (parse_from_header(msg) < 0) return -1; uri = get_from(msg)->uri; if (uri.len > MAX_URI_SIZE) { LOG(L_ERR, "match_res(): From URI too large\n"); return -1; } memcpy(uri_string, uri.s, uri.len); uri_string[uri.len] = (char)0; rec = db_first(_r); while (rec) { if (VAL_NULL_STR(rec->fld[0]) || VAL_NULL_STR(rec->fld[1])) goto next; /* check the protocol */ if (match_proto(rec->fld[0].v.cstr, msg->rcv.proto) <= 0) goto next; /* check the from uri */ if (regcomp(&preg, rec->fld[1].v.cstr, REG_NOSUB)) { LOG(L_ERR, "match_res(): Error in regular expression: %s\n", rec->fld[0].v.cstr); goto next; } if (regexec(&preg, uri_string, 0, (regmatch_t *)0, 0)) { regfree(&preg); goto next; } regfree(&preg); /* everything matched */ return 1; next: rec = db_next(_r); } return -1; } /* * Checks based on request's source address, protocol, and from field * if request can be trusted without authentication. Possible protocol * values are "any" (that matches any protocol), "tcp", "udp", "tls", * and "sctp". */ int allow_trusted(struct sip_msg* _msg, char* str1, char* str2) { int result; db_res_t *res = NULL; if (!db_url) { LOG(L_ERR, "allow_trusted(): ERROR set db_mode parameter of permissions module first !\n"); return -1; } if (db_mode == DISABLE_CACHE) { if (!cmd_query_trusted) return -1; if (!(cmd_query_trusted->match[0].v.cstr = ip_addr2a(&(_msg->rcv.src_ip))) ) { LOG(L_ERR, "allow_trusted(): Error in ip address\n"); return -1; } if (db_exec(&res, cmd_query_trusted) < 0) { LOG(L_ERR, "allow_trusted(): Error while querying database\n"); return -1; } result = match_res(_msg, res); if (res) db_res_free(res); return result; } else if (db_mode == ENABLE_CACHE) { return match_hash_table(*hash_table, _msg); } else { LOG(L_ERR, "allow_trusted(): Error - set db_mode parameter of permissions module properly\n"); return -1; } } /* * Reload trusted table to new hash table and when done, make new hash table * current one. */ int reload_trusted_table(void) { db_res_t *res = NULL; db_rec_t *rec; struct trusted_list **new_hash_table; int row; char *source, *proto, *from; if (!cmd_load_trusted) return -1; if (db_exec(&res, cmd_load_trusted) < 0) { LOG(L_ERR, "ERROR: permissions: reload_trusted_table():" " Error while querying database\n"); return -1; } /* Choose new hash table and free its old contents */ if (*hash_table == hash_table_1) { empty_hash_table(hash_table_2); new_hash_table = hash_table_2; } else { empty_hash_table(hash_table_1); new_hash_table = hash_table_1; } row = 0; rec = db_first(res); while (rec) { if (VAL_NULL_STR(rec->fld[0]) || VAL_NULL_STR(rec->fld[1]) || VAL_NULL_STR(rec->fld[2])) { LOG(L_ERR, "ERROR: permissions: trusted_reload():" " Database problem, NULL filed is not allowed\n"); goto error; } source = rec->fld[0].v.cstr; proto = rec->fld[1].v.cstr; from = rec->fld[2].v.cstr; if (hash_table_insert(new_hash_table, source, proto, from) == -1) { LOG(L_ERR, "ERROR: permissions: " "trusted_reload(): Hash table problem\n"); goto error; } DBG("Tuple <%s, %s, %s> inserted into trusted hash table\n", source, proto, from); row++; rec = db_next(res); } DBG("Number of rows in trusted table: %d\n", row); *hash_table = new_hash_table; DBG("Trusted table reloaded successfully.\n"); if (res) db_res_free(res); return 1; error: if (res) db_res_free(res); return -1; } kamailio-4.0.4/obsolete/permissions/im_db.h0000644000000000000000000000245212223032460017412 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _IM_DB_H #define _IM_DB_H #include "../../lib/srdb2/db.h" /* prepare the DB cmds */ int init_im_db(void); /* destroy the DB cmds */ void destroy_im_db(void); /* reload DB cache * return value * 0: success * -1: error */ int reload_im_cache(void); #endif /* _IM_DB_H */ kamailio-4.0.4/obsolete/permissions/im_locks.h0000644000000000000000000000323512223032460020140 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _IM_LOCKS_H #define _IM_LOCKS_H #include "../../locking.h" #include "im_hash.h" /* reader lock for ipmatch cache */ #define reader_init_imhash_lock() lock_init(&(IM_HASH->read_lock)) void reader_lock_imhash(void); void reader_release_imhash(void); /* writer lock for ipmatch cache */ #define writer_init_imhash_lock() lock_init(&(IM_HASH)->write_lock); #define writer_lock_imhash() lock_get(&(IM_HASH)->write_lock); #define writer_release_imhash() lock_release(&(IM_HASH)->write_lock); /* set writer demand on ipmatch cache */ void set_wd_imhash(void); /* delete writer demand on ipmatch cache */ void del_wd_imhash(void); #endif /* _IM_LOCKS_H */ kamailio-4.0.4/obsolete/permissions/permissions_rpc.h0000644000000000000000000000336412223032460021562 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _PERMISSIONS_RPC_H #define _PERMISSIONS_RPC_H #include "../../rpc.h" #include "trusted_rpc.h" #include "im_rpc.h" #include "ip_set_rpc.h" rpc_export_t permissions_rpc[] = { {"trusted.reload", trusted_reload, trusted_reload_doc, 0}, {"trusted.dump", trusted_dump, trusted_dump_doc, RET_ARRAY}, {"ipmatch.reload", im_reload, im_reload_doc, 0}, {"ipset.clean", rpc_ip_set_clean, rpc_ip_set_clean_doc, 0}, {"ipset.add", rpc_ip_set_add, rpc_ip_set_add_doc, 0}, {"ipset.commit", rpc_ip_set_commit, rpc_ip_set_commit_doc, 0}, {"ipset.list", rpc_ip_set_list, rpc_ip_set_list_doc, RET_ARRAY}, {"ipset.print", rpc_ip_set_print, rpc_ip_set_print_doc, RET_ARRAY}, {0, 0, 0, 0} }; #endif /* _PERMISSIONS_RPC_H */ kamailio-4.0.4/obsolete/permissions/im_db.c0000644000000000000000000001136312223032460017406 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "permissions.h" #include "im_hash.h" #include "im_locks.h" #include "im_db.h" /* DB commands to load the table */ static db_cmd_t *cmd_load_im = NULL; /* prepare the DB cmds */ int init_im_db(void) { db_fld_t load_res_cols[] = { {.name = "ip", .type = DB_CSTR}, {.name = "avp_val", .type = DB_CSTR}, {.name = "mark", .type = DB_BITMAP}, {.name = "flags", .type = DB_BITMAP}, {.name = NULL} }; if (db_mode != ENABLE_CACHE) return 0; /* nothing to do */ if (!db_conn) return -1; cmd_load_im = db_cmd(DB_GET, db_conn, ipmatch_table, load_res_cols, NULL, NULL); if (!cmd_load_im) { LOG(L_ERR, "init_im_db(): failed to prepare DB commands\n"); return -1; } return 0; } /* destroy the DB cmds */ void destroy_im_db(void) { if (cmd_load_im) { db_cmd_free(cmd_load_im); cmd_load_im = NULL; } } #define VAL_NULL_STR(fld) ( \ ((fld).flags & DB_NULL) \ || (((fld).type == DB_CSTR) && ((fld).v.cstr[0] == '\0')) \ || (((fld).type == DB_STR) && \ (((fld).v.lstr.len == 0) || ((fld).v.lstr.s[0] == '\0'))) \ ) /* * reads the table from SQL database, and inserts the IP addresses into * the hash table * returns -1 in case of error, -2 if the sql table is empty */ static int load_db(im_entry_t **hash) { db_res_t *res = NULL; db_rec_t *rec; int found; char *ip, *avp_val; unsigned int flags, mark; if (!hash || !cmd_load_im) return -1; if (db_exec(&res, cmd_load_im) < 0) { LOG(L_ERR, "ERROR: load_db(): Error while querying database\n"); return -1; } found = 0; rec = db_first(res); while (rec) { /* get every value of the row */ /* start with flags */ if (rec->fld[3].flags & DB_NULL) goto skip; flags = rec->fld[3].v.bitmap; if ((flags & SRDB_DISABLED) || ((flags & SRDB_LOAD_SER) == 0)) goto skip; found++; /* get IP address */ if (VAL_NULL_STR(rec->fld[0])) { LOG(L_ERR, "ERROR: load_db(): ip address can not be NULL!\n"); goto error; } ip = rec->fld[0].v.cstr; /* output AVP value */ if (!VAL_NULL_STR(rec->fld[1])) avp_val = rec->fld[1].v.cstr; else avp_val = NULL; if (rec->fld[2].flags & DB_NULL) mark = (unsigned int)-1; /* will match eveything */ else mark = rec->fld[2].v.bitmap; /* get mark */ /* create a new entry and insert it into the hash table */ if (insert_im_hash(ip, avp_val, mark, hash)) { LOG(L_ERR, "ERROR: load_db(): could not insert entry into the hash table\n"); goto error; } skip: rec = db_next(res); } if (res) db_res_free(res); if (found) { LOG(L_DBG, "DEBUG: load_db(): number of rows in ipmatch table: %d\n", found); return 0; } else { LOG(L_WARN, "WARNING: load_db(): there is no active row in ipmatch table!\n"); return -2; } error: if (res) db_res_free(res); return -1; } /* reload DB cache * return value * 0: success * -1: error */ int reload_im_cache(void) { im_entry_t **hash, **old_hash; int ret; if (!IM_HASH) { LOG(L_CRIT, "ERROR: reload_im_cache(): ipmatch hash table is not initialied. " "Have you set the database url?\n"); return -1; } /* make sure that there is no other writer process */ writer_lock_imhash(); if (!(hash = new_im_hash())) { writer_release_imhash(); return -1; } ret = load_db(hash); if (ret == -1) { /* error occured */ LOG(L_ERR, "ERROR: reload_im_cache(): could not reload cache\n"); free_im_hash(hash); /* there can be data in the hash already */ writer_release_imhash(); return -1; } else if (ret == -2) { /* SQL table was empty -- drop hash table */ delete_im_hash(hash); hash = NULL; } old_hash = IM_HASH->entries; /* ask reader processes to stop reading */ set_wd_imhash(); IM_HASH->entries = hash; del_wd_imhash(); if (old_hash) free_im_hash(old_hash); writer_release_imhash(); return 0; } kamailio-4.0.4/obsolete/permissions/config/0000755000000000000000000000000012223032460017431 5ustar rootrootkamailio-4.0.4/obsolete/permissions/config/permissions.allow0000644000000000000000000000077512223032460023055 0ustar rootroot# SIP Express Router permissions module config file # # Syntax: # from_list [EXCEPT from_list] : req_uri_list [EXCEPT req_uri_list] # # from_list and req_uri_list are comma separated expressions # Expressions are treated as case insensitive POSIX Extended Regular Expressions. # Keyword ALL matches any expression. # # Examples: # ALL : "^sip:361[0-9]*@abc\.com$" EXCEPT "^sip:361[0-9]*3@abc\.com$", "^sip:361[0-9]*4@abc\.com$" # # "^sip:3677[0-9]*@abc\.com$" : "^sip:361[0-9]*@abc\.com$" # # ALL : ALL kamailio-4.0.4/obsolete/permissions/config/register.allow0000644000000000000000000000101512223032460022312 0ustar rootroot# # $Id$ # # Files register.allow and register.deny could be used # by allow_register function to prevent registration of # certain IP addresses. This (allow) file is empty because # we only want to restrict IPs that can be registered and # therefore we use register.deny file only. # # If you have a privileged user that should be able to # register anything than you might want to uncomment the # following line. WARNING: Do not uncomment unless you # really know what are you doing ! # # "^sip:username@foo\.bar$" : ALL # kamailio-4.0.4/obsolete/permissions/config/register.deny0000644000000000000000000000101012223032460022126 0ustar rootroot# # $Id$ # # Suppose that we have a PSTN gateway with IP address 1.2.3.4 # We should prevent REGISTER messages that contain that IP # address in Contact header field because that can cause serious # security hole (a malicious user might be able to register such # a contact and bypass security checks performed by the SIP proxy). # # The following line prevents registering Contacts with IP 1.2.3.4 # (Don't forget to list also all hostnames that can be used to # reach the PSTN gateway) # ALL : "^sip:.*1\.2\.3\.4$" kamailio-4.0.4/obsolete/permissions/config/permissions.deny0000644000000000000000000000077412223032460022675 0ustar rootroot# SIP Express Router permissions module config file # # Syntax: # from_list [EXCEPT from_list] : req_uri_list [EXCEPT req_uri_list] # # from_list and req_uri_list are comma separated expressions # Expressions are treated as case insensitive POSIX Extended Regular Expressions. # Keyword ALL matches any expression. # # Examples: # ALL : "^sip:361[0-9]*@abc\.com$" EXCEPT "^sip:361[0-9]*3@abc\.com$", "^sip:361[0-9]*4@abc\.com$" # # "^sip:3677[0-9]*@abc\.com$" : "^sip:361[0-9]*@abc\.com$" # # All : ALL kamailio-4.0.4/obsolete/permissions/README0000644000000000000000000006047512223032460017060 0ustar rootroot1. Permissions Module Miklos Tirpak Juha Heinanen Copyright © 2003, 2006 Miklos Tirpak, Juha Heinanen, iptelorg GmbH __________________________________________________________________ 1.1. Overview 1.1.1. Call Routing 1.1.2. Registration Permissions 1.1.3. Refer-To Permissions 1.1.4. Messages catching 1.2. Parameters 1.2.1. default_allow_file (string) 1.2.2. default_deny_file (string) 1.2.3. check_all_branches (integer) 1.2.4. allow_suffix (string) 1.2.5. deny_suffix (string) 1.2.6. max_rule_files (int) 1.2.7. safe_file_load (int) 1.2.8. db_url (string) 1.2.9. db_mode (int) 1.2.10. ipmatch_table (string) 1.2.11. declare_ipset (string) 1.3. Functions 1.3.1. allow_routing() 1.3.2. allow_routing(basename) 1.3.3. allow_routing(allow_file, deny_file) 1.3.4. allow_register(basename) 1.3.5. allow_register(allow_file, deny_file) 1.3.6. allow_refer_to(basename) 1.3.7. allow_refer_to(allow_file, deny_file) 1.3.8. ipmatch (string/AVP/select, [avp]) 1.3.9. ipmatch_onsend (string) 1.3.10. ipmatch_filter (unsigned int) 1.3.11. ip_is_trusted(ip_set, ip) 1.4. XMLRPC Interface 1.1. Overview 1.1.1. Call Routing The module can be used to determine if a call has appropriate permission to be established. Permission rules are stored in plaintext configuration files similar to hosts.allow and hosts.deny files used by tcpd. When allow_routing function is called it tries to find a rule that matches selected fields of the message. SER is a forking proxy and therefore a single message can be sent to different destinations simultaneously. When checking permissions all the destinations must be checked and if one of them fails, the forwarding will fail. The matching algorithm is as follows, first match wins: * Create a set of pairs of form (From, R-URI of branch 1), (From, R-URI of branch 2), etc. * Routing will be allowed when all pairs match an entry in the allow file. * Otherwise routing will be denied when one of pairs matches an entry in the deny file. * Otherwise, routing will be allowed. A non-existing permission control file is treated as if it were an empty file. Thus, permission control can be turned off by providing no permission control files. From header field and Request-URIs are always compared with regular expressions! For the syntax see the sample file: config/permissions.allow. 1.1.2. Registration Permissions In addition to call routing it is also possible to check REGISTER messages and decide--based on the configuration files--whether the message should be allowed and the registration accepted or not. Main purpose of the function is to prevent registration of "prohibited" IP addresses. One example, when a malicious user registers a contact containing IP address of a PSTN gateway, he might be able to bypass authorization checks performed by the SIP proxy. That is undesirable and therefore attempts to register IP address of a PSTN gateway should be rejected. Files config/register.allow and config/register.deny contain an example configuration. Function for registration checking is called allow_register and the algorithm is very similar to the algorithm described in Section 1.1.1, “Call Routingâ€. The only difference is in the way how pairs are created. Instead of From header field the function uses To header field because To header field in REGISTER messages contains the URI of the person being registered. Instead of the Request-URI of branches the function uses Contact header field. Thus, pairs used in matching will look like this: (To, Contact 1), (To, Contact 2), (To, Contact 3), and so on.. The algorithm of matching is same as described in Section 1.1.1, “Call Routingâ€. 1.1.3. Refer-To Permissions In addition to call routing and REGISTER it is also possible to check REFER messages and decide--based on the configuration files-- whether or not the message should be accepted for forwarding. Main purpose of the function is to prevent referring a SIP UA to "prohibited" IP addresses. One example is user sending a REFER request to PSTN gateway trying to refer it to an expensive phone number. Function for Refer-To checking is called allow_refer_to and the algorithm is very similar to the algorithm described in Section 1.1.1. The only difference is in the way how pairs are created. Instead of Request-URI the function uses Refer-To header field because Refer-To header field in REFER messages contains the URI to which the UA is being referred to. The algorithm of matching is same as described in Section 1.1.1. 1.1.4. Messages catching The module can be also used for catching messages coming from or going to specific network elements, for example gateways or peering partners. Users can register or forward the calls to the address of a gateway resulting unauthorized access to them. Such calls must be catched and dropped, see ipmatch functions for details. 1.2. Parameters 1.2.1. default_allow_file (string) Default allow file used by functions without parameters. If you don't specify full pathname then the directory in which is the main config file is located will be used. Default value is “permissions.allowâ€. Example 1. Set default_allow_file parameter ... modparam("permissions", "default_allow_file", "/etc/permissions.allow") ... 1.2.2. default_deny_file (string) Default file containing deny rules. The file is used by functions without parameters. If you don't specify full pathname then the directory in which the main config file is located will be used. Default value is “permissions.denyâ€. Example 2. Set default_deny_file parameter ... modparam("permissions", "default_deny_file", "/etc/permissions.deny") ... If both of the default file parameters are set to "" the module does not try to load them. 1.2.3. check_all_branches (integer) If set then allow_routing functions will check Request-URI of all branches (default). If disabled then only Request-URI of the first branch will be checked. Warning Do not disable this parameter unless you really know what you are doing. Default value is 1. Example 3. Set check_all_branches parameter ... modparam("permissions", "check_all_branches", 0) ... 1.2.4. allow_suffix (string) Suffix to be appended to basename to create filename of the allow file when version with one parameter of either allow_routing or allow_register is used. Note Including leading dot. Default value is ".allow". Example 4. Set allow_suffix parameter ... modparam("permissions", "allow_suffix", ".allow") ... 1.2.5. deny_suffix (string) Suffix to be appended to basename to create filename of the deny file when version with one parameter of either allow_routing or allow_register is used. Note Including leading dot. Default value is ".deny". Example 5. Set deny_suffix parameter ... modparam("permissions", "deny_suffix", ".deny") ... 1.2.6. max_rule_files (int) Maximum number of allow/deny file pairs. Default value is 64. 1.2.7. safe_file_load (int) Module initialization fails in case of a missing config file if safe_file_load is true. Default value is 0 (false). 1.2.8. db_url (string) URL of the database to be used. 1.2.9. db_mode (int) Disables/enables database cache. Default value is 0 (cache is disabled) NOTE: ipmatch functions can operate only in cache mode, set db_mode to 1 if you want to use them. 1.2.10. ipmatch_table (string) Name of the table containing ipmatch entries. Default value is "ipmatch". 1.2.11. declare_ipset (string) Declares name of ip set which can be manipulated via RPC commands and tested using ip_is_trusted. Identifier must start with letter or underscore. IP mask may follow equal sign to initialize ipset on startup. 1.3. Functions 1.3.1. allow_routing() Returns true if all pairs constructed as described in Section 1.1.1, “Call Routing†have appropriate permissions according to the configuration files. This function uses default configuration files specified in default_allow_file and default_deny_file. Example 6. allow_routing usage ... if (allow_routing()) { t_relay(); }; ... 1.3.2. allow_routing(basename) Returns true if all pairs constructed as described in Section 1.1.1, “Call Routing†have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: * basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. Example 7. allow_routing(basename) usage ... if (allow_routing("basename")) { t_relay(); }; ... 1.3.3. allow_routing(allow_file, deny_file) Returns true if all pairs constructed as described in Section 1.1.1, “Call Routing†have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: * allow_file - File containing allow rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. * deny_file - File containing deny rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. Example 8. allow_routing(allow_file, deny_file) usage ... if (allow_routing("rules.allow", "rules.deny")) { t_relay(); }; ... 1.3.4. allow_register(basename) The function returns true if all pairs constructed as described in Section 1.1.2, “Registration Permissions†have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: * basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. Example 9. allow_register(basename) usage ... if (method=="REGISTER") { if (allow_register("register")) { save("location"); break; } else { sl_send_reply("403", "Forbidden"); }; }; ... 1.3.5. allow_register(allow_file, deny_file) The function returns true if all pairs constructed as described in Section 1.1.2, “Registration Permissions†have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: * allow_file - File containing allow rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. * deny_file - File containing deny rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. Example 10. allow_register(allow_file, deny_file) usage ... if (method=="REGISTER") { if (allow_register("register.allow", "register.deny")) { save("location"); break; } else { sl_send_reply("403", "Forbidden"); }; }; ... 1.3.6. allow_refer_to(basename) The function returns true if all pairs constructed as described in Section 1.1.2, “Registration Permissions†have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: * basename - Basename from which allow and deny filenames will be created by appending contents of allow_suffix and deny_suffix parameters. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. Example 11. allow_register(basename) usage ... if (method=="REGISTER") { if (allow_register("register")) { save("location"); break; } else { sl_send_reply("403", "Forbidden"); }; }; ... 1.3.7. allow_refer_to(allow_file, deny_file) The function returns true if all pairs constructed as described in Section 1.1.2 have appropriate permissions according to the configuration files given as parameters. Meaning of the parameters is as follows: * allow_file - File containing allow rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. * deny_file - File containing deny rules. If the parameter doesn't contain full pathname then the function expects the file to be located in the same directory as the main configuration file of the server. Example 12. allow_register(allow_file, deny_file) usage ... if (method=="REFER") { if (allow_register("refer.allow", "refer.deny")) { ... } else { sl_send_reply("403", "Forbidden"); }; }; ... 1.3.8. ipmatch (string/AVP/select, [avp]) The function tries to find an IP address and port pair (defined by the first function parameter) in the cached database table. Port is optional, it is compared only if both the function parameter and the database entry contain it. Meaning of the parameters is as follows: * string/AVP/select - File containing allow rules. + string: "src": the source address of the packet is used "via2": the ip address of the 2nd via line is used other values are not defined currently + AVP: e.g. "$myavp" + select call: e.g. "@via[0].host" * The second parameter is optional, it is used to set an AVP value from the database. Suitable for assigning logical identifiers to gateways. Note that IPv6 addresses must be enclosed in square brackets in case of port is defined: [1111:2222::3333]:5060 1.3.9. ipmatch_onsend (string) ipmatch() function replacement for onsend_route block. The function accepts only string parameter, because even AVP reading is unsafe in onsend_route. Meaning of the parameter: * "dst": the destination address is used * "ruri": the ip:port pair is extracted from the Request URI The function can be used for example to catch unauthorized requests going to gateways: Use a flag to mark the call as PSTN either in request or in failure_route, and do not touch it in onreply_route. See the examples below. 1.3.10. ipmatch_filter (unsigned int) Entries in the database can be marked to group the different kind of network elements. The function sets the filter which is used on the mark while comparing the IP addresses. The mark must be the power of 2 in the database! Note that ipmatch() and ipmatch_onsend() functions reset the filter! Example 13. ipmatch_filter (unsigned int) usage +-----------------+-------------------+------+------+ | ip | avp_val | mark | flag | +-----------------+-------------------+------+------+ | 1111:2222::1001 | first_gw | 1 | 1 | | 10.38.2.10:5060 | second_gw | 1 | 1 | | 10.0.0.10 | first_peering | 2 | 1 | +-----------------+-------------------+------+------+ route[0] { # is this a request from a GW? ipmatch_filter("1"); if (ipmatch("src", "$gw_id")) { # yes, it is from a GW ... } else { # is this a request from a peering partner? ipmatch_filter("2"); if (ipmatch("src", "$peering_id")) { # yes, it is from a peering partner ... }; } ... # request goes to PSTN setflag(PSTN); } onsend_route[0] { if (method == "INVITE" && (!isflagset(PSTN))) { # is this a request to a GW? ipmatch_filter("1"); if (ipmatch_onsend("dst")) { # request is not marked with PSTN flag, but it goes to a gateway drop; } } ... } 1.3.11. ip_is_trusted(ip_set, ip) The function returns true if ip is contained in ip_set. Both IPv4 and IPv6 are supported. Meaning of the parameters is as follows: * ip_set is identified by comma/space/semicolon delimited list of IP addresses or subnet masks. The subnet mask is written in IP_slash_number form or as IP. If the mask is not mentioned then default value is taken, the default value is number of bits of particular IP address, i.e. 32 in case of IPv4, 128 in case of IPv6. Besides list also ip set identifier declared by declare_ipset may be provided. In this case ip is checked against set maintainded by RPC commands. * ip to test. Besides direct address in string form there are extra identifiers to force IP related to current message: Source, Destination, Received. Note that only the first character is essential. Example 14. ip_is_trusted usage modparam("permissions", "declare_ipset", "my_ipset1"); modparam("permissions", "declare_ipset", "my_ipset2=127.0.0.0/24;10.0.0.0/255.25 5.255.0"); route[TT2] { if (ip_is_trusted("$net", "$ip")) { xplog("L_E", "'%$ip' - TRUE\n"); } else { xplog("L_E", "'%$ip' - FALSE\n"); } } route[TT1] { xplog("L_E", "Testing netmask '%$net'\n"); $ip = "s"; # source address route(TT2); $ip = "127.0.0.1"; route(TT2); $ip = "127.0.0.2"; route(TT2); $ip = "10.0.0.1"; route(TT2); $ip = "11.0.0.1"; route(TT2); $ip = "172.1.8.1"; route(TT2); $ip = "192.168.1.1"; route(TT2); $ip = "192.168.1.255"; route(TT2); $ip = "192.168.2.1"; route(TT2); $ip = "192.168.3.1"; route(TT2); $ip = "192.168.4.97"; route(TT2); $ip = "192.168.4.100"; route(TT2); $ip = "[0:2:4:A:B:D:E:F301]"; route(TT2); $ip = "[0:2:4:A:B:D:E:F401]"; route(TT2); $ip = "[0:0:0:0:0:0:0:0]"; route(TT2); } route[TEST] { $net = "0.0.0.0 128.2.3.4/1 127.0.128.16 [0:2:4:A:B:D:E:F301]"; route(TT1); $net = "255.255.255.255/0"; # all IPv4 addresses, dentical to 0.0.0.0/0 route(TT1); $net = "127.0.0.1/255.255.255.0"; route(TT1); $net = "10.0.0.0/8"; # All type A addresses route(TT1); $net = "192.168.1.0/24"; route(TT1); $net = "192.168.4.96/27"; route(TT1); $net = "192.168.1.1/32"; # only one IP matches route(TT1); $net = "192.168.1.0/24,192.168.2.0/24"; route(TT1); $net = "192.168.1.0/24,192.168.2.0/24,127.0.0.1/31"; route(TT1); $net = "[0:0:0:0:0:0:0:0]/0"; # all IPv6 addresses route(TT1); $net = "[0:2:4:A:B:D:E:f300]/120"; route(TT1); $net = "my_ipset1"; route(TT1); } # the result is: Testing netmask '0.0.0.0 128.2.3.4/1 127.0.128.16 [0:2:4:A:B:D:E:F301]' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - TRUE '192.168.1.1' - TRUE '192.168.1.255' - TRUE '192.168.2.1' - TRUE '192.168.3.1' - TRUE '192.168.4.97' - TRUE '192.168.4.100' - TRUE '[0:2:4:A:B:D:E:F301]' - TRUE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '255.255.255.255/0' 's' - TRUE '127.0.0.1' - TRUE '127.0.0.2' - TRUE '10.0.0.1' - TRUE '11.0.0.1' - TRUE '172.1.8.1' - TRUE '192.168.1.1' - TRUE '192.168.1.255' - TRUE '192.168.2.1' - TRUE '192.168.3.1' - TRUE '192.168.4.97' - TRUE '192.168.4.100' - TRUE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '127.0.0.1/24' 's' - FALSE '127.0.0.1' - TRUE '127.0.0.2' - TRUE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - FALSE '192.168.1.255' - FALSE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '10.0.0.0/8' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - TRUE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - FALSE '192.168.1.255' - FALSE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '192.168.1.0/24' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - TRUE '192.168.1.255' - TRUE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '192.168.4.96/27' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - FALSE '192.168.1.255' - FALSE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - TRUE '192.168.4.100' - TRUE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '192.168.1.1/32' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - TRUE '192.168.1.255' - FALSE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '192.168.1.0/24,192.168.2.0/24' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - TRUE '192.168.1.255' - TRUE '192.168.2.1' - TRUE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '192.168.1.0/24,192.168.2.0/24,127.0.0.1/31' 's' - FALSE '127.0.0.1' - TRUE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - TRUE '192.168.1.255' - TRUE '192.168.2.1' - TRUE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - FALSE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE Testing netmask '[0:0:0:0:0:0:0:0]/0' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - FALSE '192.168.1.255' - FALSE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - TRUE '[0:2:4:A:B:D:E:F401]' - TRUE '[0:0:0:0:0:0:0:0]' - TRUE Testing netmask '[0:2:4:A:B:D:E:f300]/120' 's' - FALSE '127.0.0.1' - FALSE '127.0.0.2' - FALSE '10.0.0.1' - FALSE '11.0.0.1' - FALSE '172.1.8.1' - FALSE '192.168.1.1' - FALSE '192.168.1.255' - FALSE '192.168.2.1' - FALSE '192.168.3.1' - FALSE '192.168.4.97' - FALSE '192.168.4.100' - FALSE '[0:2:4:A:B:D:E:F301]' - TRUE '[0:2:4:A:B:D:E:F401]' - FALSE '[0:0:0:0:0:0:0:0]' - FALSE } 1.4. XMLRPC Interface Some functionality may be controled using RPC commands. * ipmatch.reload - Reloads the cached ipmatch table. The original table remains active in case of any failure. * ipset.clean(ipset_name) - Clear all entries in "pending" ipset. * ipset.add(ipset_name, ip, netmask) - Add ip and mask into ipset. IPv6 should should be enclosed in brackets. Netmask may be identified as number or in IP form. Note that number requires leading slash, e.g. "/24" or "255.255.255.0". * ipset.commit(ipset_name) - Makes pending ip set usable by ip_is_trusted. Pending ip set is cleared. * ipset.list() - List declared ip sets. * ipset.print(ipset_name, pending) - Dump ipset trees. If pending non zero then pending ipset is dumped. kamailio-4.0.4/obsolete/permissions/rule.c0000644000000000000000000001013612223032460017300 0ustar rootroot/* * $Id$ * * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include "../../mem/mem.h" #include "../../sr_module.h" #include "../../mem/mem.h" #include "rule.h" /* * allocate memory for a new rule */ rule *new_rule(void) { rule *r; r = (rule *)pkg_malloc(sizeof(rule)); if (!r) { LOG(L_ERR, "permissions:new_rule(): Not enough memory\n"); return 0; } memset(r, 0, sizeof(rule)); return r; } /* * free memory allocated by a rule */ void free_rule(rule *r) { if (!r) return; if (r->left) free_expression(r->left); if (r->left_exceptions) free_expression(r->left_exceptions); if (r->right) free_expression(r->right); if (r->right_exceptions) free_expression(r->right_exceptions); if (r->next) free_rule(r->next); pkg_free(r); } /* * list rules */ void print_rule(rule *r) { if (!r) return; printf("\nNEW RULE:\n"); printf("\n\tLEFT: "); if (r->left) print_expression(r->left); else printf("ALL"); if (r->left_exceptions) { printf("\n\tLEFT EXCEPTIONS: "); print_expression(r->left_exceptions); } printf("\n\tRIGHT: "); if (r->right) print_expression(r->right); else printf("ALL"); if (r->right_exceptions) { printf("\n\tRIGHT EXCEPTIONS: "); print_expression(r->right_exceptions); } printf("\n"); if (r->next) print_rule(r->next); } /* * look for a proper rule matching with left:right */ int search_rule(rule *r, char *left, char *right) { rule *r1; r1 = r; while (r1) { if (( (!r->left) || (search_expression(r1->left, left)) ) && (!search_expression(r1->left_exceptions, left)) && ( (!r1->right) || (search_expression(r1->right, right)) ) && (!search_expression(r1->right_exceptions, right))) return 1; r1 = r1->next; } return 0; } /* * allocate memory for a new expression * str is saved in vale, and compiled to POSIX regexp (reg_value) */ expression *new_expression(char *str) { expression *e; if (!str) return 0; e = (expression *)pkg_malloc(sizeof(expression)); if (!e) { LOG(L_ERR, "permissions:new_expression(): Not enough memory\n"); return 0; } strcpy(e->value, str); e->reg_value = (regex_t*)pkg_malloc(sizeof(regex_t)); if (!e->reg_value) { LOG(L_ERR, "permissions:new_expression(): Not enough memory\n"); pkg_free(e); return 0; } if (regcomp(e->reg_value, str, REG_EXTENDED|REG_NOSUB|REG_ICASE) ) { LOG(L_ERR, "permissions:new_expression(): Bad regular expression: %s\n", str); pkg_free(e->reg_value); pkg_free(e); return NULL; } e->next = 0; return e; } /* * free memory allocated by an expression */ void free_expression(expression *e) { if (!e) return; if (e->next) free_expression(e->next); regfree(e->reg_value); pkg_free(e); } /* * list expressions */ void print_expression(expression *e) { if (!e) return; printf("%s, ", e->value); if (e->next) print_expression(e->next); } /* * look for matching expression */ int search_expression(expression *e, char *value) { expression *e1; e1 = e; while (e1) { if (regexec(e1->reg_value, value, 0, 0, 0) == 0) return 1; e1 = e1->next; } return 0; } kamailio-4.0.4/obsolete/permissions/trusted_rpc.c0000644000000000000000000000420612223032460020670 0ustar rootroot/* * $Id$ * * allow_trusted related functions * * Copyright (C) 2003 Juha Heinanen * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2006-08-14: db_mode is checked * trusted_reload() connects to the DB -- child processes * do not keep the connection open (Miklos) */ #include "../../dprint.h" #include "../../lib/srdb2/db.h" #include "permissions.h" #include "trusted_hash.h" #include "trusted.h" #include "trusted_rpc.h" const char* trusted_reload_doc[2] = { "Reload trusted table from database.", 0 }; /* * Fifo function to reload trusted table */ void trusted_reload(rpc_t* rpc, void* ctx) { if (db_mode != ENABLE_CACHE) { rpc->fault(ctx, 400, "Database cache is not enabled"); return; } /* reload cache */ if (reload_trusted_table() < 0) { rpc->fault(ctx, 400, "Trusted Table Reload Failed"); } } const char* trusted_dump_doc[2] = { "Return the contents of trusted table", 0 }; /* * Fifo function to print entries from current hash table */ void trusted_dump(rpc_t* rpc, void* ctx) { if (db_mode != ENABLE_CACHE) { rpc->fault(ctx, 400, "Database cache is not enabled"); return; } if (hash_table) { hash_table_print(*hash_table, rpc, ctx); } } kamailio-4.0.4/obsolete/permissions/trusted_rpc.h0000644000000000000000000000265412223032460020702 0ustar rootroot/* * $Id$ * * allow_trusted related functions * * Copyright (C) 2003 Juha Heinanen * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _TRUSTED_RPC_H #define _TRSUTED_RPC_H #include "../../rpc.h" extern const char* trusted_reload_doc[]; /* * Fifo function to reload trusted table */ void trusted_reload(rpc_t* rpc, void* ctx); extern const char* trusted_dump_doc[]; /* * Fifo function to print entries from current hash table */ void trusted_dump(rpc_t* rpc, void* ctx); #endif /* _TRUSTED_RPC_H */ kamailio-4.0.4/obsolete/permissions/ip_set.c0000644000000000000000000001272612223032461017624 0ustar rootroot/* $Id$ * * Copyright (C) 2008 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "ip_set.h" #include "../../resolve.h" #include #include void ip_set_init(struct ip_set *ip_set, int use_shm) { memset(ip_set, 0, sizeof(*ip_set)); ip_set->use_shm = use_shm; ip_tree_init(&ip_set->ipv4_tree); #ifdef USE_IPV6 ip_tree_init(&ip_set->ipv6_tree); #endif } void ip_set_destroy(struct ip_set *ip_set) { ip_tree_destroy(&ip_set->ipv4_tree, 0, ip_set->use_shm); #ifdef USE_IPV6 ip_tree_destroy(&ip_set->ipv6_tree, 0, ip_set->use_shm); #endif } int ip_set_add_ip(struct ip_set *ip_set, struct ip_addr *ip, unsigned int network_prefix) { switch (ip->af) { case AF_INET: return ip_tree_add_ip(&ip_set->ipv4_tree, ip->u.addr, (ip->len*8len*8:network_prefix, ip_set->use_shm); #ifdef USE_IPV6 case AF_INET6: return ip_tree_add_ip(&ip_set->ipv6_tree, ip->u.addr, (ip->len*8len*8:network_prefix, ip_set->use_shm); #endif default: return -1; } } int ip_set_ip_exists(struct ip_set *ip_set, struct ip_addr *ip) { struct ip_tree_find h; switch (ip->af) { case AF_INET: return ip_tree_find_ip(ip_set->ipv4_tree, ip->u.addr, ip->len*8, &h) > 0; #ifdef USE_IPV6 case AF_INET6: return ip_tree_find_ip(ip_set->ipv6_tree, ip->u.addr, ip->len*8, &h) > 0; #endif default: return -1; } } void ip_set_print(FILE *stream, struct ip_set *ip_set) { fprintf(stream, "IPv4:\n"); ip_tree_print(stream, ip_set->ipv4_tree, 2); #ifdef USE_IPV6 fprintf(stream, "IPv6:\n"); ip_tree_print(stream, ip_set->ipv6_tree, 2); #endif } int ip_set_add_list(struct ip_set *ip_set, str ip_set_s){ str ip_s, mask_s; /* parse comma delimited string of IPs and masks, e.g. 1.2.3.4,2.3.5.3/24,[abcd:12456:2775:ab::7533],9.8.7.6/255.255.255.0 */ while (ip_set_s.len) { while (ip_set_s.len && (*ip_set_s.s == ',' || *ip_set_s.s == ';' || *ip_set_s.s == ' ')) { ip_set_s.s++; ip_set_s.len--; } if (!ip_set_s.len) break; ip_s.s = ip_set_s.s; ip_s.len = 0; while (ip_s.len < ip_set_s.len && (ip_s.s[ip_s.len] != ',' && ip_s.s[ip_s.len] != ';' && ip_s.s[ip_s.len] != ' ' && ip_s.s[ip_s.len] != '/')) { ip_s.len++; } ip_set_s.s += ip_s.len; ip_set_s.len -= ip_s.len; mask_s.len = 0; mask_s.s=0; if (ip_set_s.len && ip_set_s.s[0] == '/') { ip_set_s.s++; ip_set_s.len--; mask_s.s = ip_set_s.s; while (mask_s.len < ip_set_s.len && (mask_s.s[mask_s.len] != ',' && mask_s.s[mask_s.len] != ';' && mask_s.s[mask_s.len] != ' ')) { mask_s.len++; } ip_set_s.s += mask_s.len; ip_set_s.len -= mask_s.len; } if (ip_set_add_ip_s(ip_set, ip_s, mask_s) < 0) { return -1; } } return 0; } int ip_set_add_ip_s(struct ip_set *ip_set, str ip_s, str mask_s){ int fl; struct ip_addr *ip, ip_buff; unsigned int prefix, i; if ( ((ip = str2ip(&ip_s))==0) #ifdef USE_IPV6 && ((ip = str2ip6(&ip_s))==0) #endif ){ ERR("ip_set_add_ip_s: string to ip conversion error '%.*s'\n", ip_s.len, ip_s.s); return -1; } ip_buff = *ip; if (mask_s.len > 0) { i = 0; fl = 0; while (i < mask_s.len && ((mask_s.s[i] >= '0' && mask_s.s[i] <= '9') || (mask_s.s[i] >= 'a' && mask_s.s[i] <= 'f') || (mask_s.s[i] >= 'A' && mask_s.s[i] <= 'F') || mask_s.s[i] == '.' || mask_s.s[i] == ':' || mask_s.s[i] == '[' || mask_s.s[i] == ']' ) ) { fl |= mask_s.s[i] < '0' || mask_s.s[i] > '9'; i++; } if (fl) { /* 255.255.255.0 format */ if ( ((ip = str2ip(&mask_s))==0) #ifdef USE_IPV6 && ((ip = str2ip6(&mask_s))==0) #endif ){ ERR("ip_set_add_ip_s: string to ip mask conversion error '%.*s'\n", mask_s.len, mask_s.s); return -1; } if (ip_buff.af != ip->af) { ERR("ip_set_add_ip_s: IPv4 vs. IPv6 near '%.*s' vs. '%.*s'\n", ip_s.len, ip_s.s, mask_s.len, mask_s.s); return -1; } fl = 0; prefix = 0; for (i=0; ilen; i++) { unsigned char msk; msk = 0x80; while (msk) { if ((ip->u.addr[i] & msk) != 0) { if (fl) { ERR("ip_set_add_ip_s: bad IP mask '%.*s'\n", mask_s.len, mask_s.s); return -1; } prefix++; } else { fl = 1; } msk /= 2; } } } else { /* 24 format */ if (str2int(&mask_s, &prefix) < 0) { ERR("ip_set_add_ip_s: cannot convert mask '%.*s'\n", mask_s.len, mask_s.s); return -1; } } } else { prefix = ip_buff.len*8; } if (ip_set_add_ip(ip_set, &ip_buff, prefix) < 0) { ERR("ip_set_add_ip_s: cannot add IP into ip set\n"); return -1; } return 0; } kamailio-4.0.4/obsolete/permissions/permissions.c0000644000000000000000000005370612223032461020717 0ustar rootroot/* * $Id$ * * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * Copyright (C) 2003 iptel.org * Copyright (C) 2003 Juha Heinanen (jh@tutpro.com) * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2006-08-10: file operation functions are moved to allow_files.c * memory is not allocated for file containers if not needed * safe_file_load module parameter introduced (Miklos) * 2006-08-14: child processes do not keep the DB connection open * if cache is enabled (Miklos) * 2008-07-xx: added ip_is_trusted function (tma) * 2008-08-01: added ipset manipulable via RPC (tma) */ #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "permissions.h" #include "trusted.h" #include "allow_files.h" #include "ipmatch.h" #include "im_db.h" #include "permissions_rpc.h" #include "ip_set.h" #include "../../resolve.h" MODULE_VERSION rule_file_t *allow = NULL; /* Parsed allow files */ rule_file_t *deny = NULL; /* Parsed deny files */ static int allow_rules_num = 0; /* Number of parsed allow files (excluding default file) */ static int deny_rules_num = 0; /* Number of parsed deny files (excluding default file) */ /* Module parameter variables */ /* general parameters */ char* db_url = 0; /* Don't connect to the database by default */ int db_mode = DISABLE_CACHE; /* Database usage mode: 0=no cache, 1=cache */ int max_rule_files = MAX_RULE_FILES; /* maximum nuber of allowed allow/deny file pairs */ /* parameters for allow_* functions */ static char* default_allow_file = DEFAULT_ALLOW_FILE; static char* default_deny_file = DEFAULT_DENY_FILE; static char* allow_suffix = ".allow"; static char* deny_suffix = ".deny"; int check_all_branches = 1; /* By default we check all branches */ int safe_file_load = 0; /* for allow_trusted function */ char* trusted_table = "trusted"; /* Name of trusted table */ char* source_col = "src_ip"; /* Name of source address column */ char* proto_col = "proto"; /* Name of protocol column */ char* from_col = "from_pattern"; /* Name of from pattern column */ /* parameters for ipmatch functions */ char *ipmatch_table = "ipmatch"; /* Database API */ db_ctx_t *db_conn = NULL; static str *ip_set_list_names = NULL; /* declared names */ static struct ip_set_ref **ip_set_list_local = NULL; /* local copy of ip set in shared memory */ static int ip_set_list_count = 0; /* number of declared names */ /* fixup function prototypes */ static int fixup_files_1(void** param, int param_no); static int fixup_files_2(void** param, int param_no); static int fixup_ip_is_trusted(void** param, int param_no); static int fixup_w_im(void **, int); static int fixup_w_im_onsend(void **, int); static int fixup_param_declare_ip_set( modparam_t type, void* val); /* module function prototypes */ static int allow_routing_0(struct sip_msg* msg, char* str1, char* str2); static int allow_routing_1(struct sip_msg* msg, char* basename, char* str2); static int allow_routing_2(struct sip_msg* msg, char* allow_file, char* deny_file); static int allow_register_1(struct sip_msg* msg, char* basename, char* s); static int allow_register_2(struct sip_msg* msg, char* allow_file, char* deny_file); static int allow_refer_to_1(struct sip_msg* msg, char* basename, char* s); static int allow_refer_to_2(struct sip_msg* msg, char* allow_file, char* deny_file); int w_im_2(struct sip_msg *msg, char *str1, char *str2); int w_im_1(struct sip_msg *msg, char *str1, char *str2); int w_im_onsend(struct sip_msg *msg, char *str1, char *str2); int w_im_filter(struct sip_msg *msg, char *str1, char *str2); static int w_ip_is_trusted(struct sip_msg *msg, char *str1, char *str2); /* module interface function prototypes */ static int mod_init(void); static void mod_exit(void); static int child_init(int rank); /* Exported functions */ static cmd_export_t cmds[] = { {"allow_routing", allow_routing_0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"allow_routing", allow_routing_1, 1, fixup_files_1, REQUEST_ROUTE | FAILURE_ROUTE}, {"allow_routing", allow_routing_2, 2, fixup_files_2, REQUEST_ROUTE | FAILURE_ROUTE}, {"allow_register", allow_register_1, 1, fixup_files_1, REQUEST_ROUTE | FAILURE_ROUTE}, {"allow_register", allow_register_2, 2, fixup_files_2, REQUEST_ROUTE | FAILURE_ROUTE}, {"allow_refer_to", allow_refer_to_1, 1, fixup_files_1, REQUEST_ROUTE | FAILURE_ROUTE}, {"allow_refer_to", allow_refer_to_2, 2, fixup_files_2, REQUEST_ROUTE | FAILURE_ROUTE}, {"allow_trusted", allow_trusted, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"ipmatch", w_im_1, 1, fixup_w_im, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE }, {"ipmatch", w_im_2, 2, fixup_w_im, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE }, {"ipmatch_onsend", w_im_onsend, 1, fixup_w_im_onsend, ONSEND_ROUTE }, {"ipmatch_filter", w_im_filter, 1, fixup_int_1, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | ONSEND_ROUTE}, {"ip_is_trusted", w_ip_is_trusted, 2, fixup_ip_is_trusted, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | ONSEND_ROUTE | BRANCH_ROUTE}, {"ip_is_in_ipset", w_ip_is_trusted, 2, fixup_ip_is_trusted, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | ONSEND_ROUTE | BRANCH_ROUTE}, {0, 0, 0, 0, 0} }; /* Exported parameters */ static param_export_t params[] = { {"db_url", PARAM_STRING, &db_url }, {"db_mode", PARAM_INT, &db_mode }, {"default_allow_file", PARAM_STRING, &default_allow_file}, {"default_deny_file", PARAM_STRING, &default_deny_file }, {"check_all_branches", PARAM_INT, &check_all_branches}, {"allow_suffix", PARAM_STRING, &allow_suffix }, {"deny_suffix", PARAM_STRING, &deny_suffix }, {"max_rule_files", PARAM_INT, &max_rule_files }, {"safe_file_load", PARAM_INT, &safe_file_load }, {"trusted_table", PARAM_STRING, &trusted_table }, {"source_col", PARAM_STRING, &source_col }, {"proto_col", PARAM_STRING, &proto_col }, {"from_col", PARAM_STRING, &from_col }, {"ipmatch_table", PARAM_STRING, &ipmatch_table }, {"declare_ipset", PARAM_STRING|PARAM_USE_FUNC, fixup_param_declare_ip_set}, {0, 0, 0} }; /* Module interface */ struct module_exports exports = { "permissions", cmds, /* Exported functions */ permissions_rpc, /* RPC methods */ params, /* Exported parameters */ mod_init, /* module initialization function */ 0, /* response function */ mod_exit, /* destroy function */ 0, /* oncancel function */ child_init /* child initialization function */ }; /* * Convert the name of the files into table index */ static int fixup_files_2(void** param, int param_no) { int idx; if (param_no == 1) { idx = load_file(*param, &allow, &allow_rules_num, 0); } else if( param_no == 2) { idx = load_file(*param, &deny, &deny_rules_num, 0); } else { return 0; } if (idx < 0) return -1; pkg_free(*param); *param = (void*)(long)idx; return 0; } /* * Convert the name of the file into table index */ static int fixup_files_1(void** param, int param_no) { char* buffer; int param_len, suffix_len; int idx1, idx2; if (param_no != 1) return 0; param_len = strlen((char*)*param); if (strlen(allow_suffix) > strlen(deny_suffix)) { suffix_len = strlen(allow_suffix); } else { suffix_len = strlen(deny_suffix); } buffer = pkg_malloc(param_len + suffix_len + 1); if (!buffer) { LOG(L_ERR, "fixup_files_1(): No memory left\n"); return -1; } strcpy(buffer, (char*)*param); strcat(buffer, allow_suffix); /* load allow file */ idx1 = load_file(buffer, &allow, &allow_rules_num, 0); if (idx1 < 0) { pkg_free(buffer); return -1; } strcpy(buffer + param_len, deny_suffix); idx2 = load_file(buffer, &deny, &deny_rules_num, 0); if (idx2 < 0) { pkg_free(buffer); return -1; } if (idx1 != idx2) { LOG(L_ERR, "fixup_files_1(): allow and deny indexes are not equal!\n"); pkg_free(buffer); return -1; } pkg_free(*param); *param = (void*)(long)idx1; pkg_free(buffer); return 0; } /* connect to the database */ static int perm_init_db(void) { db_conn = db_ctx("permissions"); if (db_conn == NULL) { LOG(L_ERR, "perm_init_db(): Unable to create database context\n"); return -1; } if (db_add_db(db_conn, db_url) < 0) { LOG(L_ERR, "perm_init_db(): cannot add the url to database context\n"); return -1; } if (db_connect(db_conn) < 0) { LOG(L_ERR, "perm_init_db(): Unable to connect to database\n"); return -1; } return 0; } /* destroy the DB connection */ static void perm_destroy_db(void) { if (db_conn) { db_disconnect(db_conn); db_ctx_free(db_conn); db_conn = NULL; } } /* * module initialization function */ static int mod_init(void) { LOG(L_INFO, "permissions - initializing\n"); /* do not load the files if not necessary */ if (strlen(default_allow_file) || strlen(default_deny_file)) { if (load_file(default_allow_file, &allow, &allow_rules_num, 1) != 0) goto error; if (load_file(default_deny_file, &deny, &deny_rules_num, 1) != 0) goto error; } if (db_url && (db_mode == ENABLE_CACHE)) { /* database backend is enabled, and cache is requested -- load the DB */ if (perm_init_db()) goto error; /* prepare DB commands for trusted table */ if (init_trusted_db()) { LOG(L_ERR, "Error while preparing DB commands for trusted table\n"); goto error; } /* init trusted tables */ if (init_trusted() != 0) { LOG(L_ERR, "Error while initializing allow_trusted function\n"); goto error; } /* prepare DB commands for ipmatch table */ if (init_im_db()) { LOG(L_ERR, "Error while preparing DB commands for ipmatch table\n"); goto error; } /* init ipmatch table */ if (init_ipmatch() != 0) { LOG(L_ERR, "Error while initializing ipmatch table\n"); goto error; } /* Destory DB connection, we do not need it anymore, each child process will create its own connection */ destroy_trusted_db(); destroy_im_db(); perm_destroy_db(); } if (ip_set_list_malloc(ip_set_list_count, ip_set_list_names) < 0) goto error; if (ip_set_list_count > 0) { ip_set_list_local = pkg_malloc(ip_set_list_count*sizeof(*ip_set_list_local)); if (!ip_set_list_local) goto error; memset(ip_set_list_local, 0, sizeof(*ip_set_list_local)*ip_set_list_count); } if (ip_set_list_names) pkg_free(ip_set_list_names); /* we need not longer names in pkg memory */ return 0; error: /* free file containers */ delete_files(&allow, allow_rules_num); delete_files(&deny, deny_rules_num); /* destroy DB cmds */ destroy_trusted_db(); destroy_im_db(); /* destory DB connection */ perm_destroy_db(); /* free the cache */ clean_trusted(); clean_ipmatch(); ip_set_list_free(); return -1; } static int child_init(int rank) { if ((rank <= 0) && (rank != PROC_RPC) && (rank != PROC_UNIXSOCK)) return 0; if (db_url) { /* Connect to the DB regarless of cache or non-cache mode, because we either have to query the DB runtime, or reload the cache via RPC call */ if (perm_init_db()) goto error; /* prepare DB commands for trusted tables */ if (init_trusted_db()) { LOG(L_ERR, "Error while preparing DB commands for trusted table\n"); goto error; } /* prepare DB commands for ipmatch tables */ if (init_im_db()) { LOG(L_ERR, "Error while preparing DB commands for ipmatch table\n"); goto error; } } return 0; error: /* destroy DB cmds */ destroy_trusted_db(); destroy_im_db(); /* destroy DB connection */ perm_destroy_db(); return -1; } /* * destroy function */ static void mod_exit(void) { int i; /* free file containers */ delete_files(&allow, allow_rules_num); delete_files(&deny, deny_rules_num); clean_trusted(); clean_ipmatch(); if (ip_set_list_local) { for (i=0; i1 */ if (ip_set_list_local[i]) shm_free(ip_set_list_local[i]); } } ip_set_list_free(); } /* * Uses default rule files from the module parameters */ int allow_routing_0(struct sip_msg* msg, char* str1, char* str2) { return check_routing(msg, 0); } int allow_routing_1(struct sip_msg* msg, char* basename, char* s) { return check_routing(msg, (int)(long)basename); } /* * Accepts allow and deny files as parameters */ int allow_routing_2(struct sip_msg* msg, char* allow_file, char* deny_file) { /* Index converted by fixup_files_2 */ return check_routing(msg, (int)(long)allow_file); } int allow_register_1(struct sip_msg* msg, char* basename, char* s) { return check_register(msg, (int)(long)basename); } int allow_register_2(struct sip_msg* msg, char* allow_file, char* deny_file) { return check_register(msg, (int)(long)allow_file); } int allow_refer_to_1(struct sip_msg* msg, char* basename, char* s) { return check_refer_to(msg, (int)(long)basename); } int allow_refer_to_2(struct sip_msg* msg, char* allow_file, char* deny_file) { return check_refer_to(msg, (int)(long)allow_file); } /* fixup function for w_ipmatch_* */ static int fixup_w_im(void **param, int param_no) { int ret; str *s; if (param_no == 1) { ret = fix_param(FPARAM_AVP, param); if (ret <= 0) return ret; ret = fix_param(FPARAM_SELECT, param); if (ret <= 0) return ret; ret = fix_param(FPARAM_STR, param); if (ret == 0) { s = &((fparam_t *)*param)->v.str; if ((s->len == 3) && (memcmp(s->s, "src", 3) == 0)) return 0; if ((s->len == 4) && (memcmp(s->s, "via2", 4) == 0)) return 0; LOG(L_ERR, "ERROR: fixup_w_im(): unknown string parameter\n"); return -1; } else if (ret < 0) { return ret; } LOG(L_ERR, "ERROR: fixup_w_im(): unknown parameter type\n"); return -1; } else if (param_no == 2) { if (fix_param(FPARAM_AVP, param) != 0) { LOG(L_ERR, "ERROR: fixup_w_im(): unknown AVP identifier: %s\n", (char*)*param); return -1; } return 0; } return 0; } /* fixup function for w_ipmatch_onsend */ static int fixup_w_im_onsend(void **param, int param_no) { char *ch; if (param_no == 1) { ch = (char *)*param; if ((ch[0] != 'd') && (ch[0] != 'r')) { LOG(L_ERR, "ERROR: fixup_w_im_onsend(): unknown string parameter\n"); return -1; } return 0; } return 0; } /* wrapper function for ipmatch */ int w_im_2(struct sip_msg *msg, char *str1, char *str2) { if (db_mode != ENABLE_CACHE) { LOG(L_ERR, "ERROR: w_im_2(): ipmatch function supports only cache mode, set db_mode module parameter!\n"); return -1; } return ipmatch_2(msg, str1, str2); } /* wrapper function for ipmatch */ int w_im_1(struct sip_msg *msg, char *str1, char *str2) { if (db_mode != ENABLE_CACHE) { LOG(L_ERR, "ERROR: w_im_1(): ipmatch function supports only cache mode, set db_mode module parameter!\n"); return -1; } return ipmatch_1(msg, str1, str2); } /* wrapper function for ipmatch */ int w_im_onsend(struct sip_msg *msg, char *str1, char *str2) { if (db_mode != ENABLE_CACHE) { LOG(L_ERR, "ERROR: w_im_onsend(): ipmatch function supports only cache mode, set db_mode module parameter!\n"); return -1; } return ipmatch_onsend(msg, str1, str2); } /* wrapper function for ipmatch_filter */ int w_im_filter(struct sip_msg *msg, char *str1, char *str2) { if (db_mode != ENABLE_CACHE) { LOG(L_ERR, "ERROR: w_im_filter(): ipmatch function supports only cache mode, set db_mode module parameter!\n"); return -1; } return ipmatch_filter(msg, str1, str2); } struct ip_set_param { enum {IP_SET_PARAM_KIND_GLOBAL, IP_SET_PARAM_KIND_LOCAL} kind; union { struct { str s; unsigned int sz; struct ip_set ip_set; fparam_t *fparam; } local; struct { struct ip_set_list_item *ip_set; } global; }u; }; #define MODULE_NAME "permissions" static inline int is_ip_set_name(str *s) { return (s->len && ((s->s[0] >= 'A' && s->s[0] <= 'Z') || (s->s[0] >= 'a' && s->s[0] <= 'z') || s->s[0] == '_')); } static int fixup_param_declare_ip_set( modparam_t type, void* val) { str *p; int i; str s; s.s = val; s.len = strlen(s.s); for (i=0; i=s.len && memcmp(val, ip_set_list_names[i].s, s.len) == 0) { ERR(MODULE_NAME": declare_ip_set: ip set '%.*s' already exists\n", s.len, s.s); return E_CFG; } } if (!is_ip_set_name(&s)) { ERR(MODULE_NAME": declare_ip_set: ip set '%.*s' is not correct identifier\n", s.len, s.s); return E_CFG; } s.len = strlen(s.s); p = pkg_realloc(ip_set_list_names, sizeof(*p)*(ip_set_list_count+1)); if (!p) return E_OUT_OF_MEM; p[ip_set_list_count] = s; ip_set_list_count++; ip_set_list_names = p; return E_OK; }; static int w_ip_is_trusted(struct sip_msg* msg, char* _ip_set, char* _ip) { str ip_set_s, ip_s; struct ip_addr *ip, ip_buf; struct ip_set new_ip_set, *ip_set; struct ip_set_list_item *isli = NULL; int kind; kind = ((struct ip_set_param*)_ip_set)->kind; if (kind == IP_SET_PARAM_KIND_LOCAL) { if (get_str_fparam(&ip_set_s, msg, ((struct ip_set_param*)_ip_set)->u.local.fparam) < 0) { ERR(MODULE_NAME": ip_is_trusted: Error while obtaining ip_set parameter value\n"); return -1; } if (is_ip_set_name(&ip_set_s)) { isli = ip_set_list_find_by_name(ip_set_s); if (!isli) { ERR(MODULE_NAME": ip_is_trusted: ip set '%.*s' is not declared\n", ip_set_s.len, ip_set_s.s); return -1; } kind = IP_SET_PARAM_KIND_GLOBAL; goto force_global; } ip_set = &((struct ip_set_param*)_ip_set)->u.local.ip_set; } else { isli = ((struct ip_set_param*)_ip_set)->u.global.ip_set; force_global: if (!isli->ip_set) return -1; /* empty ip set */ if (unlikely(isli->ip_set != ip_set_list_local[isli->idx])) { /* global ip set has changed ? */ if (ip_set_list_local[isli->idx]) { if (atomic_dec_and_test(&ip_set_list_local[isli->idx]->refcnt)) { ip_set_destroy(&ip_set_list_local[isli->idx]->ip_set); shm_free(ip_set_list_local[isli->idx]); ip_set_list_local[isli->idx] = NULL; } } lock_get(&isli->read_lock); atomic_inc(&isli->ip_set->refcnt); ip_set_list_local[isli->idx] = isli->ip_set; lock_release(&isli->read_lock); } ip_set = &ip_set_list_local[isli->idx]->ip_set; } if (get_str_fparam(&ip_s, msg, (fparam_t*)_ip) < 0) { ERR(MODULE_NAME": ip_is_trusted: Error while obtaining ip parameter value\n"); return -1; } if (!ip_s.len || !ip_set_s.len) return -1; switch (ip_s.s[0]) { case 's': /* src */ case 'S': ip = &msg->rcv.src_ip; break; case 'd': /* dst */ case 'D': ip = &msg->rcv.dst_ip; break; case 'r': /* rcv */ case 'R': ip = &msg->rcv.bind_address->address; break; default: /* string -> ip */ if ( ((ip = str2ip(&ip_s))==0) #ifdef USE_IPV6 && ((ip = str2ip6(&ip_s))==0) #endif ){ ERR(MODULE_NAME": ip_is_trusted: string to ip conversion error '%.*s'\n", ip_s.len, ip_s.s); return -1; } ip_buf = *ip; ip = &ip_buf; /* value has been in static buffer */ break; } /* test if ip_set string has changed since last call */ if (kind == IP_SET_PARAM_KIND_LOCAL) { if (((struct ip_set_param*)_ip_set)->u.local.s.len != ip_set_s.len || memcmp(((struct ip_set_param*)_ip_set)->u.local.s.s, ip_set_s.s, ip_set_s.len) != 0) { ip_set_init(&new_ip_set, 0); if (ip_set_add_list(&new_ip_set, ip_set_s) < 0) { ip_set_destroy(&new_ip_set); return -1; }; if (((struct ip_set_param*)_ip_set)->u.local.sz < ip_set_s.len) { void *p; p = pkg_realloc(((struct ip_set_param*)_ip_set)->u.local.s.s, ip_set_s.len); if (!p) { ip_set_destroy(&new_ip_set); return E_OUT_OF_MEM; } ((struct ip_set_param*)_ip_set)->u.local.s.s = p; ((struct ip_set_param*)_ip_set)->u.local.sz = ip_set_s.len; } memcpy(((struct ip_set_param*)_ip_set)->u.local.s.s, ip_set_s.s, ip_set_s.len); ((struct ip_set_param*)_ip_set)->u.local.s.len = ip_set_s.len; ip_set_destroy(&((struct ip_set_param*)_ip_set)->u.local.ip_set); ((struct ip_set_param*)_ip_set)->u.local.ip_set = new_ip_set; } } /* ip_set_print(stderr, &ip_set); */ switch (ip_set_ip_exists(ip_set, ip)) { case IP_TREE_FIND_FOUND: case IP_TREE_FIND_FOUND_UPPER_SET: return 1; default: return -1; } } static int fixup_ip_is_trusted(void** param, int param_no) { int ret = E_CFG; struct ip_set_param *p; str s; if (param_no == 1) { p = pkg_malloc(sizeof(*p)); if (!p) return E_OUT_OF_MEM; memset(p, 0, sizeof(*p)); s.s = *param; s.len = strlen(s.s); if (is_ip_set_name(&s)) { p->u.global.ip_set = ip_set_list_find_by_name(s); if (!p->u.global.ip_set) { ERR(MODULE_NAME": fixup_ip_is_trusted: ip set '%.*s' is not declared\n", s.len, s.s); goto err; } p->kind = IP_SET_PARAM_KIND_GLOBAL; } else { ret = fixup_var_str_12(param, param_no); if (ret < 0) goto err; ip_set_init(&p->u.local.ip_set, 0); p->u.local.fparam = *param; *param = p; p->kind = IP_SET_PARAM_KIND_LOCAL; } } else { return fixup_var_str_12(param, param_no); } return E_OK; err: pkg_free(p); return ret; } kamailio-4.0.4/obsolete/permissions/ip_set.h0000644000000000000000000000342612223032461017626 0ustar rootroot/* $Id$ * * Copyright (C) 2008 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _IP_SET_H_ #define _IP_SET_H_ #include "ip_tree.h" #include "../../ip_addr.h" #include /* ip_set stuff, combines IPv4 and IPv6 tree in one set */ struct ip_set { int use_shm; struct ip_tree_leaf *ipv4_tree; #ifdef USE_IPV6 struct ip_tree_leaf *ipv6_tree; #endif }; extern void ip_set_init(struct ip_set *ip_set, int use_shm); extern void ip_set_destroy(struct ip_set *ip_set); extern int ip_set_add_ip(struct ip_set *ip_set, struct ip_addr *ip, unsigned int network_prefix); extern int ip_set_add_ip_s(struct ip_set *ip_set, str ip_s, str mask_s); extern int ip_set_ip_exists(struct ip_set *ip_set, struct ip_addr *ip); extern void ip_set_print(FILE *stream, struct ip_set *ip_set); extern int ip_set_add_list(struct ip_set *ip_set, str ip_set_s); #endif kamailio-4.0.4/obsolete/permissions/rule.h0000644000000000000000000000433412223032460017310 0ustar rootroot/* * $Id$ * * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef RULE_H #define RULE_H 1 #include #define EXPRESSION_LENGTH 100 /* maximum length of an expression */ #define LINE_LENGTH 500 /* maximum length of lines in the config file */ struct rule_struct; typedef struct rule_struct rule; struct expression_struct; typedef struct expression_struct expression; rule *new_rule(void); void free_rule(rule *r); void print_rule(rule *r); int search_rule(rule *r, char *left, char *right); expression *new_expression(char *str); void free_expression(expression *e); void print_expression(expression *e); int search_expression(expression *e, char *value); /* * stores an expression * value represents the string, and reg_value is the compiled string to POSIX regular expression */ struct expression_struct { char value[EXPRESSION_LENGTH+1]; regex_t *reg_value; struct expression_struct *next; }; /* * stores 4 lists of expressions in the following way: * * a, b, c EXCEPT d, e : f, g EXCEPT h * left = a, b, c * left_exceptions = d, e * right = f, g * right_exceptions = h */ struct rule_struct { expression *left, *left_exceptions, *right, *right_exceptions; struct rule_struct *next; }; #endif /* RULE_H */ kamailio-4.0.4/obsolete/permissions/im_rpc.c0000644000000000000000000000315612223032460017606 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "../../lib/srdb2/db.h" #include "permissions.h" #include "im_db.h" #include "im_rpc.h" const char* im_reload_doc[2] = { "Reloads ipmatch cache", 0 }; /* XML RPC function to reload impatch cache */ void im_reload(rpc_t *rpc, void *c) { if (db_mode != ENABLE_CACHE) { rpc->fault(c, 400, "Database cache is not enabled"); return; } if (reload_im_cache()) { /* error occured during the reload */ LOG(L_ERR, "ERROR: Reloading of ipmatch cache failed\n"); rpc->fault(c, 400, "Reloading failed"); } else { /* reload is successful */ LOG(L_INFO, "INFO: ipmatch cache is reloaded\n"); } } kamailio-4.0.4/obsolete/permissions/im_locks.c0000644000000000000000000000517212223032460020135 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "../../sched_yield.h" #include "im_locks.h" /* reader lock for ipmatch cache */ void reader_lock_imhash(void) { /* prefer writers: suggest readers not to claim a reader ref_count if a writer is interested in writing; the reader goes spinning, not too bad, writes are rare (Jiri) */ while (IM_HASH->writer_demand) sched_yield(); /* it may be that reader tried to read at the same time when writer decided to write -- make sure reader doesn't enter */ while (1) { lock_get(&(IM_HASH->read_lock)); if (IM_HASH->reader_count >= 0) { IM_HASH->reader_count++; lock_release(&(IM_HASH->read_lock)); break; } /* reader_count < 0 ... writer is active; retry */ lock_release(&(IM_HASH->read_lock)); sched_yield(); } } /* reader release for ipmatch cache */ void reader_release_imhash(void) { lock_get(&(IM_HASH->read_lock)); IM_HASH->reader_count--; lock_release(&(IM_HASH->read_lock)); } /* set writer demand on ipmatch cache */ void set_wd_imhash(void) { /* tell the readers to wait for a bit, while the new table becomes active */ IM_HASH->writer_demand = 1; /* write_lock is already set */ /* wait for the readers (Jiri) */ while(1) { lock_get(&(IM_HASH->read_lock)); if (IM_HASH->reader_count == 0) { IM_HASH->reader_count--; lock_release(&(IM_HASH->read_lock)); break; } lock_release(&(IM_HASH->read_lock)); /* processes still reading, retry ! */ sched_yield(); } } /* delete writer demand on ipmatch cache */ void del_wd_imhash(void) { lock_get(&(IM_HASH->read_lock)); IM_HASH->reader_count++; lock_release(&(IM_HASH->read_lock)); IM_HASH->writer_demand = 0; } kamailio-4.0.4/obsolete/permissions/ip_tree.h0000644000000000000000000001001112223032460017755 0ustar rootroot/* $Id$ * * Copyright (C) 2008 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _IP_TREE_H_ #define _IP_TREE_H_ 1 #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../ip_addr.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../trim.h" #include "../../ut.h" /* Implements algorithm for testing if particular address is in a iP adrress set. IP adresses may also describe a subnetwork, i.e. only prefix is valuable and trailing part of address has no effect for decision. The algorithm is common, both IPv4 and IPv6 are supported. Reduce(match)/decide(fork) algorithm is applied to minimize memory consumption and looping. As many bits as possible are matched in leaf (longest prefix match) . If does not match "match_bit_num" then IP is not in set otherwise next bit decides what is next leaf and matching goes on. If there is not next leaf then algorithm is over (it's subnet address or special case particular IP address, in this case address is matched completly). Examples of ip tree {prefix_match_len, prefix_match, next}: 0.0.0.0/0, i.e. all addresses {0, {}, {NULL, NULL}} 128.0.0.0/1 {1, {0x80}, {NULL, NULL}} 127.0.1.0/24 {24, {0x7F, 0x00, 0x01}, {NULL, NULL}} 127.0.1.0/24, 127.0.2.0/24 01111111.00000000.00000001.00000000 01111111.00000000.00000010.00000000 {22, {0x7F, 0x00, 0x00}, { {1, {0x80}, {NULL, NULL}}, {1, {0x00}, {NULL, NULL}} } } 127.0.0.0/32, 127.0.0.1/32 {31, {0x7F, 0x00, 0x00, 0x00}, {NULL, NULL}} 192.168.5.64/26, 192.168.5.15/32, 10.0.0.0/8 11000000.10101000.00000101.01-000000 11000000.10101000.00000101.00001111 00001010-00000000.00000000.00000000 {0, {}, { {7, {00010100}, {NULL, NULL}}, {24, {10000001,01010000,00001010}, { {6, {00111100}, {NULL, NULL}}, {0, {}, {NULL, NULL}} }} }} */ struct ip_tree_leaf { unsigned int prefix_match_len; /* next prefix_match_len must be equal to next bit in IP address being compared */ struct ip_tree_leaf *next[2]; /* tree goes on in leaf based on first bit following prefix_match, if next[0] && next[1] are null then IP matches - it's subnet address */ unsigned char prefix_match[0]; /* match_bits div 8 + 1, the same representation as ip address */ }; struct ip_tree_find { struct ip_tree_leaf *leaf; unsigned int leaf_prefix_match_len; unsigned char *leaf_prefix_match; unsigned char leaf_prefix_match_mask; unsigned char *ip; unsigned int ip_len; unsigned char ip_mask; }; #define IP_TREE_FIND_NOT_FOUND 0 #define IP_TREE_FIND_FOUND 1 #define IP_TREE_FIND_FOUND_UPPER_SET 2 extern void ip_tree_init(struct ip_tree_leaf **tree); extern void ip_tree_destroy(struct ip_tree_leaf **tree, int leaves_only, int use_shm); extern int ip_tree_find_ip(struct ip_tree_leaf *tree, unsigned char *ip, unsigned int ip_len, struct ip_tree_find *h); extern int ip_tree_add_ip(struct ip_tree_leaf **tree, unsigned char *ip, unsigned int ip_len, int use_shm); extern void ip_tree_print(FILE *stream, struct ip_tree_leaf *tree, unsigned int indent); extern str ip_tree_mask_to_str(unsigned char *pm, unsigned int len); #endif kamailio-4.0.4/obsolete/permissions/ip_set_rpc.h0000644000000000000000000000412012223032460020461 0ustar rootroot/* * $Id$ * * allow_trusted related functions * * Copyright (C) 2008 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _IP_SET_RPC_H #define _IP_SET_RPC_H 1 #include "ip_set.h" #include "../../atomic_ops.h" #include "../../lock_ops.h" #include "../../rpc.h" struct ip_set_ref { struct ip_set ip_set; atomic_t refcnt; }; struct ip_set_list_item { int idx; str name; gen_lock_t read_lock; gen_lock_t write_lock; struct ip_set_ref *ip_set; struct ip_set ip_set_pending; }; extern int ip_set_list_malloc(int num, str *names); extern void ip_set_list_free(); extern struct ip_set_list_item* ip_set_list_find_by_name(str name); /* RPC functions */ extern const char* rpc_ip_set_clean_doc[]; extern void rpc_ip_set_clean(rpc_t* rpc, void* ctx); extern const char* rpc_ip_set_add_doc[]; extern void rpc_ip_set_add(rpc_t* rpc, void* ctx); extern const char* rpc_ip_set_commit_doc[]; extern void rpc_ip_set_commit(rpc_t* rpc, void* ctx); extern const char* rpc_ip_set_list_doc[]; extern void rpc_ip_set_list(rpc_t* rpc, void* ctx); extern const char* rpc_ip_set_print_doc[]; extern void rpc_ip_set_print(rpc_t* rpc, void* ctx); #endif /* _IP_SET_RPC_H */ kamailio-4.0.4/obsolete/permissions/ipmatch.h0000644000000000000000000000274512223032460017772 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _IPMATCH_H #define _IPMATCH_H /* initialize ipmatch table */ int init_ipmatch(void); /* destroy function */ void clean_ipmatch(void); /* wrapper functions for ipmatch */ int ipmatch_2(struct sip_msg *msg, char *str1, char *str2); int ipmatch_1(struct sip_msg *msg, char *str1, char *str2); int ipmatch_onsend(struct sip_msg *msg, char *str1, char *str2); /* set IM_FILTER */ int ipmatch_filter(struct sip_msg *msg, char *str1, char *str2); #endif /* _IPMATCH_H */ kamailio-4.0.4/obsolete/permissions/im_hash.c0000644000000000000000000001576512223032460017756 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include "../../str.h" #include "../../ut.h" #include "../../ip_addr.h" #include "../../mem/shm_mem.h" #include "im_locks.h" #include "im_hash.h" /* global variable for DB cache */ im_hash_t *IM_HASH = NULL; /* parses an ipv4 address with or without port number */ static int parse_ipv4(str *_s, struct ip_addr *_ip, unsigned short *_port) { int i, j; str s; unsigned int v; unsigned int port; _ip->af = AF_INET; _ip->len = 4; s.s = _s->s; for (i = 0, j = 0; i < 4; i++) { while ((j < _s->len) && (_s->s[j] != '.') && (_s->s[j] != ':')) j++; if ((i != 3) && ((j >= _s->len) || (_s->s[j] == ':'))) return -1; s.len = j - (s.s - _s->s); if (str2int(&s, &v)) return -1; if (v > 255) return -1; _ip->u.addr[i] = v; if (i < 3) { s.s = _s->s + j + 1; j++; } else { if ((j < _s->len) && (_s->s[j] == ':')) { s.s = _s->s + j + 1; s.len = _s->len - j - 1; if (s.len <= 0) return -1; if (str2int(&s, &port)) return -1; *_port = port; } else { *_port = 0; } } } return 0; } /* parses an ipv6 address with or without port number * must be in the form [address]:port if port is specified */ static int parse_ipv6(str *_s, struct ip_addr *_ip, unsigned short *_port) { char buf[IP_ADDR_MAX_STR_SIZE+1]; char *c; str s; unsigned int port; _ip->af = AF_INET6; _ip->len = 16; if (_s->s[0] == '[') { c = memchr(_s->s, ']', _s->len); if (!c) return -1; if (c - _s->s - 1 > IP_ADDR_MAX_STR_SIZE - 1) return -1; memcpy(buf, _s->s + 1, (c - _s->s - 1)*sizeof(char)); buf[c - _s->s - 1] = '\0'; if (c[1] == ':') { /* port is specified */ s.s = c+2; s.len = _s->len - (s.s - _s->s); if (s.len <= 0) return -1; if (str2int(&s, &port)) return -1; *_port = port; } else { *_port = 0; } } else { memcpy(buf, _s->s, _s->len*sizeof(char)); buf[_s->len] = '\0'; *_port = 0; } if (inet_pton(AF_INET6, buf, _ip->u.addr) <= 0) return -1; return 0; } /* parse ipv4 or ipv6 address */ int parse_ip(str *s, struct ip_addr *ip, unsigned short *port) { if (!s || !s->len) return -1; if (memchr(s->s, '.', s->len)) { /* ipv4 address */ if (parse_ipv4(s, ip, port)) { LOG(L_ERR, "ERROR: parse_ip(): failed to parse ipv4 iddress: %.*s\n", s->len, s->s); return -1; } } else { /* ipv6 address */ if (parse_ipv6(s, ip, port)) { LOG(L_ERR, "ERROR: parse_ip(): failed to parse ipv6 iddress: %.*s\n", s->len, s->s); return -1; } } return 0; } /* allocate memory for a new ipmatch entry */ static im_entry_t *new_im_entry(char *ip, char *avp_val, unsigned int mark) { im_entry_t *entry; int len; str s; if (!ip) return NULL; entry = (im_entry_t *)shm_malloc(sizeof(im_entry_t)); if (!entry) { LOG(L_ERR, "ERROR: new_im_entry(): not enough shm memory\n"); return NULL; } memset(entry, 0, sizeof(im_entry_t)); s.s = ip; s.len = strlen(ip); if (parse_ip(&s, &entry->ip, &entry->port)) { LOG(L_ERR, "ERROR: new_im_entry(): failed to parse ip iddress\n"); goto error; } if (avp_val) { len = strlen(avp_val); entry->avp_val.s = (char *)shm_malloc(len * sizeof(char)); if (!entry->avp_val.s) { LOG(L_ERR, "ERROR: new_im_entry(): not enough shm memory\n"); goto error; } memcpy(entry->avp_val.s, avp_val, len); entry->avp_val.len = len; } entry->mark = mark; /* LOG(L_DBG, "DEBUG: new_im_entry(): ip=%s, port=%u, avp_val=%.*s, mark=%u\n", ip_addr2a(&entry->ip), entry->port, entry->avp_val.len, entry->avp_val.s, entry->mark); */ return entry; error: if (entry->avp_val.s) shm_free(entry->avp_val.s); shm_free(entry); return NULL; } /* free the liked list of entries */ static void free_im_entry(im_entry_t *entry) { if (!entry) return; if (entry->next) free_im_entry(entry->next); if (entry->avp_val.s) shm_free(entry->avp_val.s); shm_free(entry); } /* maximum number of entries in the hash table */ #define IM_HASH_ENTRIES (255*4) /* hash function for ipmatch hash table * in case of ipv4: * summarizes the 4 unsigned char values * in case of ipv6: * summarizes the 1st, 5th, 9th, and 13th unsigned char values */ unsigned int im_hash(struct ip_addr *ip) { int i, j; unsigned int sum; j = ip->len / 4; sum = 0; for (i = 0; i < 4; i++) { sum += ip->u.addr[i * j]; } return sum; } /* init global IM_HASH structure */ int init_im_hash(void) { IM_HASH = (im_hash_t *)shm_malloc(sizeof(im_hash_t)); if (!IM_HASH) { LOG(L_ERR, "ERROR: init_im_hash(): not enough shm memory\n"); return -1; } IM_HASH->entries = NULL; reader_init_imhash_lock(); writer_init_imhash_lock(); return 0; } /* free memory allocated for the global cache */ void destroy_im_hash(void) { if (!IM_HASH) return; if (IM_HASH->entries) free_im_hash(IM_HASH->entries); shm_free(IM_HASH); IM_HASH = NULL; } /* create a new impatch hash table */ im_entry_t **new_im_hash(void) { im_entry_t **hash; hash = (im_entry_t **)shm_malloc(IM_HASH_ENTRIES * sizeof(im_entry_t *)); if (!hash) { LOG(L_ERR, "ERROR: new_im_hash(): not enough shm memory\n"); return NULL; } memset(hash, 0, IM_HASH_ENTRIES * sizeof(im_entry_t *)); return hash; } /* free the memory allocated for an ipmatch hash table, * and purge out entries */ void free_im_hash(im_entry_t **hash) { int i; if (!hash) return; for (i = 0; i < IM_HASH_ENTRIES; i++) { if (hash[i]) free_im_entry(hash[i]); } shm_free(hash); } /* free the memory allocated for an ipmatch hash table, * but do not purge out entries */ void delete_im_hash(im_entry_t **hash) { if (hash) shm_free(hash); } /* create a new ipmatch entry and insert it into the hash table * return value * 0: success * -1: error */ int insert_im_hash(char *ip, char *avp_val, unsigned int mark, im_entry_t **hash) { im_entry_t *entry; unsigned int i; entry = new_im_entry(ip, avp_val, mark); if (!entry) { LOG(L_ERR, "ERROR: insert_im_hash(): failed to create ipmatch entry\n"); return -1; } i = im_hash(&entry->ip); if (hash[i]) entry->next = hash[i]; hash[i] = entry; return 0; } kamailio-4.0.4/obsolete/permissions/trusted_hash.h0000644000000000000000000000477612223032460021050 0ustar rootroot/* * $Id$ * * Header file for allow_trusted hash table functions * * Copyright (C) 2003 Juha Heinanen * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _TRUSTED_HASH_H #define _TRUSTED_HASH_H #include #include "../../parser/msg_parser.h" #include "../../rpc.h" #include "../../str.h" #define HASH_SIZE 128 /* * Structure stored in the hash table */ struct trusted_list { str src_ip; /* Source IP of SIP message */ int proto; /* Protocol -- UDP, TCP, TLS, or SCTP */ char *pattern; /* Pattern matching From header field */ struct trusted_list *next; /* Next element in the list */ }; /* * Create and initialize a hash table */ struct trusted_list** new_hash_table(void); /* * Release all memory allocated for a hash table */ void free_hash_table(struct trusted_list** table); /* * Destroy a hash table */ void destroy_hash_table(struct trusted_list** table); /* * Add into hash table, where proto is integer * representation of string argument proto. */ int hash_table_insert(struct trusted_list** hash_table, char* src_ip, char* proto, char* pattern); /* * Check if an entry exists in hash table that has given src_ip and protocol * value and pattern that matches to From URI. */ int match_hash_table(struct trusted_list** table, struct sip_msg* msg); /* * Print domains stored in hash table */ void hash_table_print(struct trusted_list** hash_table, rpc_t* rpc, void* ctx); /* * Empty hash table */ void empty_hash_table(struct trusted_list** hash_table); #endif /* _TRUSTED_HASH_H */ kamailio-4.0.4/obsolete/permissions/allow_files.c0000644000000000000000000004556612223032460020650 0ustar rootroot/* * $Id$ * * PERMISSIONS module * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2006-08-10: file operation functions are moved here (Miklos) */ #include "../../mem/mem.h" #include "../../parser/parse_from.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_refer_to.h" #include "../../parser/contact/parse_contact.h" #include "../../str.h" #include "../../dset.h" #include "../../globals.h" #include "rule.h" #include "permissions.h" #include "parse_config.h" #include "allow_files.h" /* * Extract path (the beginning of the string * up to the last / character * Returns length of the path */ static int get_path(char* pathname) { char* c; if (!pathname) return 0; c = strrchr(pathname, '/'); if (!c) return 0; return c - pathname + 1; } /* * Prepend path if necessary */ static char* get_pathname(char* name) { char* buffer; int path_len, name_len; if (!name) return 0; name_len = strlen(name); if (strchr(name, '/')) { buffer = (char*)pkg_malloc(name_len + 1); if (!buffer) goto err; strcpy(buffer, name); return buffer; } else { path_len = get_path(cfg_file); buffer = (char*)pkg_malloc(path_len + name_len + 1); if (!buffer) goto err; memcpy(buffer, cfg_file, path_len); memcpy(buffer + path_len, name, name_len); buffer[path_len + name_len] = '\0'; return buffer; } err: LOG(L_ERR, "get_pathname(): No memory left\n"); return 0; } /* * If the file pathname has been parsed already then the * function returns its index in the tables, otherwise it * returns -1 to indicate that the file needs to be read * and parsed yet */ static int find_index(rule_file_t* array, int num, char* pathname) { int i; for(i = 0; i <= num; i++) { if (array[i].filename && !strcmp(pathname, array[i].filename)) return i; } return -1; } /* check if the file container array has been already created */ static int check_file_container(rule_file_t **_table) { if (!*_table) { *_table = (rule_file_t *)pkg_malloc(sizeof(rule_file_t) * max_rule_files); if (!*_table) { LOG(L_ERR, "ERROR: check_file_container(): not enough memory\n"); return -1; } memset(*_table, 0, sizeof(rule_file_t) * max_rule_files); } return 0; } /* * loads a config file into the array * return value: * <0: error * >=0: index of the file * * _def must be true in case of the default file which * sets the index to 0 */ int load_file(char *_name, rule_file_t **_table, int *_rules_num, int _def) { char *pathname; int idx; rule_file_t *table; int err; if (check_file_container(_table)) return -1; table = *_table; pathname = get_pathname(_name); if (!pathname) return -1; if (_def) { /* default file, we must use the 0 index */ idx = 0; } else { idx = find_index(table, *_rules_num, pathname); } if (idx == -1) { /* Not opened yet, open the file and parse it */ idx = *_rules_num + 1; /* skip the 0 index */ if (idx >= max_rule_files) { LOG(L_ERR, "ERROR: load_files(): array is too small to open the file,"\ " increase max_rule_files module parameter!\n"); pkg_free(pathname); return -1; } table[idx].filename = pathname; table[idx].rules = parse_config_file(pathname, &err); if (err) return -1; if (table[idx].rules) { LOG(L_INFO, "load_files(): File (%s) parsed\n", pathname); } else { LOG(L_WARN, "load_files(): File (%s) not found or empty => empty rule set\n", pathname); } LOG(L_DBG, "load_files(): filename:%s => idx:%d\n", pathname, idx); (*_rules_num)++; return idx; } else if (idx == 0) { /* default file, use index 0 and do not increase _rules_num */ if (table[0].rules) { LOG(L_INFO, "load_files(): File (%s) already loaded, re-using\n", pathname); LOG(L_DBG, "load_files(): filename:%s => idx:%d\n", pathname, idx); pkg_free(pathname); return 0; } table[0].filename = pathname; table[0].rules = parse_config_file(pathname, &err); if (err) return -1; if (table[0].rules) { LOG(L_INFO, "load_files(): File (%s) parsed\n", pathname); } else { LOG(L_WARN, "load_files(): File (%s) not found or empty => empty rule set\n", pathname); } LOG(L_DBG, "load_files(): filename:%s => idx:%d\n", pathname, idx); return 0; } else { /* File already parsed, re-use it */ LOG(L_INFO, "load_files(): File (%s) already loaded, re-using\n", pathname); LOG(L_DBG, "load_files(): filename:%s => idx:%d\n", pathname, idx); pkg_free(pathname); return idx; } } /* free memory allocated for the file container */ void delete_files(rule_file_t **_table, int _num) { int i; rule_file_t *table; if (!*_table) return; table = *_table; for(i = 0; i <= _num; i++) { if (table[i].rules) free_rule(table[i].rules); if (table[i].filename) pkg_free(table[i].filename); } pkg_free(*_table); *_table = NULL; } /* * Return URI without all the bells and whistles, that means only * sip:username@domain, resulting buffer is statically allocated and * zero terminated */ static char* get_plain_uri(const str* uri) { static char buffer[EXPRESSION_LENGTH + 1]; struct sip_uri puri; int len; if (!uri) return 0; if (parse_uri(uri->s, uri->len, &puri) < 0) { LOG(L_ERR, "get_plain_uri(): Error while parsing URI\n"); return 0; } if (puri.user.len) { len = puri.user.len + puri.host.len + 5; } else { len = puri.host.len + 4; } if (len > EXPRESSION_LENGTH) { LOG(L_ERR, "allow_register(): (module permissions) Request-URI is too long: %d chars\n", len); return 0; } strcpy(buffer, "sip:"); if (puri.user.len) { memcpy(buffer + 4, puri.user.s, puri.user.len); buffer[puri.user.len + 4] = '@'; memcpy(buffer + puri.user.len + 5, puri.host.s, puri.host.len); } else { memcpy(buffer + 4, puri.host.s, puri.host.len); } buffer[len] = '\0'; return buffer; } /* * determines the permission of the call * return values: * -1: deny * 1: allow */ int check_routing(struct sip_msg* msg, int idx) { struct hdr_field *from; int len, q; static char from_str[EXPRESSION_LENGTH+1]; static char ruri_str[EXPRESSION_LENGTH+1]; char* uri_str; str branch; /* turn off control, allow any routing */ if (!allow || !deny || ((!allow[idx].rules) && (!deny[idx].rules))) { DBG("check_routing(): No rules => allow any routing\n"); return 1; } /* looking for FROM HF */ if ((!msg->from) && (parse_headers(msg, HDR_FROM_F, 0) == -1)) { LOG(L_ERR, "check_routing(): Error while parsing message\n"); return -1; } if (!msg->from) { LOG(L_ERR, "check_routing(): FROM header field not found\n"); return -1; } /* we must call parse_from_header explicitly */ if ((!(msg->from)->parsed) && (parse_from_header(msg) < 0)) { LOG(L_ERR, "check_routing(): Error while parsing From body\n"); return -1; } from = msg->from; len = ((struct to_body*)from->parsed)->uri.len; if (len > EXPRESSION_LENGTH) { LOG(L_ERR, "check_routing(): From header field is too long: %d chars\n", len); return -1; } strncpy(from_str, ((struct to_body*)from->parsed)->uri.s, len); from_str[len] = '\0'; /* looking for request URI */ if (parse_sip_msg_uri(msg) < 0) { LOG(L_ERR, "check_routing(): uri parsing failed\n"); return -1; } len = msg->parsed_uri.user.len + msg->parsed_uri.host.len + 5; if (len > EXPRESSION_LENGTH) { LOG(L_ERR, "check_routing(): Request URI is too long: %d chars\n", len); return -1; } strcpy(ruri_str, "sip:"); memcpy(ruri_str + 4, msg->parsed_uri.user.s, msg->parsed_uri.user.len); ruri_str[msg->parsed_uri.user.len + 4] = '@'; memcpy(ruri_str + msg->parsed_uri.user.len + 5, msg->parsed_uri.host.s, msg->parsed_uri.host.len); ruri_str[len] = '\0'; DBG("check_routing(): looking for From: %s Request-URI: %s\n", from_str, ruri_str); /* rule exists in allow file */ if (search_rule(allow[idx].rules, from_str, ruri_str)) { if (check_all_branches) goto check_branches; DBG("check_routing(): allow rule found => routing is allowed\n"); return 1; } /* rule exists in deny file */ if (search_rule(deny[idx].rules, from_str, ruri_str)) { DBG("check_routing(): deny rule found => routing is denied\n"); return -1; } if (!check_all_branches) { DBG("check_routing(): Neither allow nor deny rule found => routing is allowed\n"); return 1; } check_branches: init_branch_iterator(); while((branch.s = next_branch(&branch.len, &q, 0, 0, 0, 0))) { uri_str = get_plain_uri(&branch); if (!uri_str) { LOG(L_ERR, "check_uri(): Error while extracting plain URI\n"); return -1; } DBG("check_routing: Looking for From: %s Branch: %s\n", from_str, uri_str); if (search_rule(allow[idx].rules, from_str, uri_str)) { continue; } if (search_rule(deny[idx].rules, from_str, uri_str)) { LOG(L_INFO, "check_routing(): Deny rule found for one of branches => routing is denied\n"); return -1; } } LOG(L_INFO, "check_routing(): Check of branches passed => routing is allowed\n"); return 1; } /* * Test of REGISTER messages. Creates To-Contact pairs and compares them * against rules in allow and deny files passed as parameters. The function * iterates over all Contacts and creates a pair with To for each contact * found. That allows to restrict what IPs may be used in registrations, for * example */ int check_register(struct sip_msg* msg, int idx) { int len; static char to_str[EXPRESSION_LENGTH + 1]; char* contact_str; contact_t* c; /* turn off control, allow any routing */ if (!allow || !deny || ((!allow[idx].rules) && (!deny[idx].rules))) { DBG("check_register(): No rules => allow any registration\n"); return 1; } /* * Note: We do not parse the whole header field here although the message can * contain multiple Contact header fields. We try contacts one by one and if one * of them causes reject then we don't look at others, this could improve performance * a little bit in some situations */ if (parse_headers(msg, HDR_TO_F | HDR_CONTACT_F, 0) == -1) { LOG(L_ERR, "check_register(): Error while parsing headers\n"); return -1; } if (!msg->to) { LOG(L_ERR, "check_register(): To or Contact not found\n"); return -1; } if (!msg->contact) { /* REGISTER messages that contain no Contact header field * are allowed. Such messages do not modify the contents of * the user location database anyway and thus are not harmful */ DBG("check_register(): No Contact found, allowing\n"); return 1; } /* Check if the REGISTER message contains start Contact and if * so then allow it */ if (parse_contact(msg->contact) < 0) { LOG(L_ERR, "check_register(): Error while parsing Contact HF\n"); return -1; } if (((contact_body_t*)msg->contact->parsed)->star) { DBG("check_register(): * Contact found, allowing\n"); return 1; } len = ((struct to_body*)msg->to->parsed)->uri.len; if (len > EXPRESSION_LENGTH) { LOG(L_ERR, "check_register(): To header field is too long: %d chars\n", len); return -1; } strncpy(to_str, ((struct to_body*)msg->to->parsed)->uri.s, len); to_str[len] = '\0'; if (contact_iterator(&c, msg, 0) < 0) { return -1; } while(c) { contact_str = get_plain_uri(&c->uri); if (!contact_str) { LOG(L_ERR, "check_register(): Can't extract plain Contact URI\n"); return -1; } DBG("check_register(): Looking for To: %s Contact: %s\n", to_str, contact_str); /* rule exists in allow file */ if (search_rule(allow[idx].rules, to_str, contact_str)) { if (check_all_branches) goto skip_deny; } /* rule exists in deny file */ if (search_rule(deny[idx].rules, to_str, contact_str)) { DBG("check_register(): Deny rule found => Register denied\n"); return -1; } skip_deny: if (contact_iterator(&c, msg, c) < 0) { return -1; } } DBG("check_register(): No contact denied => Allowed\n"); return 1; } /* * determines the permission to refer to given refer-to uri * return values: * -1: deny * 1: allow */ int check_refer_to(struct sip_msg* msg, int idx) { struct hdr_field *from, *refer_to; int len; static char from_str[EXPRESSION_LENGTH+1]; static char refer_to_str[EXPRESSION_LENGTH+1]; /* turn off control, allow any refer */ if (!allow || !deny || ((!allow[idx].rules) && (!deny[idx].rules))) { DBG("check_refer_to(): No rules => allow any refer\n"); return 1; } /* looking for FROM HF */ if ((!msg->from) && (parse_headers(msg, HDR_FROM_F, 0) == -1)) { LOG(L_ERR, "check_refer_to(): Error while parsing message\n"); return -1; } if (!msg->from) { LOG(L_ERR, "check_refer_to(): FROM header field not found\n"); return -1; } /* we must call parse_from_header explicitly */ if ((!(msg->from)->parsed) && (parse_from_header(msg) < 0)) { LOG(L_ERR, "check_refer_to(): Error while parsing From body\n"); return -1; } from = msg->from; len = ((struct to_body*)from->parsed)->uri.len; if (len > EXPRESSION_LENGTH) { LOG(L_ERR, "check_refer_to(): From header field is too long: %d chars\n", len); return -1; } strncpy(from_str, ((struct to_body*)from->parsed)->uri.s, len); from_str[len] = '\0'; /* looking for REFER-TO HF */ if ((!msg->refer_to) && (parse_headers(msg, HDR_REFER_TO_F, 0) == -1)){ LOG(L_ERR, "check_refer_to(): Error while parsing message\n"); return -1; } if (!msg->refer_to) { LOG(L_ERR, "check_refer_to(): Refer-To header field not found\n"); return -1; } /* we must call parse_refer_to_header explicitly */ if ((!(msg->refer_to)->parsed) && (parse_refer_to_header(msg) < 0)) { LOG(L_ERR, "check_refer_to(): Error while parsing Refer-To body\n"); return -1; } refer_to = msg->refer_to; len = ((struct to_body*)refer_to->parsed)->uri.len; if (len > EXPRESSION_LENGTH) { LOG(L_ERR, "check_refer_to(): Refer-To header field is too long: %d chars\n", len); return -1; } strncpy(refer_to_str, ((struct to_body*)refer_to->parsed)->uri.s, len); refer_to_str[len] = '\0'; DBG("check_refer_to(): looking for From: %s Refer-To: %s\n", from_str, refer_to_str); /* rule exists in allow file */ if (search_rule(allow[idx].rules, from_str, refer_to_str)) { DBG("check_refer_to(): allow rule found => refer is allowed\n"); return 1; } /* rule exists in deny file */ if (search_rule(deny[idx].rules, from_str, refer_to_str)) { DBG("check_refer_to(): deny rule found => refer is denied\n"); return -1; } DBG("check_refer_to(): Neither allow nor deny rule found => refer_to is allowed\n"); return 1; } kamailio-4.0.4/obsolete/permissions/parse_config.c0000644000000000000000000001573012223032460020775 0ustar rootroot/* * $Id$ * * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * 2006-08-10: parse_config_file() can return error value (Miklos) */ #include #include #include "../../sr_module.h" #include "rule.h" #include "permissions.h" #include "parse_config.h" /* * parse a comma separated expression list like a, b, c * return 0 on success, -1 on error * parsed expressions are returned in **e */ static int parse_expression_list(char *str, expression **e) { int start=0, i=-1, j=-1, apost=0; char str2[EXPRESSION_LENGTH]; expression *e1=NULL, *e2; if (!str || !e) return -1; *e = NULL; do { i++; switch(str[i]) { case '"': apost = !apost; break; case ',': if (apost) break; case '\0': /* word found */ while ((str[start] == ' ') || (str[start] == '\t')) start++; if (str[start] == '"') start++; j = i-1; while ((0 < j) && ((str[j] == ' ') || (str[j] == '\t'))) j--; if ((0 < j) && (str[j] == '"')) j--; if (start<=j) { /* valid word */ if (j-start+1 >= EXPRESSION_LENGTH) { /* error */ LOG(L_ERR, "ERROR: parse_expression_list(): " \ "too long expression, increase EXPRESSION_LENGTH\n"); goto error; } strncpy(str2, str+start, j-start+1); str2[j-start+1] = '\0'; e2 = new_expression(str2); if (!e2) { /* memory error */ LOG(L_ERR, "ERROR: parse_expression_list(): " \ "not enough memory\n"); goto error; } if (e1) { /* it is not the first */ e1->next = e2; e1 = e2; } else { /* it is the first */ *e = e1 = e2; } } else { /* parsing error */ LOG(L_ERR, "ERROR: parse_expression_list(): " \ "expression parsing error\n"); goto error; } /* for the next word */ start = i+1; } } while (str[i] != '\0'); return 0; error: if (*e) { free_expression(*e); *e = NULL; } return -1; } /* * parse a complex expression list like a, b, c EXCEPT d, e * return 0 on success, -1 on error * parsed expressions are returned in **e, and exceptions are returned in **e_exceptions */ static int parse_expression(char *str, expression **e, expression **e_exceptions) { char *except, str2[LINE_LENGTH]; int i=0, l; if (!str || !e || !e_exceptions) return -1; except = strstr(str, " EXCEPT "); if (except) { /* exception found */ l = except-str; if (l >= LINE_LENGTH) { /* error */ LOG(L_ERR, "ERROR: parse_expression(): too long config line, increase LINE_LENGTH\n"); goto error; } strncpy(str2, str, l); str2[l] = '\0'; /* except+8 points to the exception */ if (parse_expression_list(except+8, e_exceptions)) { /* error */ goto error; } } else { /* no exception */ l = strlen(str); if (l >= LINE_LENGTH) { /* error */ LOG(L_ERR, "ERROR: parse_expression(): too long config line, increase LINE_LENGTH\n"); goto error; } strncpy(str2, str, l); str2[l] = '\0'; *e_exceptions = NULL; } while ((str2[i] == ' ') || (str2[i] == '\t')) i++; if (strncmp("ALL", str2+i, 3) == 0) { *e = NULL; } else { if (parse_expression_list(str2+i, e)) { /* error */ if (*e_exceptions) free_expression(*e_exceptions); goto error; } } return 0; error: *e = *e_exceptions = NULL; return -1; } /* * parse one line of the config file * return the rule according to line */ static rule *parse_config_line(char *line, int *err) { rule *rule1; expression *left, *left_exceptions, *right, *right_exceptions; int i=-1, exit=0, apost=0, colon=-1, eval=0; static char str1[LINE_LENGTH], str2[LINE_LENGTH+1]; *err = 0; if (!line) return 0; rule1 = 0; left = left_exceptions = right = right_exceptions = 0; while (!exit) { i++; switch(line[i]) { case '"': apost = !apost; eval = 1; break; case ':': if (!apost) colon = i; eval = 1; break; case '#': if (apost) break; case '\0': case '\n': exit = 1; break; case ' ': break; case '\t': break; default: eval = 1; } } if (eval) { if ((0left = left; rule1->left_exceptions = left_exceptions; rule1->right = right; rule1->right_exceptions = right_exceptions; return rule1; } else { /* error */ LOG(L_ERR, "ERROR parsing line: %s\n", line); goto error; } } return 0; error: if (left) free_expression(left); if (left_exceptions) free_expression(left_exceptions); if (right) free_expression(right); if (right_exceptions) free_expression(right_exceptions); *err = 1; return 0; } /* * parse a config file * return a list of rules */ rule *parse_config_file(char *filename, int *err) { FILE *file; char line[LINE_LENGTH+1]; rule *start_rule = NULL, *rule1 = NULL, *rule2 = NULL; *err = 0; file = fopen(filename,"r"); if (!file) { if (safe_file_load) { LOG(L_ERR, "ERROR: File not found: %s\n", filename); *err = 1; } else { LOG(L_WARN, "WARNING: File not found: %s\n", filename); } return NULL; } while (fgets(line, LINE_LENGTH, file)) { rule2 = parse_config_line(line, err); if (*err) goto error; if (rule2) { if (rule1) { /* it is not the first rule */ rule1->next = rule2; } else { /* it is the first rule */ start_rule = rule2; } rule1 = rule2; } } error: fclose(file); return start_rule; /* returns the linked list */ } kamailio-4.0.4/obsolete/permissions/im_hash.h0000644000000000000000000000523012223032460017745 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _IM_HASH_H #define _IM_HASH_H #include "../../ip_addr.h" #include "../../locking.h" /* linked list of entries */ typedef struct im_entry { struct ip_addr ip; unsigned short port; str avp_val; unsigned int mark; struct im_entry *next; } im_entry_t; /* hash table for the entries */ typedef struct im_hash { im_entry_t **entries; gen_lock_t read_lock; /* lock for reader_count variable */ gen_lock_t write_lock; /* lock for writer processes */ int reader_count; /* number of reader processes */ int writer_demand; /* writer processes set this flag */ } im_hash_t; /* global variable for DB cache */ extern im_hash_t *IM_HASH; /* parse ipv4 or ipv6 address */ int parse_ip(str *s, struct ip_addr *ip, unsigned short *port); /* hash function for ipmatch hash table * in case of ipv4: * summarizes the 4 unsigned char values * in case of ipv6: * summarizes the 1st, 5th, 9th, and 13th unsigned char values */ unsigned int im_hash(struct ip_addr *ip); /* init global IM_HASH structure */ int init_im_hash(void); /* free memory allocated for the global cache */ void destroy_im_hash(void); /* create a new impatch hash table */ im_entry_t **new_im_hash(void); /* free the memory allocated for an ipmatch hash table, * and purge out entries */ void free_im_hash(im_entry_t **hash); /* free the memory allocated for an ipmatch hash table, * but do not purge out entries */ void delete_im_hash(im_entry_t **hash); /* create a new ipmatch entry and insert it into the hash table * return value * 0: success * -1: error */ int insert_im_hash(char *ip, char *avp_val, unsigned int mark, im_entry_t **hash); #endif /* _IM_HASH_H */ kamailio-4.0.4/obsolete/permissions/allow_files.h0000644000000000000000000000432512223032460020641 0ustar rootroot/* * $Id$ * * PERMISSIONS module * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2006-08-10: file operation functions are moved here (Miklos) */ #ifndef _ALLOW_FILES_H #define _ALLOW_FILES_H 1 /* * loads a config file into the array * return value: * <0: error * >=0: index of the file * * _def must be true in case of the default file which * sets the index to 0 */ int load_file(char *_name, rule_file_t **_table, int *_rules_num, int _def); /* free memory allocated for the file container */ void delete_files(rule_file_t **_table, int _num); /* * determines the permission of the call * return values: * -1: deny * 1: allow */ int check_routing(struct sip_msg* msg, int idx); /* * Test of REGISTER messages. Creates To-Contact pairs and compares them * against rules in allow and deny files passed as parameters. The function * iterates over all Contacts and creates a pair with To for each contact * found. That allows to restrict what IPs may be used in registrations, for * example */ int check_register(struct sip_msg* msg, int idx); /* * determines the permission to refer to given refer-to uri * return values: * -1: deny * 1: allow */ int check_refer_to(struct sip_msg* msg, int idx); #endif /* _ALLOW_FILES_H */ kamailio-4.0.4/obsolete/permissions/permissions.h0000644000000000000000000000451012223032460020710 0ustar rootroot/* * $Id$ * * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* * History: * -------- * 2003-09-03 replaced /usr/local/et/ser/ with CFG_DIR (andrei) */ #ifndef PERMISSIONS_H #define PERMISSIONS_H 1 #include "../../sr_module.h" #include "../../lib/srdb2/db.h" #include "rule.h" #define DEFAULT_ALLOW_FILE "permissions.allow" #define DEFAULT_DENY_FILE "permissions.deny" typedef struct rule_file { rule* rules; /* Parsed rule set */ char* filename; /* The name of the file */ } rule_file_t; /* * Maximum number if allow/deny file pairs that can be opened * at any time */ #define MAX_RULE_FILES 64 extern rule_file_t *allow; /* Parsed allow files */ extern rule_file_t *deny; /* Parsed deny files */ extern int max_rule_files; extern int check_all_branches; extern int safe_file_load; extern char* db_url; /* Database URL */ extern int db_mode; /* Database usage mode: 0=no cache, 1=cache */ extern char* trusted_table; /* Name of trusted table */ extern char* source_col; /* Name of source address column */ extern char* proto_col; /* Name of protocol column */ extern char* from_col; /* Name of from pattern column */ extern char* ipmatch_table; /* Name of trusted table */ /* Database API */ extern db_ctx_t *db_conn; #define DISABLE_CACHE 0 #define ENABLE_CACHE 1 #endif kamailio-4.0.4/obsolete/permissions/parse_config.h0000644000000000000000000000235712223032460021003 0ustar rootroot/* * $Id$ * * PERMISSIONS module * * Copyright (C) 2003 Miklós Tirpák (mtirpak@sztaki.hu) * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef PARSE_CONFIG_H #define PARSE_CONFIG_H 1 #include "rule.h" rule *parse_config_file(char *filename, int *err); #endif /* PARSE_CONFIG_H */ kamailio-4.0.4/obsolete/permissions/ipmatch.c0000644000000000000000000001623012223032460017757 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "../../onsend.h" #include "../../parser/parse_fline.h" #include "../../ut.h" #include "permissions.h" #include "im_hash.h" #include "im_db.h" #include "im_locks.h" #include "ipmatch.h" /* initialize ipmatch table */ int init_ipmatch(void) { if (db_mode != ENABLE_CACHE) { /* not an error, but ipmatch functions will not operate */ LOG(L_WARN, "WARNING: ipmatch_init(): Database cache is disabled, thus ipmatch functions cannot be used\n"); return 0; } if (init_im_hash()) { LOG(L_ERR, "ERROR: ipmatch_init(): cannot init ipmatch hash table\n"); return -1; } if (reload_im_cache()) { LOG(L_ERR, "ERROR: ipmatch_init(): error occured while caching ipmatch table\n"); return -1; } return 0; } /* destroy function */ void clean_ipmatch(void) { LOG(L_DBG, "DEBUG: clean_ipmatch(): free shared memory required by ipmatch table\n"); destroy_im_hash(); } /* process variable to filter on entry->mark */ static unsigned int IM_FILTER = ~0; /* tries to find the given IP address and port number in the global hash table * return value * 1: found * 0: not found * -1: error */ static int ipmatch(struct ip_addr *ip, unsigned short port, avp_ident_t *avp) { im_entry_t *entry; int ret; avp_value_t avp_val; ret = 0; if (!IM_HASH) { LOG(L_CRIT, "ERROR: ipmatch(): ipmatch hash table is not initialied. " "Have you set the database url?\n"); return 0; } /* lock hash table for reading */ reader_lock_imhash(); LOG(L_DBG, "DEBUG: ipmatch(): start searching... (ip=%s, port=%u, filter=%u)\n", ip_addr2a(ip), port, IM_FILTER); if (!IM_HASH->entries) { LOG(L_DBG, "DEBUG: ipmatch(): cache is empty\n"); goto done; } entry = IM_HASH->entries[im_hash(ip)]; while (entry) { if ((entry->mark & IM_FILTER) && ((entry->port == 0) || (port == 0) || (entry->port == port)) && (ip_addr_cmp(&entry->ip, ip))) { LOG(L_DBG, "DEBUG: ipmatch(): entry found\n"); /* shall we set the AVP? */ if (avp) { /* delete AVP before inserting */ delete_avp(avp->flags, avp->name); avp_val.s.s = entry->avp_val.s; avp_val.s.len = entry->avp_val.len; if (add_avp(avp->flags | AVP_VAL_STR, avp->name, avp_val)) { LOG(L_ERR, "ERROR: ipmatch(): failed to add AVP\n"); ret = -1; break; } } ret = 1; break; } entry = entry->next; } if (!entry) LOG(L_DBG, "DEBUG: ipmatch(): entry not found\n"); done: /* release hash table */ reader_release_imhash(); /* reset filter */ IM_FILTER = ~0; return ret; } /* wrapper function for ipmatch */ int ipmatch_2(struct sip_msg *msg, char *str1, char *str2) { int ret; fparam_t *param1; str s; struct ip_addr *ip, ip_buf; unsigned short port; unsigned int iport; param1 = (fparam_t *)str1; switch(param1->type) { case FPARAM_AVP: case FPARAM_SELECT: if (get_str_fparam(&s, msg, param1)) { LOG(L_ERR, "ERROR: w_ipmatch_2(): could not get first parameter\n"); return -1; } if (parse_ip(&s, &ip_buf, &port)) { LOG(L_ERR, "ERROR: w_ipmatch_2(): could not parse ip address\n"); return -1; } ip = &ip_buf; break; case FPARAM_STR: if (param1->v.str.s[0] == 's') { /* "src" */ ip = &msg->rcv.src_ip; port = msg->rcv.src_port; } else { /* "via2" */ if (!msg->via2 && ((parse_headers(msg, HDR_VIA2_F, 0) == -1) || !msg->via2)) { LOG(L_ERR, "ERROR: w_ipmatch_2(): could not get 2nd VIA HF\n"); return -1; } if (!msg->via2->received || !msg->via2->received->value.s) { LOG(L_ERR, "ERROR: w_ipmatch_2(): there is no received param in the 2nd VIA HF\n"); return -1; } if (parse_ip(&msg->via2->received->value, &ip_buf, &port)) { LOG(L_ERR, "ERROR: w_ipmatch_2(): could not parse ip address\n"); return -1; } ip = &ip_buf; if (!msg->via2->rport || !msg->via2->rport->value.s) { LOG(L_WARN, "WARNING: w_ipmatch_2(): there is no rport param in the 2nd VIA HF\n"); port = 0; } else { if (str2int(&msg->via2->rport->value, &iport)) { LOG(L_ERR, "ERROR: w_ipmatch_2(): invalid port number %.*s\n", msg->via2->rport->value.len, msg->via2->rport->value.s); return -1; } port = iport; } } break; default: LOG(L_ERR, "ERROR: w_ipmatch_2(): unknown parameter type\n"); return -1; } ret = ipmatch( ip, port, (str2) ? &((fparam_t *)str2)->v.avp : 0 ); return (ret == 0) ? -1 : 1; } /* wrapper function for ipmatch */ int ipmatch_1(struct sip_msg *msg, char *str1, char *str2) { return ipmatch_2(msg, str1, 0); } /* wrapper function for ipmatch */ int ipmatch_onsend(struct sip_msg *msg, char *str1, char *str2) { int ret; struct ip_addr ip; unsigned short port; char *buf, *ch1, *ch2; struct msg_start fl; str *uri, s; if (str1[0] == 'd') { /* get info from destination address */ port = su_getport(get_onsend_info()->to); su2ip_addr(&ip, get_onsend_info()->to); } else { /* get info from Request URI we need to parse the first line again because the parsed uri can be changed by another branch */ /* use another buffer pointer, because parse_first_list() modifies it */ buf = get_onsend_info()->buf; parse_first_line(buf, get_onsend_info()->len, &fl); if (fl.type != SIP_REQUEST) { LOG(L_ERR, "ERROR: w_ipmatch_onsend(): message type is not request\n"); return -1; } uri = &(fl.u.request.uri); /* find the host:port part in the uri */ if ((!(ch1 = memchr(uri->s, '@', uri->len))) && (!(ch1 = memchr(uri->s, ':', uri->len)))) { LOG(L_ERR, "ERROR: w_ipmatch_onsend(): unable to get host:port part of uri: %.*s\n", uri->len, uri->s); return -1; } /* is there a parameter in the uri? */ if ((ch2 = memchr(uri->s, ';', uri->len))) { s.s = ch1 + 1; s.len = ch2 - ch1 - 1; } else { s.s = ch1 + 1; s.len = uri->len - (ch1 - uri->s) - 1; } if (parse_ip(&s, &ip, &port)) { LOG(L_ERR, "ERROR: w_ipmatch_onsend(): could not parse ip address\n"); return -1; } } ret = ipmatch( &ip, port, 0 /* AVP operations are unsafe in onsend_route! */ ); return (ret == 0) ? -1 : 1; } /* set IM_FILTER */ int ipmatch_filter(struct sip_msg *msg, char *str1, char *str2) { int i; if (get_int_fparam(&i, msg, (fparam_t *)str1)) return -1; IM_FILTER = (unsigned int)i; return 1; } kamailio-4.0.4/obsolete/permissions/Makefile0000644000000000000000000000045412223032460017627 0ustar rootroot# $Id$ # # PERMISSIONS module makefile # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=permissions.so LIBS= DEFS+=-DSER_MOD_INTERFACE SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/permissions/im_rpc.h0000644000000000000000000000232612223032460017611 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _IM_RPC_H #define _IM_RPC_H #include "../../rpc.h" extern const char* im_reload_doc[]; /* XML RPC function to reload impatch cache */ void im_reload(rpc_t *rpc, void *c); #endif /* _IM_RPC_H */ kamailio-4.0.4/obsolete/rr/0000755000000000000000000000000012223032460014234 5ustar rootrootkamailio-4.0.4/obsolete/rr/record.c0000644000000000000000000002566012223032460015667 0ustar rootroot/* * $Id$ * * Route & Record-Route module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-04-04 Extracted from common.[ch] (janakj) */ #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../str.h" #include "../../data_lump.h" #include "record.h" #include "rr_mod.h" #include "avp_cookie.h" #include "fix_lumps.h" #define RR_PREFIX "Record-Route: " #define RR_LR_TERM_LEN (sizeof(RR_LR_TERM)-1) #define RR_LR_FULL_TERM ";lr=on>" #define RR_LR_FULL_TERM_LEN (sizeof(RR_LR_FULL_TERM)-1) #define RR_SR_TERM ">" #define RR_SR_TERM_LEN (sizeof(RR_SR_TERM)-1) #define RR_FROMTAG ";ftag=" #define RR_FROMTAG_LEN (sizeof(RR_FROMTAG)-1) #define RR_AVPCOOKIETAG ";avp=" #define RR_AVPCOOKIETAG_LEN (sizeof(RR_AVPCOOKIETAG)-1) #define RR_R2 ";r2=on" #define RR_R2_LEN (sizeof(RR_R2)-1) #define INBOUND 1 /* Insert inbound Record-Route */ #define OUTBOUND 0 /* Insert outbound Record-Route */ /* * Extract username from the Request URI * First try to look at the original Request URI and if there * is no username use the new Request URI */ static inline int get_username(struct sip_msg* _m, str* _user) { struct sip_uri puri; /* first try to look at r-uri for a username */ if (parse_uri(_m->first_line.u.request.uri.s, _m->first_line.u.request.uri.len, &puri) < 0) { LOG(L_ERR, "get_username(): Error while parsing R-URI\n"); return -1; } /* no username in original uri -- hmm; maybe it is a uri * with just host address and username is in a preloaded route, * which is now no rewritten r-uri (assumed rewriteFromRoute * was called somewhere in script's beginning) */ if (!puri.user.len && _m->new_uri.s) { if (parse_uri(_m->new_uri.s, _m->new_uri.len, &puri) < 0) { LOG(L_ERR, "get_username(): Error while parsing new_uri\n"); return -2; } } _user->s = puri.user.s; _user->len = puri.user.len; return 0; } /* * build a Record-Route header field */ static inline int build_rr(struct lump* _l, struct lump* _l2, int _lr, str* user, str* tag, str* avp_cookie, int _inbound) { char* prefix, *suffix, *crlf, *r2; int suffix_len, prefix_len, suffix_pos; prefix_len = RR_PREFIX_LEN + (user->len ? (user->len + 1) : 0); prefix = pkg_malloc(prefix_len); if (enable_full_lr) { suffix_len = (_lr ? RR_LR_FULL_TERM_LEN : RR_SR_TERM_LEN); } else { suffix_len = (_lr ? RR_LR_TERM_LEN : RR_SR_TERM_LEN); } suffix_len += ((tag && tag->len) ? (RR_FROMTAG_LEN + tag->len) : 0); suffix_len += ((avp_cookie && avp_cookie->len) ? (RR_AVPCOOKIETAG_LEN + avp_cookie->len) : 0); suffix = pkg_malloc(suffix_len); crlf = pkg_malloc(2); r2 = pkg_malloc(RR_R2_LEN); if (!prefix || !suffix || !crlf || !r2) { LOG(L_ERR, "build_rr(): No memory left\n"); if (suffix) pkg_free(suffix); if (prefix) pkg_free(prefix); if (crlf) pkg_free(crlf); if (r2) pkg_free(r2); return -3; } memcpy(prefix, RR_PREFIX, RR_PREFIX_LEN); if (user->len) { memcpy(prefix + RR_PREFIX_LEN, user->s, user->len); prefix[RR_PREFIX_LEN + user->len] = '@'; } suffix_pos = 0; if (tag && tag->len) { memcpy(suffix + suffix_pos, RR_FROMTAG, RR_FROMTAG_LEN); memcpy(suffix + suffix_pos + RR_FROMTAG_LEN, tag->s, tag->len); suffix_pos += RR_FROMTAG_LEN + tag->len; } if (avp_cookie && avp_cookie->len) { memcpy(suffix + suffix_pos, RR_AVPCOOKIETAG, RR_AVPCOOKIETAG_LEN); memcpy(suffix + suffix_pos + RR_AVPCOOKIETAG_LEN, avp_cookie->s, avp_cookie->len); suffix_pos += RR_AVPCOOKIETAG_LEN + avp_cookie->len; } if (enable_full_lr) { memcpy(suffix + suffix_pos, _lr ? RR_LR_FULL_TERM : RR_SR_TERM, _lr ? RR_LR_FULL_TERM_LEN : RR_SR_TERM_LEN); } else { memcpy(suffix + suffix_pos, _lr ? RR_LR_TERM : RR_SR_TERM, _lr ? RR_LR_TERM_LEN : RR_SR_TERM_LEN); } memcpy(crlf, CRLF, 2); memcpy(r2, RR_R2, RR_R2_LEN); if (!(_l = insert_new_lump_after(_l, prefix, prefix_len, HDR_RECORDROUTE_T))) goto lump_err; prefix = 0; if (!(_l = insert_subst_lump_after(_l, _inbound ? SUBST_RCV_ALL : SUBST_SND_ALL, HDR_RECORDROUTE_T))) goto lump_err; if (enable_double_rr) { if (!(_l = insert_cond_lump_after(_l, COND_IF_DIFF_REALMS, HDR_RECORDROUTE_T))) goto lump_err; if (!(_l = insert_new_lump_after(_l, r2, RR_R2_LEN, HDR_RECORDROUTE_T))) goto lump_err; r2 = 0; } else { pkg_free(r2); r2 = 0; } if (!(_l2 = insert_new_lump_before(_l2, suffix, suffix_len, HDR_RECORDROUTE_T))) goto lump_err; suffix = 0; if (!(_l2 = insert_new_lump_before(_l2, crlf, 2, HDR_RECORDROUTE_T))) goto lump_err; crlf = 0; return 0; lump_err: LOG(L_ERR, "build_rr(): Error while inserting lumps\n"); if (prefix) pkg_free(prefix); if (suffix) pkg_free(suffix); if (r2) pkg_free(r2); if (crlf) pkg_free(crlf); return -4; } /* * Insert a new Record-Route header field * And also 2nd one if it is enabled and realm changed so * the 2nd record-route header will be necessary */ static inline int insert_RR(struct sip_msg* _m, int _lr) { struct lump* l, *l2; str user; struct to_body* from; str* tag; str* avp_cookie; int res = 0; from = 0; /* Makes gcc happy */ user.len = 0; if (fparam_username) { if (get_str_fparam(&user, _m, fparam_username) < 0) { ERR("insert_RR(): Error while getting username (fparam '%s')\n", fparam_username->orig); } } if (append_fromtag) { if (parse_from_header(_m) < 0) { LOG(L_ERR, "insert_RR(): From parsing failed\n"); return -2; } from = (struct to_body*)_m->from->parsed; tag = &from->tag_value; } else { tag = 0; } avp_cookie = rr_get_avp_cookies(); if (enable_double_rr) { l = anchor_lump(_m, _m->headers->name.s - _m->buf, 0, HDR_RECORDROUTE_T); l2 = anchor_lump(_m, _m->headers->name.s - _m->buf, 0, HDR_RECORDROUTE_T); if (!l || !l2) { LOG(L_ERR, "insert_RR(): Error while creating an anchor\n"); res = -5; goto exit; } l = insert_cond_lump_after(l, COND_IF_DIFF_REALMS, HDR_RECORDROUTE_T); l2 = insert_cond_lump_before(l2, COND_IF_DIFF_REALMS, HDR_RECORDROUTE_T); if (!l || !l2) { LOG(L_ERR, "insert_RR(): Error while inserting conditional lump\n"); res = -6; goto exit; } if (build_rr(l, l2, _lr, &user, tag, avp_cookie, OUTBOUND) < 0) { LOG(L_ERR, "insert_RR(): Error while inserting outbound Record-Route\n"); res = -7; goto exit; } } l = anchor_lump(_m, _m->headers->name.s - _m->buf, 0, HDR_RECORDROUTE_T); l2 = anchor_lump(_m, _m->headers->name.s - _m->buf, 0, HDR_RECORDROUTE_T); if (!l || !l2) { LOG(L_ERR, "insert_RR(): Error while creating an anchor\n"); res = -3; goto exit; } if (build_rr(l, l2, _lr, &user, tag, avp_cookie, INBOUND) < 0) { LOG(L_ERR, "insert_RR(): Error while inserting inbound Record-Route\n"); res = -4; } exit: if (avp_cookie) pkg_free(avp_cookie); return res; } /* * Make sure that the record-route function is not called * twice and if not, execute it */ static inline int do_RR(struct sip_msg* _m, int _lr) { static unsigned int last_rr_msg; if (_m->id == last_rr_msg) { LOG(L_ERR, "record_route(): Double attempt to record-route\n"); return -1; } if (insert_RR(_m, _lr) < 0) { LOG(L_ERR, "record_route(): Error while inserting Record-Route line\n"); return -3; } last_rr_msg=_m->id; return 1; } /* * Insert a new Record-Route header field with lr parameter */ int record_route(struct sip_msg* _m, char* _s1, char* _s2) { return do_RR(_m, 1); } /* * Insert manually created Record-Route header, no checks, no restrictions, * always adds lr parameter, only fromtag is added automatically when requested */ int record_route_preset(struct sip_msg* _m, char* _data, char* _s2) { str user; str data; struct to_body* from; struct lump* l; char* hdr, *p; int hdr_len; from = 0; user.len = 0; if (fparam_username) { if (get_str_fparam(&user, _m, fparam_username) < 0) { ERR("record_route_preset(): Error while getting username (fparam '%s')\n", fparam_username->orig); return -1; } } if (get_str_fparam(&data, _m, (fparam_t*)_data) <0) { ERR("record_route_preset(): Error while getting header value (fparam '%s')\n", ((fparam_t*)_data)->orig); return -1; } if (append_fromtag) { if (parse_from_header(_m) < 0) { LOG(L_ERR, "record_route_preset(): From parsing failed\n"); return -2; } from = (struct to_body*)_m->from->parsed; } l = anchor_lump(_m, _m->headers->name.s - _m->buf, 0, HDR_RECORDROUTE_T); if (!l) { LOG(L_ERR, "record_route_preset(): Error while creating an anchor\n"); return -3; } hdr_len = RR_PREFIX_LEN; hdr_len += user.len; if (user.len) hdr_len += 1; /* @ */ hdr_len += data.len; if (append_fromtag && from->tag_value.len) { hdr_len += RR_FROMTAG_LEN + from->tag_value.len; } if (enable_full_lr) { hdr_len += RR_LR_FULL_TERM_LEN; } else { hdr_len += RR_LR_TERM_LEN; } hdr_len += 2; /* CRLF */ hdr = pkg_malloc(hdr_len); if (!hdr) { LOG(L_ERR, "record_route_preset(): No memory left\n"); return -4; } p = hdr; memcpy(p, RR_PREFIX, RR_PREFIX_LEN); p += RR_PREFIX_LEN; if (user.len) { memcpy(p, user.s, user.len); p += user.len; *p = '@'; p++; } memcpy(p, data.s, data.len); p += data.len; if (append_fromtag && from->tag_value.len) { memcpy(p, RR_FROMTAG, RR_FROMTAG_LEN); p += RR_FROMTAG_LEN; memcpy(p, from->tag_value.s, from->tag_value.len); p += from->tag_value.len; } if (enable_full_lr) { memcpy(p, RR_LR_FULL_TERM, RR_LR_FULL_TERM_LEN); p += RR_LR_FULL_TERM_LEN; } else { memcpy(p, RR_LR_TERM, RR_LR_TERM_LEN); p += RR_LR_TERM_LEN; } memcpy(p, CRLF, 2); if (!insert_new_lump_after(l, hdr, hdr_len, HDR_RECORDROUTE_T)) { LOG(L_ERR, "record_route_preset(): Error while inserting new lump\n"); pkg_free(hdr); return -5; } return 1; } /* * Insert a new Record-Route header field without lr parameter */ int record_route_strict(struct sip_msg* _m, char* _s1, char* _s2) { return do_RR(_m, 0); } /* * Remove Record-Route header from message lumps */ int remove_record_route(struct sip_msg* _m, char* _s1, char* _s2) { free_rr_lump(&(_m->add_rm)); return 1; } kamailio-4.0.4/obsolete/rr/doc/0000755000000000000000000000000012223032460015001 5ustar rootrootkamailio-4.0.4/obsolete/rr/doc/loose_vs_strict_routing.dia0000644000000000000000000020601512223032460022454 0ustar rootroot #A4# #Route present# #Exit# #No# #parse_ruri# #Yes# #Error# #OK# #No# #ruri==myself# #loose_route# #parse_route# #Yes# #Error# #Ok# #route==myself# #strict_route# #No# #Yes# #route==myself# #Yes# #No# #has To-tag# #Yes# #Exit# #No# #this cant be strict routed# #no URI points to us: mis-routed!?# #preloaded strict routing does not exist# #route contains 2rr param# #No# #Yes# #this is a preloaded request# #this contains 2 of our Routes and was strict routed# #plain strict routing# kamailio-4.0.4/obsolete/rr/doc/functions.xml0000644000000000000000000000550312223032460017536 0ustar rootroot
Functions
<function>loose_route()</function> The function performs loose routing as defined in RFC3261 and will set Avp value passed in Route header that were created by record_route. If ftag!=tag.from then from/to are flipped. <function>loose_route</function> usage ... loose_route(); ...
<function>strict_route()</function> -- deprecated If there are any Route HFs in the message, the function takes the first one, rewrites Request-URI with it's value and removes the first URI from Route HFs. <function>strict_route</function> usage ... strict_route(); ...
<function>record_route()</function> The function adds a new Record-Route header field. The header field will be inserted in the message before any other Record-Route header fields. Avp marked using setavpflag, flag dialog_cookie will be inserted as serialized parameter of record-route header. Note that only user class AVPs should be passed as cookies, i.e. domain and global should be avoided. <function>record_route</function> usage avpflags dialog_cookie; # must be declared when used in script ... setavpflag($cookie, "dialog_cookie"); setavpflag("$f./^dlg_/", "dialog_cookie"); record_route(); ...
<function>record_route_preset(string)</function> This function will put the string into Record-Route, don't use unless you know what you are doing. Meaning of the parameters is as follows: string - String to be inserted into the header field. <function>record_route_preset</function> usage ... record_route_preset("1.2.3.4:5090"); ...
kamailio-4.0.4/obsolete/rr/doc/loose_vs_strict_routing.png0000644000000000000000000017675112223032460022520 0ustar rootroot‰PNG  IHDR„©¹@ sBITÛáOà pHYs   jŒw IDATxœìÝy\Tõþøñϰ  ".iH‚: ¢’á¾.…V^lñ^¼¸æ.¦ˆ fj¢©™{êÕÔÌÍ%÷ÊkÜ7\ØdqK½j*j"œßsó‘eàÌ9¼žÜÇÌ™3gÞx©^~Μ$IP‹+W®|üñÇëׯïÙ³çŒ3¼½½åžECŒBôºnݺ'Ožè·h4š€€’sf!÷@Y]¹reøðá7^¹r¥¾Dûöíkaa!IÒÎ;Û´ióÆoœ8qBî1@!ˆQ(X¾ Õh4'Ožüþûï/]º4bÄ’sÆiz(R¾“òæ7Þ˜1cF«V­Œw»zõêœ9sÖ®]›-8q€ù!F¡0ÅÌPc$)f‹…b”"C‘¤˜!b PÆ 5F’`VˆQ˜5f¨1’3AŒÂL•S†#I1 ³SjŒ$@FÄ(ÌHg¨1’Y£0 2f¨1’€ FŒBff’¡ÆHR* 1 Ù˜a†#I¨Ä(d`æjŒ$ \£¨P ÊPc$)å„EQh†#I09båNjŒ$À„ˆQ”#•e¨1’“ FQ.Tœ¡ÆHRʈ…‰U’ 5F’PjÄ(L¦f¨1’€R Fa•Ä(žƒ 59’ ¹€ùºråÊðáÃ7n¼råÊ'Ožh4šÀÀÀ'Nlß¾-‹úõë/[¶ìâÅ‹#FŒ°±±‘$içÎmÚ´yã7Nœ8!÷t(Ü!C4ͬY³ [<¨Ñhîß¿/ãT Ä( A†V’Tqlmm£££ïܹ#÷  *Ä(þ‚ ­`$©‚øøø¸¸¸/Žù¤Gnnn‹-rss[¶l™\C€²£•j†HR3÷É'ŸlݺõôéÓ†-ÉÉÉ999:tÐßÕh4;v$)@AˆQ#CQ’ Ĩ"‘¡(&’`æøÐ{…1ϯ2dˆF£Ñh4–––uëÖ¬]»öºuë,„$ÉÓÓÓÏÏoÉ’%B//¯!C†ÞRÙ¯_?GGÇ¥K— !5j´hÑ"!Dffæ /¼°{÷înݺé÷œ9sæÑ£G8o’îÝ» !<¨¿»lÙ²™3gþ÷¿ÿÕ?¤Õj÷ìÙ£¨ˆc&%%5mÚôÂ… †#=C÷îÝkÖ¬¹mÛ6ýC ¨U«ÖòåËóý æ¦àåMþþþ¡¡¡æùk¥¥¥iieÚá씕¡ÞÞÞëÖ­ËÌÌ\»víæÍ›oݺ¥Ñää䜜œ:èwÓh4;v}ú4ßm+«ÿýžn}LF³wïÞÿûß?ýôÓW_}5}úôƒêÏh1C¾ŸEAçêׯ¿xñâìììµk×Ê=  Ò!FÍ‹­­íùóç>88844ÔÑÑÑÍÍmñâÅ©©©ß~û­~OŸ­[·Ž3¦F+V¬ˆïÔ©“þ!—ØØØ«W¯ÚÙÙÕªU+444<<¼jÕª¯½öZVVÖ‘#G$I )øêIII3fÌ ‰‹‹ûä“O""" ²J•*Ï:f\\Ü?þèïï_§Nóçϧ¥¥±ÑÆ?ˆYýW0Cu:ÝôéÓƒ‚‚,--å `n~ùå—rŠQ®¦7w‡êÒ¥‹áÿ/{{û©S§Þ¾}[î¹þ"ßG;I’´lÙ2­V{ãÆ I’²³³'L˜P·n]­V›ï£=z4`À77·©S§¾ù曆«é/^¼èíímmm-„¸xñ¢$IK—.uww·¶¶®^½ºÏ Nâçç7~üøàà`{{ûZµj………é?bIÿД)Sòí_è1SRRzöìY»vmKKKggç°°0ÃçC=k†|6lØ€ þ ¥úÓ5½ÌÌÌÏ>û¬^½z†ß+N·aÃà @>o¼ñ†ÂÛÛÛäGæjze0>q/„°··WÊ*iëÞ½»··÷ܹsåÄL± (ÀÀÀ~ø¡<.`☔Á××÷—_~1¬’êOÜ»¹¹M›6íÎ;rOÈÊÊZºti£F>øà}‰êWCãããß}÷]J bTIHR” 0gœ¦W*NÜã¹8)0NÓ#?VIQVCJAŒ*IŠ|ÈP€²£j@’B¡e"FÕƒ$­´ÈP€r£jC’V*d(@éˆQu"IU ¨1ªf$©*‘¡5!FÕ$U 2 >ÄheA’* P+b´r!I‡ ¨1Z‘¤Š@†*b´ò"IÍ ¨<ˆÑÊŽ$5+d( ²!F!IjÈP@åDŒâÿ¤² C•1ŠüHÒ C†@Œ¢p$i¹"CÐ#FQ’ÔäÈPŒ£x>’Ô$ÈP "FQ\$i©‘¡< 1Š’!IK„  hÄ(Jƒ$}.2€â FQz$i¡ÈPŠEY‘¤d(%EŒÂ4*y’’¡”1 Sª„IJ†PÄ(L¯’$) @Ù£(/*NR2S!FQ¾T–¤d(¦EŒ¢"¨ IÉPÊ1ŠŠ£Ð$%C(?Ä(*š‚’”  ¼£‡™') @Å F!'3LR2€ŠDŒB~f’¤d(…¹1IÉPäBŒÂ¼Tp’’¡È‹…9ª€$%C0Ä(ÌW9%) €ù FaîL˜¤d(憅2”1IÉPÌ1 %)E’’¡˜3bÊSÌ$%C0Ä(”ªˆ$ýý÷ßÉPA#I’Ü3euøðá™3g9r¤àC:núôéAAA4(¥øÃ?x{{ÇÆÆšöȬŒB ò­’ê± €ù³’{Àd|}}}}}{õêµÿþºuëÆÇÇÓ ˜9VF¡6ÎÎÎB;;;JóGŒBmô š››+÷ àùˆQ¨ 1 €‚£P}ŒæååÉ=x>bjÃÊ( BŒBmˆQ„…Ú£(1 µ!FPbjCŒ  Ä(Ô†@AˆQ¨ 1 €‚£PÃçŒJ’$÷,à9ˆQ¨á+éYÀü£Pb!F¡6Ä( BŒBmˆQ„…Ú£(1 µ!FPbjCŒ  Ä(Ô†@AˆQ¨ 1 €‚£Pb!F¡6Ä( BŒBmˆQ„…Ú£(1 µ!FPbjCŒ  Ä(ÔÆÂâ¿ÕÄ(æ…Ú°2 €‚£Pb!F¡6Ä( BŒBmˆQ„…Ú£(1 µ!FPbjS¢ ìÖ­›$I†-«V­ªQ£Æµk×Êk>`„…Ú”(FW®\yîܹÏ>ûL÷Ê•+“&MZ´h‘³³s9Žþ?bjS¢uttüüóϧNš’’"„îÚµë!CòòòfÍšõòË/kµÚfÍš}ûí·úý¿ùækkk‡nݺeff–ß@e`%÷€‰•ô=£üöÛoÿþ÷¿4èÔ©SçÏŸBL™2eÿþý6lpss;|øð{ï½W¿~}WW×   … öíÛ7;;û—_~)ǀʅڔâ¦eË–5kÖìƒ>ظq£““Ó£G>ûì³ÿûß­[·B¼ÿþû?ÿüó† FŒ‘““Ó·o_ýIü† –Ó@åÁiz¨M)b´N:!!!õë× B$$$dgg·iÓFóÿ­_¿þúõëîîîݺuswwûí·—.]zçÎòú¨4ˆQ¨Mé>ÚÉÊÊÊÊê' òòò„ׯ_—Œ|÷Ýw‡Ú±c‡»»ûªU«š4i’ššjòù¨TˆQ¨MÙ?gÔÃÃC«ÕîÞ½»àC¾¾¾}ôÑéÓ§«U«¶k×®Ò xÏ(Ô§ì1joo?~üøÉ“'ÛÚÚvíÚõÁƒû÷ïwsssqq9xðà믿^»víãÇߺu«qãÆ¦€Êˆ…Ú˜ä˜æÌ™S·nݨ¨¨ôôt{{ûöíÛÏž=»jÕª˜;wnFF†‹‹ËìÙ³ýýýM45•”Æø»g¸~ýz½zõ„«W¯:t¨Üã ?üðƒ··wll¬iÌ{F¡6|7= BŒBmˆQ„…Ú£(1 µ!FPbjCŒ  Ä(ÔÆ£ú/R挅ڰ2 €‚£Pb!F¡6F£(1 Ò/Ž£˜?b*DŒ Ä(Tˆ@)ˆQ¨1 €R£P!b¥ F¡BÄ(JAŒB…ˆQ”‚… £(1 "FP b*DŒ Ä(Tˆ@)ˆQ¨1 €R£Pƒœ9sÆp·`ŒnÙ²åÖ­[2LŠDŒBñ/^ü /„„„H’¤ß’/FïÞ½;lØ0''§Õ«WË6%( 1 ÅËÈÈÈÉɉýá‡ô[òÅèâÅ‹>|˜——çææ&Û” 0Ä(oܸqµk×BDDDäå剿Æèƒ>ûì3!DûöíýüüdäGŒBñªW¯*„ˆ‹‹ûæ›oÄ_ctéÒ¥÷îÝB„……É:&(1 55jÔ‹/¾(„ˆŒŒÌÍÍ5ÄèŸþ¹hÑ"!D«V­üýýež@ŒB ªV­:uêT!DBB¦M› 1ºråÊÛ·o !ÂÂÂ4ÌS€ˆQ¨Ä°aÜ…QQQBˆœœœèèh!D³fÍúöí+ó| 0Ä(TÂÖÖvúôéBˆÔÔTýjèÕ«Woܸ!„ Óç)07üêñüÃÕÕUqùòeÃÿ6nܸÿþ2Ož…zX[[Ϙ1C‘••U»víììl!ÄÔ©Sõo!fˆ…ª¼óÎ;Mš4BüñÇBˆ ¼óÎ;rž‰…ªXYYEDDîN™2ÅÚÚZÆy@шQ¨Í€š7o.„¨W¯Þßÿþw¹ÇE!F¡63gÎBLš4ÉÆÆFîq@Q¬äŠôÏþsíÚµrOQF3qâĉ'Ê=È3µmÛ6&&Fî)1ŠÒ$)//Oî)žC’$¹G(ŠùÿPˆQ”žƒƒÃ¸qãäžBy6oÞ|ñâE¹§À,£(½5jDFFÊ=…òœ8q‚@ ˜ C‡Õü•­­­ÜC€ ±2 …óððØ´i“á®…E!{ÏÉÉÉ÷Y¶·¡D;€*£P8;;;//¯‚Û­¬¬vìØQ£F„„„‚[rss###×­[wûömN7sæÌ·ÞzëYO¯Ø Ì §é Ä6lØàååuí򵯯ØB·„††._¾|ùòåIIIƒ êׯߊx:TZÄ(îäÉ“ÆïíÖ­›á¡6mÚ >ÜÒÒ²Zµj·dff.[¶lÖ¬Y}úôiРÁ´iÓüýý,XPÄÓ Òâ4=.ß{FÃÑÓÓ3ßÎÆ[’““³²²:uêdØÒ¥K—E‹ñt¨´ˆQ(ܳÞ3*„°²Êÿ/Ï‚[ŠP¢@Ý8M&¦Óélmm=jØräÈ‘-ZÈ8˜-þv…ËÌÌú裊ót¨ÌxÏ(dCŒ@6Ä(dCŒ¥7pàÀ!C†È= ÆL@éuíÚÕÚÚZî)P0bÊ“““S X¢Ãêw1b„ÉÇ Rá4=*Z@@@ppppp°››[íÚµ‡ òèÑ#ýCßÿ}»víªU«fgg×¹sçãÇ?kèСÇwrrÒ“Íž={¼¼¼´Zm•*U^yå•S§Né÷üì³Ïš4i¢Õjëׯ•——Wô0ùëíímüq<“&MêÞ½û³væ4=eDŒBÿú׿:tè–––˜˜xöìÙàà`ýöìììiÓ¦?þÌ™3Í›7ýõ×322 ÏÚ°aƒ——×µk×bcc322Þ~ûí~ýú¥¤¤$&&FDDØØØ!>þøã… .X° --mݺuk×®ýøã‹Æø°Ï¼D;€ç"F!ƒW_}U uêÔ™3gζmÛnݺ%„8pào¼Ñ Aƒ&Mš|þùçVVV0<«M›6Ç·´´¬V­Ú;w²²²^ýu—¾}û6kÖìÉ“'sçÎ]ºti@@€³³s÷îÝgΜ¹jÕª¢‡1>ìs'/ÑÎà¹xÏ(dвeKÃm///I’’““ëÖ­{éÒ¥ÈÈÈß~û-##C’¤?þøãƆ==== ·6lاOŸŽ;úúúvîܹ_¿~7NJJzøða@@€ñki4š¢‡1>ìs•hçgyúô©âĉe?àñãÇBˆ¤¤$“™… ô5¦—““#„°²²ÊËËëÑ£‡§§ç† êÕ«giiÙ¾}ûÜÜ\ÞVVùuýá‡bbb~úé§ü1<<ü믿nذ¡âôéÓ^^^Å&ßaóÅ«$IEì\:§OŸBäææ>xð ìG Â}%Fé£ALLŒ$Iúì;zô¨µµµ»»ûï¿ÿž’’òÝwß5oÞ\ñàÁƒëׯ}œ:tèÐ!,,,00pãÆ[¶l©V­Ú®]»J£ùÔ¨Qãþýû†»©©©¥>Ô³8::ÞºuËÉÉ)((Èä <¬X±âÏ?ÿ,#£Áõë×'L˜0~üøK—.Mž<ùïÿ»ƒƒƒMõêÕwïÞݼyóÌÌÌQ£FñׯóçÏoß¾= ÀÑÑ1555..nàÀ¶¶¶Ó§OˆˆpppÈÍÍ=~üxjjjxxxñgóõõ]¹rå¤I“¿ûî»}ûöuìØÑ?ôÿ©_¿þÙ³g]\\¢££M{dÊɦM›Ê)F¹€ 2û¬Aƒ%šmüøñ]»vuwwoРÁþñ”ígEÑä{KPC‡ýâ‹/^~ùåK—.•ô¹ 4Xºti9Ì¥ »wïnÛ¶ío¿ý&÷,‹““ÓÍ›7«V­jøtpSae²!F¡~)))šÂ33ó믿 Ñ—¨ž¥¥åäÉ“ãããOžzôÈ´GfeêÄwÓ Ä(ÔÉäíÊ1 u*Ñ×¹£P'NÓ Ä(Ô‰Óô(1 ubeE F¡N¬Œ Ä(Ô‰ ˜PbêÄiz…:qšE F¡N¬Œ Ä(Ô‰•Q…:±2 €"£P'®¦@ˆQ¨§éPbêÄiz…:±2 €"£P'VFPbêÄL(1 uâ4=Š@ŒB8M€"£P'VFPbêÄÊ(Š@ŒB¸€ E F¡§OŸ6Ü.xš>%%åÁƒ2Œž…;vÌÃã{÷îú-OÓøá‡µk× ‘gDPbjpûöí„„„»wï~úé§ú-ùVF/\¸ðý÷ß?}úT’$Ù¦£Pƒ€€€¶mÛ !-Zt÷î]Q`etöìÙyyyVVV~ø¡Œs€|ˆQ¨DTT”"###::Züue499ù믿B 4¨aÆ²Ž þ‚…JôìÙ³sçÎBˆÏ>ûìÖ­[ÆWÓÏ™3'77×ÂÂbÚ´i2O þŠ…zÌš5KñèÑ£yóæNÓ§§§oÚ´Iñ·¿ý­iÓ¦2þŠ…ztíÚÕÏÏO±|ùò?ÿüS‘››;wîÜœœF&÷€ ?bª¢_ÍÌÌüõ×_…yyyëׯB6oÞ\ÞÙ@AÄ(T¥}ûöþþþBˆS§N !$Izòä‰F£™>}ºÜ£€B£P›¨¨(FóôéS{{{F#„xýõ×_}õU¹ç… F¡6¯¾új`` âáÇú¸gY³EŒB…fΜ©ÿœQ!„ŸŸ_ûöíå< 1 jÑ¢Åßþö7ýíððpy‡E F¡N‘‘‘–––;wîÚµ«Ü³€g²’{˜»5kÖ,\¸Pî)J£Zµj—/_öðð{ÒØ¿¿³³³ÜSPîˆQ<Çü‘ ÷¥”‘‘!÷¥”““#÷TbÅÕ¿ýwl¢ü\ºté?ÿùÜSPqˆQׯµZ­ÜS¨Ü—_~IŒš³¡C‡~ñÅÆ[lll²²²äšT€€ðððØ´i“á®áCÄŒåääX[[½¥%Ú”Ž€°³³óòò*¸= ÀÑÑÑÊÊjÇŽ5jÔHHH(¸%77722rݺu·oßÖét3gÎ|ë­·žõôŠý±@6|´˜Æ† ¼¼¼®]»[è–ÐÐÐåË—/_¾<))iРAýúõ;pà@O€Ê€€8yò¤ÆH·nÝ µiÓføðá–––ÕªU+¸%33sÙ²e³fÍêÓ§Oƒ ¦M›æïï¿`Á‚"ž•§é ò½gÔ8===óíl¼%999++«S§N†-]ºtY´hQO€Ê€€xÖ{F…VVùÿZpKJ´3¨§é "èt:[[Û£G¶9r¤E‹2Ž怿ˆ@ dffž9sÆx‹§§gq5íììF^¿~ýæÍ›oÞ¼yÏž=ûöí+·I@ˆQ(øøøW^yÅxË7‹óÜyóæÙÚÚ6ìöíÛMš4Ù¶m[=ÊgLP $Irϳ6oÞ¼?üP‘Í70•·/¿üòÝwßB¤¥¥¹ººÊ=ÿãäätóæÍªU«>zôÈ´Gæ=£ 1 Ù£ 1 Ù£ 1 ”X@@À¸qãäž5 Faî²³³tXP"Ä(ÊW÷îÝGŽÔ AƒºuëNš4)77WÿÐÖ­[½½½«T©RµjÕ^½z]¼xÑøY£G~ï½÷êÖ­ „رc‡§§§µµµ½½}»ví®_¿.„$)::ºaÆZ­¶aÆ+V¬xî0ùûäÉ“I“&999ÙÚÚ¶mÛöÈ‘#†½¼¼-Zd¸Û¯_¿Ñ£G !† ²{÷îÅ‹k4F“’’RÒ1€1Šr·zõê·ÞzëÒ¥KÇß¶m[TT”~ûÓ§O£¢¢;foo˜——gü¬^½zݼysûöíwïÞ0`À!CÒÒÒΞ=;fÌF#„ˆŒŒ\³fÍŠ+ÒÒÒ,X¶uëÖçc8¬bìØ±[¶lÙ°aC|||»vízõꕘ˜XôÖ¯_ß»wï±cÇJ’$IR£FJ1Ðãë@QîºvíÚ¯_?!Dƒ ¦L™2sæÌ™3g !lØgݺu.\hÞ¼¹~‹ŸŸŸ~‡ªU«^»v-;;Ûßß¿~ýúBýWeffFGGïÞ½»[·nBgg縸¸5kÖ 0 ˆaŒûðáÃ/¾øbݺuú¯d\´hÑÁƒ—-[¶dÉ’âÿt¥ã¹RRR233ËrLèéÓ§BˆòøæNbå®E‹†Û-[¶¼uëÖ½{÷jÖ¬™’’ûàÁý*ãÍ›7 1êááax–N§ëÙ³§··w=ºvíÚ¿ggçÄÄÄÇûøø¿–»»{ÑÃ6999''§C‡ú»¦cÇŽçÎ+ÑOWº1ž«gÏže<&—••eòcršåNÿw)ãÛVVV¹¹¹Ý»w—$ióæÍ'Ožõ"##ëÔ©³xñâ>øÀÎÎîÕW_6mZ‰f[´h‘]PPÐýû÷½¼¼öíÛפIýC“&MJHHpuu­]»ö€ô¥7vìØAƒ5lØ0''çâÅ‹e€JKSWEAMæÍ›÷ᇠ!²³³µZmIŸÞ½{wooï¹sç–Ãh*ôå—_¾ûî»Bˆ´´4VFæÃÖÖ6;;Û¢è·Õ•0@6Ä(Ô&%%ES˜k×®É=È÷Œ¢|½ì3P £P§RÇhzzú“'OZ¶l™o»~KBB‚aËÚµkƒ‚‚6nÜ8tèв @åe%÷@¹(ïvÚ±cÇòåË<èããSÇ ’`eêTêuuuÕjµqqqù¶Ÿ={VÑ´iSýÝ–-[:;;¯Y³&77·ÌÃPy£P§RǨ]¿~ýV¯^}ïÞ=ÃÆ¼¼¼èèhwwwoooý– üòË/¿þúëàÁƒŸ>}jª±¨lˆQ¨SYNÓ/\¸P«ÕvîÜùÛo¿MHH8tè¿¿ÿÉ“'ׯ_oañÿÈè{ôøñãAAAô(¥CŒBÊ£/¾øâñãÇýüü&NœØ²eËAƒÕ¬YóøñãmÚ´É·çË/¿üË/¿œ:ujÀ€&€ÊGÃ7wC•>ìçç'„ø÷¿ÿÝ©S'¹Ç@Ùlmm³³³-,,L~±+£P§ò¾š˜1 u2¼¹“EùéÞ½û‡~XèC}ûö7n\Ïó\E r!F¡N¬Œ¢<31}}} ¾É¸bH0|è=Ô‰…¼ÆŒ#÷  ¬ŒBˆQ˜Ü!CvïÞ½xñbF£ÑhRRR„¹¹¹'N¬Y³f5&Nœhø}3>M¿cÇOOOkkk{{ûvíÚ]¿~=ß‘%IúôÓOu:V«uttœÿüó è·GFF®Y³fÅŠiii , Ûºu«éþðà™8Mu"FQ1Ú¶m.„hÔ¨Ñ믿~øðá¿ýíoÆ;ܾ};;;Ûßß¿~ýúBWW×|Gxüøñ‚ –,Y$„pqq1|Ñ×àÁƒ »­[·ÎÁÁáÂ… Í›7×oñ÷÷ Bètº^½z|éByxxDFF !œ§NºdÉ’I“&effFGGïÞ½»[·nú‡âââÖ¬YÃ訬ŒBˆQTŒ–-[n;::Þ¿?ß:®gÏžÞÞÞ .¼víZ¾333}}} <%%åwÞiÒ¤‰““SãÆ%Iºyó¦áQ777Ãíš5k|éBµoßÞp»C‡W¯^ÍÈÈHLL|üø±æÿ‹ŒŒüý÷ß‹s@(#bêDŒ¢bC¬(ì÷M£ÑìÝ»wïÞ½­Zµúꫯt:]LLLqŽœ››Û½{wI’6oÞ|òäÉ3gÎXZZÖt¾ÓýeùUןýONN–ŒÄÇÇ—ú€P|Ä(Ô‰Ey°´´4~×f1i4š.]ºDDD?~ÜÃÃcÛ¶mÆ6mÚÔÎÎîðáÃùžuùòåË—/ôÑG¯¾úêK/½tÿþýb~ëIÑCþöÛo†Û111õë×wpppww·³³Û¹sgI~,0 bê‘`HÏBcÔø?Ã@)¸¸¸ÄÆÆ^½zõÎ;ŬҸ¸¸¹sçž={öÆHKKÓétÆ;T©Reüøñ“'OÞ²eËÕ«WO:µzõj!„££cµjÕvïÞ-„ÈÈÈ7n\Á+ŸJ1ä… "##¯]»¶gÏž¹sç~ðÁúBCCÃÃÃW®\yùò夤¤Õ«W¯Zµª8/eDŒB îܹäéé¹yófý–‚1ºsçÎöíÛûùù]¹rEž)¡|cÇŽ}úôiÆ ëÔ©“––Vœ§T«Ví§Ÿ~òõõ­_¿þ?þñ‘#G†„„äÛç£>š:ujXX˜››[ïÞ½õŸÇT¥J•M›6}úé§õêÕëÒ¥Kpp°V«-ûÿüç?¯]»æááñÎ;ïŒ1bÒ¤Iúí‘‘‘Ÿ|òÉâÅ‹7nܦM›-[¶¿'ʆ“˜P¬¬¬† ^¿~½I“&.\°´´2¼£%//O¿,êàà0vìX9ç£P›iÓ¦ !RSSõˆ#„8sæŒþ3ÆÇŒS³fM9çp5=TåÉ“':îòåËõêÕÓ™¡»»{BBBõêÕÓÓÓkÕª%÷€(WÓÅ¢ÕjÝÕNHHBŒ=šÀ ±2 µÉÉÉiÒ¤Izzº•••µµuff¦½½}zzzíÚµå ¥be(.kkëéÓ§ !ž>}š™™)„5j% õíÛwܸqrOQz>|¸ÜS…ëÔ©ÓÀ·üöÛoæÌ™3Bˆ¡C‡j4FcaaQ«V­>}úèO—é :ÔÛÛ»¢'FÄ(Tè½÷Þkذ¡þvµjÕ&L˜ ï}8'lnˆQ¨••Uxx¸þöˆ#êÔ©#ï<¨´²³³…cÆŒ 2áu 7nœ³³sõêÕß{ャ¬,ýC¹¹¹áááÎÎÎ666Í›7ÿî»ïJý*¶¶¶M›6uwwíµ×>øàƒÔÔÔŒŒ ý0 bê4xðàÆW©ReÒ¤IrÏõرc‡§§§µµµ½½}»ví®_¿>dÈÝ»w/^¼X*0%%¥{÷î£G~ï½÷êÖ­ þzš^’¤O?ýT§ÓiµZGGÇÉ“' ! ÄøE ðÉ“'“&Mrrr²µµmÛ¶í‘#G ;{yy-Z´Èp·_¿~£G.ô%$IŠŽŽnذ¡V«mذáŠ+ ÏÊÎÎ ©^½zݺu###ËïÏظqc«V­®\¹’päÈ‘èèhýöÐÐÐåË—/_¾<))iРAýúõ;pà@_ë÷ßÿúë¯u:]5Ê<8L‰…:ÅÇÇ;88888èß6”ÝÝ»w 0dÈ´´´³gÏŽ3F£Ñ¬_¿¾wïÞcÇŽ•$I’¤F !V¯^Ý«W¯›7onß¾=ßAf̘™’’²k×®Æ ! =ˆ±|;vì–-[6lØß®]»^½z%&&=|Á—ˆŒŒ\³fÍŠ+ÒÒÒ,X¶uëVýÎãÇß¿ÿž={Ž;véÒ¥={ö˜ä(¨K—.ï½÷ž……E½zõ pèÐ!!Dffæ²eËfÍšÕ§OŸ L›6ÍßßÁ‚¥{‰“'OZYYYYY9;;gffî߿ߤ?L€…ÚÄÅŽýöÛ^^^'Nœ¸qãF¯^½:tèÀ¿}Pv·oßÎÎÎö÷÷¯_¿¾««kPP““S¡{úùù <ØÂ¢jÕªÆÛ?~¼`Á‚ùó繸¸x{{‡„„ç¥øðáÃ/¾øâ“O>éÑ£‡››Û¢E‹ÜÜÜ–-[V¢Ÿ%333::zÅŠ=zôpvvîÛ·ï˜1cÖ¬Y#„øóÏ?×®];þüN:¹ºº®ZµÊÎήDŠÏø¯^5kÖ¼ÿ¾"999++«S§N†‡ºtéröìÙÒ½D³fÍΜ9sìØ±ˆˆˆS§N¥¦¦–qf˜œ•Ü&õý÷ßëßœ®ÑhìííM›6µ³³;|øpÁ‡ŠýÅO†–$)&&Æðßiã+…ÓÒÒ } www;;»;w<~Æ -,,Ο?oØRÒeW ŒìììF¾k׮˗/Ï™3gÏž='N,tç#FܺuëÝwß=vìX||üš5kæÌ™3bÄÃʨ1OOÏ×^{Íp™Ì1 ¥*:C÷$IaqqqsçÎ={öì78––¦Óé„...±±±W¯^½sçNÑAY¥J•ñãÇOžj-Z”žž^¶?9 ÄæÍ›7|øðaÆ5nÜxóæÍÛ¶mëÑ£G¡{6oÞüàÁƒ·nÝêÑ£‡——×¼yó¦L™2þügyòäÉ'Ožüé§ŸÊmv”œ(Í™3gÞzë-ÃéH}†¦¥¥ç¹wïÞ5$©^ûöí÷íÛWÞ3CRRRzöìY»vmKKKggç°°°§OŸJ’tñâEoookkk!ÄÅ‹ýüü¦L™büÄÀÀ@ÃeìyyyóæÍsuuµ²²rtt ÕoÏwã§<`vvö„ êÖ­«ÕjÛ´ióË/¿zôèÑ€ÜÜܦNúæ›oŽ5êY/±téRwwwkkëêÕ«ûøø8p@¿gFFFPPPíÚµ7nñ¨lúöí;nÜ8¹§€)£“yf¨1’r‘$éý÷ß?qâ„bäÈ‘£G–{"À,øúú¶iÓæ¹»”¨YKº?Lˆ…<Ì?C‘¤¨xáááß|ó¢gÏž‹/–{À\Œ3&((Hî)`R&ÿN' heù2OsÀŠÂ$bbbîß¿ÿ¬G7nܨÿgÄÝݽˆŽ¥¸Ý IDATÝEóóó1bÄ Aƒ^~ùå:uêLœ8Qÿ»’$eggOœ8ÑÑÑÑÆÆ&ßwÞ¿®ŸŸß˜1cBBBêÕ«W³fÍ &äååI’ôþûï§ÎÅ‹·oßÞ¬Y3++«jÕªµmÛö÷ß7ž¤àþE `¬eË–Ÿ~ú©áîÛo¿møÞ¢_Q‰Êïë@‰QT¥g¨1’e1þüB:zô¨þ_ú/¼ðBjjjT???++«mÛ¶I’”žžîââ2cÆ ýCÇ饗öïߟšš:fÌ[[Û„„ýCùb´J•*Û·o—$)))ÉÁÁá믿Ö?Ô»woÃnüñ‡Íüùó¯\¹’––¶iÓ¦ëׯçÆxÿ¢0ö¬}Ö+nܸQqûöí2þÑÉ‚…²©)C‘¤(5Ÿ Ö ÒÒÒêÖ­+„ÐjµGŽ‘e6 bøùùùùùî~þùçuëÖ•$éÁƒÖÖÖ_~ù¥~{^^ž‡‡Ç| ¿›/Fûõëg8Bÿþý‡®¿m—‰‰‰Bˆ .1ŒñþE`ìY1ú¬WÜ»woÛ¶mzº£üb”÷Œ¢|)뽡%Å{IQ:Ož<9vìØ¥K—~øáãí<èÓ§Ï­[·„«V­êܹ³L¤E‹†Û-[¶¼uëÖ½{÷’““srr:tè ß®Ñh:vìxîܹBàææf¸]³fÍû÷ïÜG§ÓõìÙÓÛÛ;00páÂ…úO¨(B‰(Ô³^±W¯^¿ýö›ñ0¡ü¨;C‘¤(©“'O>~üX±dÉÃÆÜÜÜ^¸pAñá‡æ{ JOŸ>ÍwÛÊʪDG0œsÓ“ ûôtF³wïÞ½{÷¶jÕꫯ¾Òét111¥š÷9¯ž——WÞ¯¨JÄ(L¯òd¨1’ÅwäÈýŸþùìÙ³úÛ&LøñÇ…o¾ùæìÙ³e¨@Ɖö믿ºººÚÛÛët:­VkxH’¤˜˜ã5Ôâ°´´4¤¡B£ÑtéÒ%""âøñãÛ¶m+bÿâààà`øb!DZZZñ_Ä(L©rf¨1’ÅñïÿÛp[¿8º|ùrýV­ZmܸÑ‚9£RHJJš1cƵk×vïÞýÉ'Ÿ|ðÁB{{ûàààÐÐÐC‡¥§§?>55uäÈ‘%:²‹‹KllìÕ«Wïܹsúôé¹sçž={öÆHKKÓétEì_µjÕbàãã³uëÖ{÷îI’´|ùòøøxýö¸¸¸B_qÿþý:u2îWÁG;ÁDÔz‰RYpy •››[£F Ão…ݶmÛô§&_zé¥k×®É= PAüüüÆloo_«V­°°0ý3I’”=a„ºuëjµÚ¢?ÚiÊ”)†‡† 6`Àýí‹/z{{[[[ !öîÝÛ³gÏÚµk[ZZ:;;‡……¼vÐxýG;=kc=0`€ƒƒƒ››ÛÔ©Sß|óMýL)))…¾"WÓŠï¦GY™ÛwÊ›¾ãùœ9sæ•W^1Þbkk›••U¥J•#G޼úê«r T°îÝ»{{{Ï;WîAP,|7=Ì'勃÷ÈÇø½^VV–F£™4iR³fÍd dDŒ¢4ÈÐ’"Ia`¸zɘ$IQQQÕ«Wo×®ÝäÉ“wîÜy÷îÝŠŸ *§éQ2œ”/;NÜWf’$999ý÷¿ÿ}îžÆÓÓ³K—.³gÏæS ÈŽÓô«¡¦Â*ie–œœ\œBØÚÚ¶iÓ&$$„ nÄ(ž -$iI 2D£ÑÌš5˰åàÁƒ¦Ð/\1[ß0ZPýúõ?þøã+W®¬Y³¦¤Ÿ­ŠCŒ¢(dhy#IKÄÖÖ6::úÎ;rRzEÇhÇŽ·nÝš––6uêÔ^x¡Â¦£(Z‘HÒbòññqqq1^5xòäɤI“œœœlmmÛ¶m[èEBæ ÐÁlllÞ{ï½'N=z´ÿþ%ý:DP4bù‘¡r!IŸËÂÂbîܹ+V¬HOOÏ÷ÐØ±c·lÙ²aÆøøøvíÚõêÕ+11Q–!‹põêÕK—.oqrrš9sæåË—ÿõ¯ÿÇÞ}4uî ƒƒae(Šâ¨Ž«`Q¡Š\«Ô­¢ Rðp`ÔAÝZ¹jë· uT´NªÈRÄj½Z±(2Îïso~)ËÉYù¼þJ‡ä ¥ðö9ç$ÿÆ+Œ€–RûËèá]”¸ïÞTÝĉ½¼¼(Šêß¿ÿ¸qã(Š:uê!äåË—¯_¿ÖÓÓÛµk½gee¥““ÓÌ™3Ù·&»víRüíÙ³ç®]»JKKÙ €”ßu‰ÆŽ;mÚ4¶§P3ͽVF¬†rVIë°|ùò¤¤¤7n(¶dee•••¹ººÒwE"‘››Û;wX°V?ÿü³žžÞ¸qã._¾|åÊ•þ󟉄í¡xÀÃãgÏžÌ?¯··whhhãÇÂÂbñâÅŠ»Ë—/wrr’H$ß~ûmãŸï£ÚÊeHÒõêÕkÔ¨QlRo;vüã?~üñÇÞ½{³= ŸÌš5+  aŸ[ZZªÞaïøñãK–,IOOŸ8qâܹsSSSÙžˆeˆQí… å $iuqqq©©©ôazBˆ½½½D"¹té}—¢¨K—.qðE‘fΜ٢E ¶§àŠAƒÍ;wæÌ™ÆÆÆ¦¦¦6l())™rppð„ LMM½½½I×8º¸¸¬^½Zq×ÏÏ/88˜xôèÑ5kÖˆD"‘H”““CQÔÊ•+mmm%‰­­íÆŸUZZÔ¬Y3SSÓ˜˜˜º¿ð³gÏŽ5ÊÅÅ%&&FGG'''§þß}:»CÀmÛ¶­M›6¿þúë‚ f̘1zôh—›7o†„„ýþûïUöÿÏþ3vìØÀÀÀ¼¼¼Û·oÏš5KqC[¶l:tèüqàÀÒ k½¼¼窶oß>&&fëÖ­7nÌËË‹ŒŒLJJ¢wž={öÉ“'SRR®\¹òðáÔ”U¾üØØØ-Z|òÉ'ªì,dj? ¸ —( ƒv^Þ¤¸€‰öôéSBÈË—/)Š*--3gŽ©©©D"éÙ³ç¹sçØ›Tâéé9tèPÅ]KKKúvEE…L&Û½{7}Wq]¿ýöÛyذaŠ»u_ãØ¥K—U«V)vöõõ1c}[9FKJJš6mšššªØ3&&fРAE½yó¦I“&{öì¡·—––ššš*_Àdii¹dÉ’*CFFFšššÞºu«î¯…;p4VC…D;WI9¢¸knnþæÍŠ¢Œ !‰$>>þÙ³g¥¥¥W®\éß¿?{“€ª”O§155uvv¦o‹ÅâV­ZU5{{û!C†ôèÑÃÇÇçÛo¿}üø1!äСCºÿóã?Ò{:99)>K-×8fff–”” 8Pô?111ôÚíýû÷KKKÝÜÜè=%ÉǬü¹ô?ž~ûí·%K–9r„ƒ'11*|ÈP¡ÒÎ$!‹ÅuÜ¥(ªÊþ"‘èøñãÇïÖ­ÛîÝ»ííí/]ºäáá‘ñ?ô¢„Õß<¢ÊþÊÊÊw£·gee)/éݽ{—RQQQ嫼P†L&322RÞB¿äp•fÕZˆQ!C†j$)h‘HÔ¿ÿèèèôôt''§äädCCÃÿÓ¬Y³êŸR÷5Ž2™ìÏ?ÿT윗—§¸­£££hSGGG}}ýÇW|[[[±Xœ‘‘¡ØReÙõôéÓŸ}ö™ò–ž={;v¬^_¸€!F… ªm¤  nݺõÍ7ßܾ}ûéÓ§§NÊË˳··ÿàgÕ}ãÀ“’’è³Ï7lØ@/vÒ¬¬¬®^½ZPPðüùs©TµiÓ¦üü|úOêæÍ› !-Z´=zô‚ è¨]½zu•w‰›0a‚\.WÞrûöíÅ‹¿}û¶ñß@Œ 2T›!I@Ø SSS=<<,--'Mš4}úô   U>qõêÕþþþ:t¸|ùò‰'è………uîܹ]»víÛ·/((Pè'„„„„”——ÛÚÚš˜˜äååÅÄÄ,_¾|Íš5vvv={ö”Ëå666ôžÛ¶m³¶¶¶µµµ··ÏÎÎöññQ~öôôôG)o)**ºxñ"}|DÕOÈžºuëÖÂ… ÷ïßOÿ7ÕÓÓ›8qâ×_ÕN/_¾\µjÕÚµk‡Ÿúôé3dÈv>’J¥¥¥¥b±Xí d(ÔI j¡¹Åaz~ÞAyÿÀÀ@¶§h,寢¼¼œ~C‘Ú^™Y£pà81ÊWÂËPÚ€úöíËöêtèÐ!¹\~þüù—/_²5’8 1Ê?<Êв²²úîüå—_N™2Ec±àáÇ–––...ôk³³I „å^d¨··÷”)S¾øâ‹Ö­[Ó¯âÖ£GÅ‹+v 4hPm;kâ0½··÷ôéÓç͛תU+ccãùóçWVV.\¸ÐÌÌÌÈȈ~«7BÈöíÛ•_hcΜ9Ý»w'„¤¤¤¸¸¸H$’¦M›víÚõúõëô ‰ÄÒÒráÂ…Õ_*ÙÏÏoîܹYYY"‘ˆõ¥!I€S£üÀ‹ Uرc‡‹‹Ëãǯ^½Ú˜ãââtk±~ýzÕ÷!„ÈårŠ¢.\¸°~ýúo¾ùæ“O>yùòeZZÚöíÛ7lذwï^BÈØ±c !ômBÈû÷ïwîÜ9eÊ”?ÿüÓ×××ÏÏ/'''333::š~Þ¸¸¸o¿ý6>>>//oûöíßÿ}\\\•/aïÞ½+V¬ppp (ªúûÚ±I «é¹ŽwWÊ{{{¿zõêÂ… Š-=zô9räüùóé»aaa7oÞ<}út;ûûûK¥ÒÄÄDBHQQѳgÏj|–>ú¨E‹*îãííýŸÿüGñÞýû÷ýúõÍ›7é»Ã† k󦒠 !3gμ}ûö¹sç!{÷î8qâ“'Ož?Þ¾}ûk׮ѫ¤´÷ïß·jÕJ.—{yyÑ[þýïGEEѯ$§üU¬\¹rëÖ­™™™õú62©Æ+î§OŸ.°“w a,,,TkM0Í]M/îÊÉɉˆˆPd(!dÊ”) ,°´´dw°êÔ©“Zv611111©ûÓUÙ‡Ò¥KÅmSSÓ¶mÛ*îš™™)Ö,§M›Ö¹sçììl;;»mÛ¶}úé§2™L&“1ÂÍÍÍÃã_¿~~~~vvv÷ïß/..V~mdRí Žù‚^%;wn||üÒ¥KËËË/_¾|ùòe¶çNÈÉɱµµe{ 2¦ç®6mÚôïßßÜÜ\±åâÅ‹.\àþ6Tù7t•D«²_Ç?¸/^,ªÅwß}§ú>„åG‹ÿö“¯©S§N}úôÙºukAAÁÉ“'§NJo?tèÐÙ³gÝÜÜŽ;æèè¸oß>úôÐ7nPJªŸ3Ê#7nÜ8þ|yy9Ûƒ€vÁÊ(wéë뇄„mÞ¼yÙ²eOŸ>½wï^@@À¢E‹¢¢¢ÆŒS%°8ËØØXùtÉÜÜ\?qêÔ©U–,,,Tß§^‚‚‚"""š4iâàààææ¦Øîêêêêêéãã³sçN¹\nhhxäÈ—< §¤¥¥ÅÄÄÐ''B ÜÝ݇nddÄî`À¢K—.mܸ‘í)@+ F¹NIêáá±iÓ¦°°0ssó}ûö8qB9òê`fffffÖø}êe̘1¡¡¡Ë–-[ºt)½%##ãÀÞÞÞæææ¹¹¹·nÝ¢O ?~tt´L&óöö®¨¨HOOÏÍÍŠŠRã0šV%C g̘1wî\UÎ|a‹ÅˆQ`Ó󤹹¹«W¯nݺ5!„NRggg¹\Îñ÷³gÏ0`€££cÛ¶mO:5iÒ$¶'ª‹¾¾þøñã !&L ·\¼xqÈ!mÚ´ñóó1bDtt4!$""býúõ[·níСƒ‹‹KBB‚ò©¨—––æîî>pà@ºD #""òòò¾ùæ”(0 WÓóÏÛ·o«¤ôGGG¾¬’ò‚¯¯¯®®nRRÛƒhVC@?üðý/s\À4¼7=ü?^¯’rÜË—/;vðàÁàà`¶gQ?¬†!Fù Iª ]»v3fLxxx¿~ýØžE¡ÀYˆQ~C’ª×Ç‹‹‹«¿‘!C€ã£B€$…ê¡À ˆQá@’  <‚$©6C†ï F… Iªm¡ÀSˆQ!C’jd(€¦=~ü8..Ž~ÇP;Ĩð!I…  ÀŒ9sæDFF®Y³æÝ»wlÏ"@ˆQm$d(“ÆGùóÏ?;Æö,„Õ.HR¾C†0oذa2™Œ²{÷n¶g Ĩ6B’ò2€-R©täÈ‘„#G޳=ŽÐ Fµ’”/¡¬£Ô—””:tˆíY„1ªí¤\† àOOOú¯\.g{¡AŒ!HRîA†pŠ®®î§Ÿ~J9yòä‹/ØGP£ðÿ¤\€ à&BHYYÙ¾}ûØžEP£P’”-ÈP.sss³´´$8R¯nˆQ¨’”IÈPî‹ÅcÇŽ%„œ;wîÉ“'l#ˆQ¨ ’TÓ¡ Iª ÈPÞéÞ½»½½=Á«ß«bT…$Ud(Ñ‹£éééyyylÏ"ˆQ¨$ic CøŽŽQŠ¢°8ª.ˆQh$i}!C„ÁÑÑÑÅÅ…àH½ú F¡á¤ª@† ½8zçÎŒŒ ¶gÄ(4’´6ÈPA;v¬H$"XUÄ(¨’T2@ÀÚ¶mÛ§OBÈîÝ»)Šb{ÞCŒ‚:!I‘¡Ú€>RŸ››{õêU¶gá=Ä(¨Ÿv&)2@{|úé§:::GêÕ1 š¢=IŠ Ð6æææ$„ìÙ³§²²’íqø 1 š%ì$E†h­qãÆB~ÿý÷óçϳ= ¿!F ÂKRd(€–5jT“&MŽÔ7b˜#Œ$E†!¤yóæC‡%„ìÝ»·¬¬Œíqx 1 Lão’"C@}¤þÅ‹§Nb{CŒ;ø•¤ÈP¨nĈ„¹\Îö,<†6q?I‘¡PƒO>ù„rðàÁ·oß²=_!F}ÜLRd(|ýê÷ÅÅÅGe{¾BŒWp'I‘¡ ¢üãÍ›7'8R߈Qàv“ õ"‘HFMIIIyýú5Ûãðb¸ˆù$E†@ÃÐGêß½{·ÿ~¶gá%Ä(p3IŠ €Æ8p ¹¹9Á«ß7b¸NsIŠ €ÆÓÑÑùôÓO !§OŸ.**b{þAŒ?¨7I‘¡ Fô‘úòòò½{÷²= ÿ FOŸ¤ÈPP»>}ú´mÛ–àH}ƒ F–¤ÈPБH4vìXBÈ… ?~Ìö8<ƒ¾R=I‘¡ iôûÔWVV&%%±= Ï FßjKÒŽ;ÊåòsçÎõë× ðAgÏž6lØ_ýUÛwîÜñóóËÉÉar*éÒ¥‹££#Á‘úúCŒ‚TOÒû÷︻»_¸p Cꔚšêééyüøñµk×Ö¶OTTÔO?ýäääôæÍ&gãú2¦k×®egg³= Ÿ FA8”“ÔÌÌL± P7ww÷®]»Bâãã‹‹‹«ïpýúõC‡B&OžlhhÈô|»“ð‚Om‹£ŠeÑI“&YYY±3OЗ1Ý»wïÖ­[lψQ¨kqË¢ª3fŒX,&8R_ˆQ äï‹£ïÞ½£7þöÛoXU¥¥¥››!d÷îÝE±=? F€¿/Žž>>VVVãÆÃ²hÃx{{¼ú½ £ð7)))½{÷~ôèÑŽ;Fw¶l}}}BÈ¡C‡þúë/¶Çá4Ä(üWJJJ¯^½¼¼¼ÒÓÓ !Eíß¿¿k×®HÒ Ôÿõ×_‡f{NCŒ@Õ 544\µjÕÔ©Suuu‘¤ 3dÈ–-[©ÿÄ(€V«’¡Í›7}üøqhhèæÍ›³³³§Nª§§‡$­/===___BÈñãÇ_½zÅö8Ü…ÐR5fèƒ,X “Éè}Ú¶m»y󿬬,$iÐïS_ZZºoß>¶gá.Ä(€ÖQ%C•!I¦ÿþ}ôÁ‘ú:!F´H}3T’´¾Äbñ˜1c!gÏž}öìÛãpb@+4&C•!Ië…>R_QQ‘œœÌö,…8ue¨2$©Š>þøc[[[‚÷©¯b@°4‘¡Ê¤$‰è½|ùr~~>Ûãpb@€4¡Ê¤u£c”¢¨¤¤$¶gá"Ä(€ 0™¡Ê¤µéÔ©S§NŽÔ×1 le¨2$iè˘nÞ¼™™™Éö,œƒà=.d¨2$iþþþ"‘ˆ`q´&ˆQãZ†*C’*ØØØôìÙ“àÕïk‚à%.g¨2$)¾Œ)++ëúõëlÏÂ-ˆQžáK†*C’Ž3F,©¯1 À|ÌPeÚœ¤}ôÑ€!III•••lÃ!ˆQà{†*ÓÚ$¥Ô\¼x‘íY81 ÀiBÊPeZ˜¤¾¾¾zzz—1ýb€£„š¡Ê´*I[¶l9dÈBÈÞ½{ËËËÙ‡+£œ£ ªL{’”>R_XXxæÌ™ê½sçý_\« F8DÛ2T™6$©¾¾>©éHýo¿ýæééillÌÆ\lBŒp‚6g¨2a'©‘‘‘··7!dÿþýïÞ½SlÏÌÌôôô|ñâ…µµ5{Ó±1 À2dhuNRú}êÿüóÏcÇŽÑ[²³³===Ÿ={Ö¦M›&Mš°: £¬A†ÖMI:lØ0ú?.}¤þÁƒžžžOžŒ‹‹KLL,++#„ˆD¢‘#GFGGwéÒ…íѪºuëÖàÁƒ‹ŠŠ”7ŠÅb‘HT}ÝtÇŽŸ}öƒÓÕƒT*---‹Å5.÷6VF4«¡êÅ£UÒ.]º¤¦¦š››+o¬¬¬¬1ép˜Ô ª9|IÒŽ;¦¦¦~ôÑGÜ1 jƒ e/’´C‡çγ´´¬c©TªJ° b@Í¡Ìã~’¶oßþܹsmÛ¶­m‡¶mÛŠD"'â Ä(€Ú CÙÅñ$m׮ݹsçlmmkû¨Š(‰-Z¤Ørúôi‘HôêÕ+5LÉ8Ä(€ C¹ƒËIjeeuîÜ9úõ^ª¨×ë:I¥Ò•+W>þ\}£±1 Ð(ÈPnâl’¶iÓ&55ÕÉÉ©Êöz]½4pà@+++åÅQ…÷ï߇……µnÝZ*•öêÕëüùóWó£ „ å>n&iëÖ­SSS•7Ö+FÅbñ7ß|³qãÆTùPHHˆ\.ß±cÇÝ»w{÷î=tèÐÌÌL5 ­1ˆQ€zC†ò “ÔÔÔ455µk×®Š-õ}û%//¯Þ½{GFF*o,..Þ¶mÛòåËlcc³zõj›õë׫ghÍ@ŒÔ2”¿¸–¤-[¶ò÷÷ ¤o———Ož<¹Y³f"‘ˆ™gG’jb´ˆ 3ÔÏÏoÊ”)lOÁ¨C‡ÉåòóçÏ¿|ù’ÉçE’jb€B|||ÜÝÝ•_élóæÍÆÆÆ?fq*P#Žg(ýBB ¢‡ZZZº¸¸3ÿìHR5£„èþýûôO¸\.Weÿ§OŸ¶hÑbÍš5ôÝüü|##£íÛ·kpD`ÊÑ£G{öì©øÃGgè«W¯Øž‹òòòšvìØ‰'Råëë«øze2™z¿®úzðà¤ô<"‘hÔ¨Q7oÞdw*MhÒ¤ !D,«ý‘£ LõQŠ¢äryÓ¦M³³³)Š4h··7EQ .´²²ÒÓÓsrrÚ»w/½srr²£££®®n³fÍ  ü{¸ƒ³JóòòÒÓÓÛ°aCyyyqq1EQsæÌiÙ²å¡C‡wÉ’%:µX·nêûxyy‡‡‡ß»wï‡~ÐÑÑñòò ½{÷nrr²žžÞž={(Šú믿d2ÙŽ;èÏ*--mÕªÕúõë_½z%•J-Z”ŸŸŸŸŸ¿ÿþŒŒŒº'WÄ(EQ+V¬pppPëFц$EŒÔOb”¢(??¿¾}û®[·®E‹Ož<¡(*,,¬sçÎiii=JLLlÚ´é•+W õôô rrr¶mÛ†àŽg(ÍËËËÍÍMq·¤¤D*•®_¿^y‡¡C‡Ò·UÑÒÒR##£#GŽ(¶$&&ZZZÒ· ïÔâÅ‹ªïãååÕ§OÅSôëׯK—.Š»ÿøÇ?¾øâ úvpppÿþýéÛÉÉÉM›6}õêUNN!äÚµkÊߺ'çrŒÒ„¤š‹QÝÆæõë×wìØqæÌ™;wîlݺõ›7o~þùç?þ˜2qâÄ´´´;v|ùå—eee#GŽ´°° „ØÚÚ²=8ü¿Ó§OGDD\¿~¾kddÚ¬Y3v«Q§N·³²²Þ½{×·o_Å–þýû¯^½º¾yÿþýââbooo劫ÎMLLLLLê~Uö!„(¿C¦©©iÛ¶mwÍÌÌ^½zEßž6mZçγ³³íìì¶mÛöé§ŸÊd2™L6bÄ777~ýúùùùÙÙÙÕ=9÷Ñç’Ο?ñâÅ[·n¥(jÿþýpuuýüóÏ­­­Ù°Q*++5ôȈQ€ÿgbb´k×®€€BȽ{÷JKK•W!£Frtttwwwtt2dÈÀýýý[µjÅÒÈP•……E‡nݺE¿ƒvIIIvvöÓ§O¹£ººªþ!®Òd”Ò—UÐÑpãÆ —ê]¼xqTTTŸ˜¬â>„å‰Å»*Z1a§Núôé³uëÖààà“'Ož?žÞ~èСK—.¥¦¦;v,**jÏž=ô¿ík›œ/òòòè3¾è»E]¼xñâÅ‹ìN¥.šHRÄ(Àßèêê*þ6ÐÿË=yò¤uëÖUv;sæLZZÚÙ³g7oÞžžŽõQŽèСÃ?üµhÑ¢¤¤¤ŠŠŠ]»vÉåòqãÆÍŸ?ßÁÁíkeoo/•J/\¸àììLo9þ|çÎéÛÆÆÆŠµFBHnn®â¶ŽŽŽr"8889r¤Æ¤›:uj•¥GúhŠûÔKPPPDDD“&MÜÜÜÛ]]]]]]###}||vîÜ)—Ë똜ûÒÒÒbccÓÒÒè»R©ôÝ»w¬NĈQ€Z999I$’£GV?±XìááááákccsäÈ‘V†€ñ1Iõõõg̘eiiéììüã?¦¤¤œ8q‚þ¨‡‡Ç¦M›ÂÂÂÌÍÍ÷íÛwâÄ EÕY[[Ÿ>ÞÎÎNWW·yóæÃ‡¿qãFVVÖàÁƒ[´h¡££Ó®]»øøxuêwïÞ½€€ÅiŽ:::ãÇÏÌÌd{.m1zôè1cư=…:Ñݯ*CCÃðððÂÂB¶çÒÅ9ÁjdUûÐü•••E¯yÈår¶Ç®ÈÌÌT¬’Bttt8»J*/_¾üå—_FŒ‘ššÚ¯_?¶ÇQƒºWC…Jqj²ÚÓo Z„>pŸ‘‘A¯’Òî;vìøÙgŸ)ލ€zuíÚu̘1ááá(Ñ´´´8.Qz54//oÙ²eÂ.QBŒ€ÖA’2éáÇÅÅÅqqqlÒ(ÈPÍAŒ€–B’‚*¡š†­†$…Ú C™@’Âß C™„ø/$) C™‡ø$©vB†²1 P$©ö@†² 1 P+$©°!C¹1 ðHRáA†rb@%HRa@†r b ¤ü… å&Ä(@½!IùÊeˆQ€B’r2”û£‚$å&d(_ FÔIÊÈP~AŒ¨ ’”]ÈP>BŒ¨’”yÈPþBŒh’”ÈP¾CŒh’Ts¡Â€Ð8$©z!C…1 À$iã!C…1 À($ià C… 1 À$©ê¡Â†` ’´nÈPm€`’´:d¨ö@Œp’”† Õ6ˆQÑæ$E†j'Ä(çh[’"Cµb€£´!I‘¡€à4¡&)2hˆQR’"CAb€7øž¤ÈP¨1 À3|LRd(Ô1 ÀK|IRd(Ô 1 Àc\NRd(¨1 À{\KRd(¨1 \HRd(Ôb@PØJRd(4 b@€˜LRd(4b@°4¤ÈPh<Ä(Ä“'O&L˜››[Ûoß¾ýôÓOïÞ½ËäT\ ‰$E†‚º FAÞ¼ycmm½sçΨ¨¨ÚöY·nÝÞ½{;vìxíÚ5&gàu%)2Ô 1 B`hh8nÜ8BHRRÒ½{÷ªïð×_­X±‚âììÜ­[7¦çàŒÆ$)241 1þ|ÊÊÊE‹Uÿè†  ! ,‹ñcÚ®¾IŠ ÍÁ_e{{û€€RÓâ¨ò²èèÑ£Ù™€{TIRd(hšˆ¢(¶gP¬¬,''§ŠŠŠqãÆÅÄÄ888BärùãÇ¿úê+BHrr²ŸŸÛcpQffæ¢E‹’’’***!b±8$$äСCŠ C §OŸ†ÕNôáGBˆÚÓ1 ‚2a„;wŠÅâ#GŽ >œ’˜˜^XXèìì|óæM£¨C•$¥!Ch2Fñ‡Eqæèºuëè-§NÂÙ¢*R¸wuu¥·á €6ÂÊ(0‡-“••5aÂ]]]ú€ÎÐß~ûí¹@UOŸ>U$)mÈ!/^d{®óóóëÛ·ïºuëZ´hñäÉŠ¢ÂÂÂ:w–öèÑ£ÄÄĦM›^¹r¥°°POO/!!¡   ''gÛ¶m%%%lÏ uÒÓÓéß0ÇŽ«c·O>ùdÀ€•••Š-›6m’ÉdšŸ%‹5”ŽˆQ-‚ >&iaa¡‰‰‰X,þá‡(Š*..nÒ¤Izzºb‡ÀÀÀ3fdddBð— €]W¯^¥·¤¤¤Ô±ÛÓ§O[´h±fÍún~~¾‘‘ÑöíÛ™˜……FA† ï’422ÒÚÚš¾­XwQ6jÔ¨ŠŠ wwwCCÃÑ£G'$$±:2€–ºvíýåÑ£GëÞS.—7mÚ4;;›¢¨Aƒy{{SUQQ±páB+++===''§½{÷Ò;''';::êêê6kÖlÀ€8îÁ#ˆQh d¨6àQ’FGGÛÚÚÒ·ùåB}¼¾ŠŠŠŠ3gÎDFF:;;·hÑ"''‡Ù1àÿcôÈ‘#Ü'áhÄ(Ô2TÛð"I•côõë׉dË–-uì_^^neeµzõjF¦€ÿ÷믿ҿI>üÁqŽ6Ð\Œâjz•òÚ‰wWÜÍž=û«¯¾ÚµkWAAÁo¿ý¶jÕªƒÞ¸qcÅŠOŸ>=räHaa¡ÛÃhz½“‰‰IPP¥¥e@@!äÞ½{¥¥¥={öýObbâ“'OÝÝÝ}}}¿ûî»çÏŸkl|PŠ¢îÝ»WÇyyy"Ĩ  C_IºtéÒ¨¨¨… ÚØØôë×ïôéÓÖÖÖ†††§N0`€¥¥åìÙ³—,Y2|øp¶'Ð:õ}i']]]ű¸ÊÊJRí$œ}ûö‰Åâ3gΙÚ×Z8(ÕñâÀ=pÓÍ›7éßTeœ„#0kÖ¬¡víÚEo©r˜þìÙ³ô݈ˆˆF>VFy«¡P~­’7QõÑ{œ„#AAA}ô!dñâÅ5.ŽÆÆÆB ÃÂÂù\ˆQC†‚*¤Ð|&œ„ÃwR©4""‚’™™Yý@|jjê¹sç!3fÌhÕªUcŸ¬‘+«À ”磱cÇNœ8‘¾]VV6iÒ$###†ÿÄ{PÑíÛ·éßû÷ïg{`ÇÛ·oéÅÑ:Ðk^Št0`!ÄÐÐP-/•Qžäj¨ŸŸß”)SØž‚Q‡’ËåçÏŸùò%“Ï‹URPÞ›j[Uó²(ÓóÇ3´¬¬ŒíøäáÇ–––...ÆÆÆÌ?;’>1 äïgŽ*6ªñlQb”8›¡ÞÞÞS¦Lùâ‹/Z·nݹsgBHEEETT”……E“&Mœ÷íۧعGÊ?Êaaaƒ "„þôÓOÛ¶m£_‹.--Þ!!!ÁÁÁA"‘XZZ.\¸~¡õ?}úôyóæµjÕÊØØxþüù••• .433322 ¡ÿnß¾ÝØØøíÛ·ŠOœ3gN÷îÝ !)))...‰¤iÓ¦]»v½~ýºŠ“ûùùÍ;7++K$±£4$)¨1ªÍ”GÕ»,JÎå6ŽŸêå奧§·aÆòòòââbŠ¢æÌ™Ó²eËC‡=xð`É’%"‘èäÉ“ôÎÝ»w_´h‘âsçÎëééIßöõõ}Oѯ_¿.]º(îþãÿøâ‹/èÛÁÁÁýû÷§o'''7mÚôÕ«W999„k×®)CêžœË1JC’€ÂÝ»w£@S¼æ¨Bã_[T™®zÖWA}rrr-Zôã?–——BÄbñ˜1c,XàèèÈöh5èÔ©“âvVVÖ»wïúöí«ØÒ¿ÿÕ«W×÷1ïß¿_\\ìíí­¼Qqö’‰‰‰‰‰IÝ Ê>„.]º(n›šš¶mÛVq×ÌÌìÕ«WôíiÓ¦uîÜ9;;ÛÎÎnÛ¶mŸ~ú©L&“Éd#FŒpssóððèׯŸŸŸŸ]Ý“s}à>""bÙ²e›6mzûöíÉ“'Ož<Ù·oß™3gº¸¸°= ¨Ê¢iÓ¦lOü†sFA!((hÙ²eOž<¡ïªñlQb”sôôô¤R©ò (¼{÷®´´”Ý©j£X»ý *MVÇo7ú$Ë7nÔX?‹/ŽŠŠªñ‚ƒƒU܇¢£££ü!Å÷¼Ê„:uêÓ§ÏÖ­[ƒƒƒOž,..Ž‹‹c{F)((˜>}ºÝ† JKKE"ш#®^½zèÐ!”(Ô1 š†å1$)¨ €•Q` b”÷¤Pd(4Þ=ø1*HRP† €FÂÊ(01*(HR@†€Z F1ˆQB’j'd(hb4 1*XHRí µÃÊ(01*pHRaC†€†à&` bT+ I… …•Q` bT‹ I… @Œc£ZIÊ_ÈP`b4 1ª¥¤ü‚ †aeƒÕjHRîC†+p01 HRŽB†‹°2 ŒAŒÂ!I¹ ¬CŒÂùûû6þq£ð7HRv!C€k£ZbÊ”)¢¿“J¥uÊ€úöíKßöóó›2eJÞ1 5@’2 œ‚•Q-ääätCIzzzÝûùå— PeˆQ¨’”ÈPà \À¤…ôõõ]”tîÜ™òâÅ ‹yóæÑûäçç·hÑ">>ž(¦ üé§Ÿ¶mÛF/©¦¥¥Õëy£ðHRÍA†gaeh-[¶ÜµkW||ü™3gÊËËzöì9gÎå}}}}'OžLQEQîîîõz Ä(¨Iª^ÈPà8Ĩúõ×_•ÏU4¥»»{DDÄgŸ}š““óïÿ[½ çˆQ¨$iã!C€_£Ú£Ê9£[·nU|(&&æ£>Z·nÝöíÛÍÌÌÔû¼ˆQ¨7$ià C€G°2ª…ªœ3Ú¾}{Ň~ÿý÷œœ¬¬,µ?/bIª:d(ðb***z÷8oÞ¼ÿÊëèèTVV6ìñ£Ð(HÒº!C€§p5½zûöíÍ¿+//'„ÄÆÆÒ§ŠŽ?~ìØ±ãÆ+))©ò¹ÖÖYWD` IDATÖׯ_ÏÏÏþüyYYY½ž1 j€$­ ¼†•Q-t÷îÝ®÷üùóóçÏ/]º411‘>Utݺuååå!!!U>788ØÀÀÀÞÞÞÄÄäâÅ‹õz^Ä(¨ ’”† !AŒj‰­[·RÕ˜››÷ïß¿¬¬lذaôn†††ÙÙÙ[¶l!„ìÞ½;11‘ÞneeuñâÅÒÒR¼´°O›“ ‚•Q` b4BÛ’ ƒÆ FAƒ´!I‘¡ H¸€ ƒj’"C@À°2 ŒAŒC„”¤ÈP<Ä(01 Œâ{’"C@Û FAÓ£À>&)2´ VF1ˆQ` _’ Z0c£À2.')2´VF1ˆQà®%)2´bƒáB’"C§ èÛëׯÿý÷ßÙ1 š†Îa+I‘¡ÀS………_}õ•Í?ü@o9xð`ûöígÍš…$…ÃÊ(01 Åd’"C§ºråÊ¿þú‹âááDy÷î]BB’ 0c£ÀišNRd(ðTzþüù3gÎlÚ´é—_~6lA’B#`eƒÐD’"C§êÈÐ~ýúÑûôêÕ+%%I Æ F7Ô•¤ÈPà)U2T’Ô1 š†žiL’"C§ê›¡Ê¤Ð0XƈðCü•ŸŸ—˜˜øþý{BˆH$òññ‰ŽŽvqq©²gAAÁÒ¥K¿ÿþûÒÒRzOooïèèh4(p\aaáŠ+6lØ@7(!ÄÃÃ#&&æƒ Z£+W®ÄÆÆ;vŒ¾+•J§NѦMµM "‹)ŠŠ]°`Û³@ýüþûï„ÿÞ½{³=NÍîÝ»·iÓ&B¡xîáÇô*)ýÃ-‰FŽyãÆ ú£=úòË/›4i¢øèˆ#®]»ÆîÌôìÙ³°°0Å/nz5´ñ¬X%¥I¥Ò™3g>~ü¸ñ #‹ !111lõöøñcÆšR Øþv¨GI:þ|]]]Åd(ð‚æ2T’>HGG‡Íö PoüŠQ¦A©ràž†ƒòÀê=(¯ ¸‡:èêêVTT,X° 66–íY ~(Š*..f{ U!FA€òóóçÍ›·{÷nBH·nÝ6oÞŒ Žc>C•!I¡FzzzåååQQQ .d{2\Mdmm½xñbúöܹsQ¢Àe¹R^]pÅ=ÔoÂÌ@Œ‚0ÑçÝB*++Ù 6\ÈPeHR¨‚ŽQAMCŒ‚0)þAâZ†*C’‚b˜aR¬Œâ×(p —3T’ð[4 1 „ÃôÀ5|ÉPeHR-‡•Q`b„ ‡é;ø˜¡Ê¤Z 1 Ì@Œ‚0ae¸€ïª Iª…p5=01 „sF]BÊPeHR­‚•Q`b„ ‡é-BÍPeHR­‚MÓ¢íÛ·ïwß}×ø}€p˜˜§ ª I*xXfhQŒªbøðᎎŽlOj€ÃôÀ$mËPeHRCŒ3£óõ×_{zz²=¨VFÚœ¡Ê¤‚„ ˜€ŒQ—ÈÈHOOOkkk;;»cÇŽUßgË–-ÎÎΉÄÔÔtΜ9åååôvåÃô...‹-òòò255µµµ={ö,s_4ÎMC†V‡$¬Œ3£„õëׯ^½:??ñâž¾¾=ª²ƒ®®nBBBnn®\.?zôèªU«j{œ¸¸¸ÂÂÂÏ>û¬¢¢Bó³ƒzà0=h2´nHRÁ@Œ3„£ÿüç? !cÇŽíÔ©ÓîÝ»«ìðù矻»»[ZZzzz~ýõ×û÷ï¯ñq>ÿüó.]ºB¦NúôéÓœœMOê‚Ãô  ÈPÕ!I1 š&ÌíØ±£âv§Nrss«ìðóÏ?»ºº6mÚT$>{ö¬ÆÇi×®}C___"‘¼zõJCƒÚá0=¨2´a¤¼†•Q`†0cTq(!¤¬¬LWWWù£%%%#FŒðññ)((¨¬¬LNN®íø{•s·ñ?$`eÔÚxHRžÂLÀ aÆè•+WèE¥§§+/”B²³³‹‹‹¿úê«–-[ŠD¢ŒŒ 6fÍÂ9£ÐxÈPõB’òVFŒѣGîÚµë÷ßÿú믟={6fÌåZYYéêêž8q‚’‘‘±iÓ&–Æ ÂÊ(42Ts¤<‚f3FÃÃÃwíÚekk»ÿþƒ¶jÕJù£Í›7ß¶mÛ”)S¬­­gΜ9gζæÍÁ9£Ð0ÈPf Iy1 š&Þ™‹‹Kppð”)SØØDQ½8ºhÑ¢ùóç³=ð@aaáŠ+6lØ@7(!ÄÃÃ#&& ªiW®\‰U¼&´T*:ujDDD›6mØ LMM‹ŠŠ‚ƒƒØž„L˜+£"‘ˆ^ÅÊ(|VCÙ…URÎÂLÀ Ä(b>ÊHRÂ9£À ÆèÍ›7qŒÈÿ®a¯Q¨2”›¤œ‚f0FhtŒbeª@†r’”S£ iˆQ,¦‡*¡ü‚$eVFˆQ,¬Œ‚2”¿¤,ÂLÀ Ä(΂ %„âííÊö‚$eVFˆQ,¬Œj9ehii)Û#°IÊ0Ä(01ú_#GŽäàÊ7§â œ3ªµ“¡ƒ ž0a‚©©©··7!äýû÷aaa­[·–J¥½zõ:þ¼bg—Õ«W+îúùùB=ºfÍúÅwsrr(ŠZ¹r¥­­­D"±µµÝ¸q#ó_Zc I†MÓ–ýàQ*ž={6òA¯ÊS¨2Ô‡éµ`2TaË–-C‡ýã?8@ ‘Ëå;vì¸{÷nïÞ½‡š™™Y÷#$&&zyy…„„PEQTûöícbb¶nݺqãÆ¼¼¼øøøÈÈȤ¤$F¾uÒ’$ ‰D‹-Rl9}ú´H$zõêÏŽ•Q`†¶Ähèƒ_³fÍ P㪅§ÒB8L¯U„—¡4OOÏþóŸb±ØÀÀ ¸¸xÛ¶mË—/ÜÒÒ²]»v­[·®qOåƒ_ÊÛKJJâããW¬X`eeÕ£G   UžZíGÓª>|¸!ÄÞÞ~èСgÏž%„¼~ýz×®]ñññ®®®mÛ¶ýî»ïd2Y#ŸHpΨ€iC†VG®¹té}—¢¨K—.uîÜ™¾+“ÉþüóOÅÎyyyŠÛ:::Š6uttÔ××?|ø0SS3MIêååÕ»wïÈÈHåúCS~‹‚¦ -Fííí‡ Ò£GŸo¿ýöñãǵí©|ðKYffæÛ·o=<<êûÔj?šVâvóæÍéس²²ÊËË{õêEo×ÑÑéÞ½{#ŸHp˜^´3CiFFF“'O?sæÌƒfÏž››;}útú£LJJzùò%EQ6lP^ì´²²ºzõjAAÁóçÏ¥RixxxTTÔ¦M›òóóïß¿¿eË–Í›7³ô5iŠð’tùòåIII7nÜPlÑÐeXf-FE"ÑñãÇ?Þ­[·Ý»wÛÛÛ+VªPýà—ŠÔ~4탟ˆ_uÃazÑæ UX½zµ¿¿@@@‡._¾|âÄ úCaaa;wn×®]ûöí è“×i!!!ååå¶¶¶&&&yyy111Ë—/_³f]Ïž=år¹ò?t…DHIÚ«W¯Q£FEDD0ù¤ˆQ`†šƒŒ D"Qÿþýû÷ïÝ£GäädWWWå£TuëСƒ¾¾þÙ³g«ÿvVýAGÓÚµkGþw4ÍÓÓ“þhõ£i}ôQ}ŸBù¹tuu¯\¹BÁ¯¨¨øõ×_½¼¼êõ ‚„Ãô‚QXX¸bÅŠ 6Ð Jñððˆ‰‰|ƒž>}ºÊ‰D_}gƒÝ»w×ø8íÛ·¿zõªò–3f̘1C]sr¤W®\‰=vì¤[¶l™:ujDDD›6mØPUqqq;vìÚµ+}·î?4j«éB[½uëÖ7ß|sûöí§OŸž:u*//ÏÞÞžüý(Uݵ״iÓÙ³gõÕWr¹¼  àúõë[¶l¡?¤úƒ¨åhšŠUÚ¬Y³ñãLJ……]¾|9???88X¹tµVF«¡ .X%µ··Ÿ}:}ùQ•£Tu?ÈâÅ‹ÿõ¯EFFÚØØxyy)^Å©^¢–£i*~ÕëÖ­sss;;{üøñ;wîd{2¡­Œj§[·n>|øÙ³g<˜5kÖÓ§O}}}ÙŠ}8LÏ;X &ñ}•”8LÌ@Œ Ayyù¿þõ/+++GGÇ_ý5%%¥¶WWÕ*8LÏ#ÈP` ’´¸€ ˜‚îÝ»gdd”––¾{÷îÒ¥KøãMÃÊ(/ C ¤5ÂÊ(01 ‚…—vâ8d(p ’´ Ä(01 ‚…Ãôœ… .C’VMCŒ‚`á0=!CYçíí=þ|¶§à$)ÁÊ(01 ‚…Ãôœ‚ Õ´AƒÅÄĨ²[·nÝ4?Ž@hy’â&`b +£ å”ÐÐÐÑ£G³=Ïhm’be˜ÁÂ9£¬C†~Peee\\œžžž¥¥åš5kèíïÞ½›5k–©©©¾¾¾‡‡GFF½ÝÝÝ=<<ü³Ï>355511Q¼I}``à™3gbccE"‘H$zóæÍ?þØ­[7©TÚ¬Y3__ßgÏžÑ{*¦¯íÑ!{÷îurrÒÓÓ“Édîîîoß¾eè;ÂaZ˜¤ˆQ`b ‡éY„ UQxxøš5kV®\™———””dffFoŸ1cÆáÇ“““o߾ݦM›Aƒ½|ù’þЦM›Æ_XXxàÀyóæ]¿~’˜˜èééMQEQ†††E-]º4++ëìÙ³oÞ¼ ¬q€­¨¨( `úôé<¸~ýú„ ˜ø^ð„&)~‹‚ÆQ MæÎ;wîÜ»wï²=zôèAùä“OØD»<{ö,,,ÌÀÀ@ñK†ÎP¶çâ¢×¯_K$’¤¤¤*Û_¾|©««{àÀúnii©©©éºuë(Š0`Àĉ{öìÙsåÊ•ômå­"''G,SåååIo¯íÑè…Ø‚‚‚ƦHRšT*9sæãÇÙžKm:wîLñõõe{8¬Œj‘7oÞlÚ´)>>ÞÉÉ©{÷îk×®-**b{( Âaz†a5´¾îÝ»÷þý{ww÷*Ûïß¿_^^îêêJß•H$Ý»w¿sç}×ÖÖV±góæÍ_½zUãƒ_»vmàÀFFF"‘¨}ûö•••5þÿ^ã£9::º»»;::úúú~÷ÝwÏŸ?oø)h5®’ZXXˆD"± ܾ}›òÓO?°ýÍ!CŒj‘çÏŸ÷êÕ‹N´ëׯ‡„„´iÓfÔ¨Qxÿþ=ÛÓ©Ÿ01èÝ»w}úôQd(!ÄÝÝýèÑ£ÈPµ«r3UÓ!Ô÷ïß>¼{÷îÙÙÙOŸ>%„TTT¨øhb±øÌ™3tttܼy³ƒƒCnn®:¿aéÕ«×”—H‰PŽ:*9Œ_A‹è²=0§mÛ¶§OŸ~ôèÑÎ;wìØ‘••UVVvàÀ´jÕ* `âĉBzÍ1ÎeT*ÍÉÉ9xðàÂ… oܸAIKK³´´œ3gNpp°‘‘Ûr‘£££D"IKK3fŒòv]]ÝË—/òÉ'„÷ïßÿúë¯ÞÞÞu?šŽŽŽâŸ^<(**š7o^«V­!ôúV½ˆÅbØØX›#GŽ„„„Ô÷A´AIIÉÆW¬XñÇÐ[lmm»uëæää$€×EZµjÕŸþÉö |ˆQ­ceeùõ×__¾|ùßÿþ÷ž={^½zõüùóµk×þ{wÓþÿüsZ¦½öˆJ;¡$r[”\Š”(.‰l]Q’5-Zì!;éJ¶\ûÖµDö%ß,éÒžD¨Üö25Óùýq¾÷üæÛ2¦šæÌLïç3gùœ÷ŒæÌk>Ÿ³ìÞ½{ðàÁsæÌùí·ßTUU©®´« g”Ç0 ›2eŠ‹‹ IËËË×­[·cLj¤m’““[ºtéÒ¥KÅÄÄ,,,>}úôáÃOOÏ^½zyyy-_¾¼OŸ>ªªª†Í˜1ƒ}kýû÷þüyII FÓÐБ‘¹víš··÷·oß:z•ûW¯^¥¤¤L˜0AQQ1--­´´tРA]x¡Â©u 566 ™6m±óG…0 x@H>0 £0 =zô¡C‡JJJNŸ>ýë¯¿ŠŠŠ"„Þ¾}Ô¯_?ggç³gÏÒétª+í¼N3êííaXdd$9%%%ðöË­‘4==ýâŋÆ C‘T[[{Ó¦M555TÈ_¶lÙâçç·|ùòLŸ><¬sß¾}'Ntuu511)..NIIéÝ»7û¦ËÊÊ´´´”””BÇ×ÔÔtrr ìPU²²²·oß¶±±éׯßòåË£££'NœØ¹(”êëëwìØ¡££³bÅ "‰'%%eddxxxM€w(> ðÏŸ?oÙ²ÅÈȈõÏ£wïÞ¾¾¾Ïž=£ººÎ°¶¶F988p¾Êœ9sˆë2–••Snß¾ª¨¨èž…\ss3I }ûöݸqcuu5Õ¥Ðuuu111¬GD e2™T—Ö- @¼Ì¢¢"ªk ~ÀÿRWW_µjÕßÿ––¶dÉEEE„PEEÅ,-- 7oÞüéÓ'ªËì€Î ÓÛÙÙõïߟµs”ÔØØ¤¦¦&))9räÈp§Pὤ@hôÌÞP!8ìáüü€®1bÄÞ½{?þ|îܹI“&‰‹‹#„²²²Ö®];`À€ñãÇŸ:uª¾¾žê2®s—vÙ¼yóÁƒ [Ìò÷÷?}útbbâ»wï,--ÇŸ••ŵr…DR Ðzf €§¨îšüîëׯ;vì055eý³‘——Ÿ?þÇ›››©.°]ööö!ÎW™3gŽ““ŽãÖÖÖ3fÌÀY†é«««ÅÅÅOœ8A,ÙÜÜldd´téÒn(\˜ÁÀ= =mP¾µ/†éA·‚Ÿtà'TTT–/_þúõëW¯^(++#„ª««9òË/¿èééEFF~øð¡Óí×ÔÔtâº3œèÊÙô[·n=sæ q‰"q%,ò:ä†YYY‘×!‚^R  7”Ãô€7zÊ' tÝСCwîÜùéÓ§Ë—/»¹¹Ñh4„P^^^hh¨ŽŽÎرc;V[[ÛÑfeeeýõ×îÈ£]¹ÓÈ‘#]]]W¯^Íí¢BIƒ ïÁç tŒ¸¸øäɓϟ?_RR²gÏâþïÍÍÍ©©©ÞÞÞjjjÞÞÞ©©©œG@ 䤤ÆŸ——ÇÝR»xÑ7¦¦¦Ãô!===öäÉâ)ŽãOž}âbaÄ•---9_…+ò+¶¦OŸŽ211ábÙ€rÂI=<<ŒŒŒ‰§ööö&L`] õ”síííɧûöíSVVn½Xuu5†aœ´ÙzáñãÇ“sûõëçééI;vÌÇLJ'‚˜˜X‹…[OaÅ`0Z<c2™ÄÁ §NJOOýúµ¨¨(“Él³ÍŸ.Ü"´µxŠãøO_/ïñy mmÀ€^^^GŽÉÍÍ=|øp›÷666¼/Œw`¼ÁwŸLÐCà8ÎÉ ,¸xñbG7A|ñðçW&à ÁФÍÍÍ ,¸råʃ ºØygZ„ÐãÇ(''WTTTTTeff¦®®^YYÉ.[èÐÂüOàbh ="Ž’o}ä’µµ5%%!£€Wøýó „ƒÁ˜;wnll,çËϘ1#==½C[žÑBP"©——ןþ¹{÷îÚÚÚׯ_¿~ýúÛ·on-;;;44ôÓ§Oׯ_ߺuëÒ¥KBªªª²²²×¯_GUUU° Z˜Ÿ z Eåç绺ºÒét ‰Ë—/kjj’³¤¤¤,,,(¬ €O)2t:}Ú´iÇŽk=KDDDEEÅÌÌlòäɾ¾¾ÑÑÑ )))ïß¿/++333ëІà˜Ñ…ÿ#iRRRMM«î|y IDAT«ë°ÅÅÅuºµ ”””yyyùùù „¤¥¥Ož<¹sçN kkkÖ^ Z˜? A EUVVNš4©¼¼ð¸¸8;;»Å‹“sGŽ)!!Aayðƒ˜€—jkk§OŸþþý{ MMMò_ uuu.~Î;7!!A[[;??Ÿ[m€ãøåË—#""^½zELéÛ·/qÕLöWoæææ›7o¦ºÊÔ××=22’˜¨¬¬ìééI Qxö‚cF¯ÞG4^Ž7Á0}Çÿ÷ C„cPžULLÌáÇB#GŽLHH`M~ÄAÀ4ÍÒÒ’²úàÁûôÀ!èHH#iJJJ£¾ŠºråʪU«BZZZ—/_–’’bkff6jÔ(sssiiiŠ DzF¯ägNÀ¥I(#iO ”1!ôêÕ«ß~û­¹¹Y^^þêÕ«***­—Yºt)µcôðŒ ~’ø)¦-@$ ÂCB%%%“'O®­­MJJ"omÕ´iÓÜÜÜx\”àÏ3ìÁ0=hDR>'Ä1!TWW7yòäOŸ>!„víÚ5a„ö–377çaim€azÀ‚ý©€ ¦l@$åCÂCBÍÍͳgÏ&îßáçççççGuE?að†À¶hôŒ‚Ÿ‚HÊ'„>†Ö¬YCÜÙx„ »ví¢ºø…|Âh Ž‚HJ¡CBñññÛ¶mC <8))ITT”êŠàÂó9 ¦‘”ÇzN Eݽ{×××!¤¢¢rõêUyyyª+â ÓÞªO;¬`˜tDRèQ1!”íîîÞÔÔ$%%uùòe---ª+â„QÀÂö™€Ãô Ó ’v“žCBß¿wvv®¨¨À0,!!aäÈ‘TWßÂO>è]‘”‹z` E566º¹¹ååå!„"""¦OŸNuE=£€7„óó‚cF—@$í¢žC .|ðàBhöìÙÁÁÁT—Ÿæ½èá`˜pDÒNèÉ1!´qãÆcÇŽ!„~ùå—¸¸8èe =B¾/= Ó®ƒHÊ¡CBgÏž]¿~=BHGGçÂ… TWÔ oÿôX0Lº DR6 †"„ÒÒÒæÌ™ƒãxïÞ½¯]»Ö·o_ª+ê$£€7zÄ~ôLÐ3 ºDÒ †>~üèââÒÐÐ ..~öìYª+€ßõ”½èà˜QÀIÄP555“&M"Þ„}ûöÙÛÛS] í#@OCÓÃH=èn=6’B eÅd2===322BAAA , º¢®‚azÀ=kOzò‹Â(àI!†¶˜œœŒrqqÙ²e Õåp„QÀ=qzr7 #õ€—„>’B mÓ¾}ûvïÞ>|øÉ“'{ìû@'À§-òËÂ(à=¡Œ¤CÛsãÆ€€„††Æ•+Wddd¨®ˆ; gðFÏÝw¡Ãô€rBI!†²‘™™éááÁ`0ddd®\¹¢¡¡AuE˜½ zFŸèH 1”½oß¾Mš4©ººZDDäĉǧº"OOß!ÇŒ¾"p‘b('6lØðáÄЖ-[¦L™Bu9\Ãô€7`o„ Ó>$‘b(çbbb<<<æÏŸDu-ÜaðìS€Ð‚azÀ·ø6’B í())©¤¤¤P] ö,@hÁ0=às|I!†v…˜˜Õ%t è¼û ´`˜Ê#)ÄPµ`/„ ÓBI$… ؃žQÀ°¯B †éÀáY$… à°ÇB zF€êÖH 1pzFoÀ~-8f4®GRˆ¡þ{ ´ g®DRˆ¡~û ´à˜Q 4:I!†‚®€azÀ°'B †ééP$… ºÂ(à Ø¡Ãô@(ý4’B Ø+¡Ãô@ˆµIûöí 1p ôŒÞ€}ZÐ3 „^ëHÚØØ1 X`„3 z2’nÙ²…˜²nÝ:ˆ¡ ë gð지ЂazУ`fccC<¶±± <Ý[9;;¯Y³†+MÙÚÚFEEu÷*@ Á0=ÇÙÙÙÏϯÍYžžžÞÞÞœ7ÕÑåAkÐ3 xCŒêè.0LŸswwïիב#G8YØÆÆF\\œóÆ;º<€*\£t:]BB‚»mÐ9Ð3 €0ñõõíÖåTát˜ÞÖÖvÙ²eÓ¦MÓÕÕUSS‹ˆˆ`8{öl%%%bL¤¹¹yãÆ  ÑhIII­¤ÓéË–-SVV–””üå—_ÒÓÓ‰é§N>|¸¤¤¤œœÜäÉ“?}úDL¯¯¯÷öö–••USS‹‰‰amª½Í±YôpÌ(üÌÛÛûüùóñññ†avïÞ=b:ŽãáááÊÊʲ²²^^^?~ü ¦³»''':”F£IKK6ìåË—­Ûïèò 5¦¼ÑcF:´pá¼¼¼{÷îíÝ»÷À䬃º¹¹•––£-ÁÁÁ§OŸNLL,** ›?þýû÷[´¶dÉ’«W¯^¸p!++ËÀÀÀÁÁ¸‰ˆˆÈ¶mÛ =z„aØŒ3ˆåýýýŸ>}zÿþýgÏž={öŒ ¯l6ÇfÐÀ0=ü,!!aêÔ©>>>8Žã8nkkKL?}út]]ݽ{÷Ο?íÚµíÛ··X±ªªjêÔ©îîîyyyYYYaaaìGä:º< A¼Ñaú_ýuܸq!}}ý%K–>|˜™0a‚««+BHFF¦¡¡!66öÎ;£FB͘1ãÁƒ äiž¡êêêcÇŽ9sf̘1¡ܸq#!!aÍš5žžžÄ2êêêÐÐÐ(--•––NLL<þ¼™™B(>>^UU•X¬½Í™™™µ· è!`˜Ad``°mÛ6„‘‘ÑìÙ³ïܹ³~ýzÖÊËËüø1a„þýû#„ˆÙèèòë@Ϩ‰‰ ùxðàÁ999äS###òqVVVCCƒä¿âãã‰^ORNNƒÁ â#BHLLÌÂÂâï¿ÿFýç?ÿ;v¬¼¼<†a¡oß¾åää466Ž=šX^^^ÞØØ˜ýæØ¬z¦@ :”|¬¦¦VYYÙbI“&YYYMœ8qÓ¦M¹¹¹ììèò€=£€7:F›ššX‹‰ý¯ª¨¨(ù˜øâÏËËûñ¯ÆÆÆ¿þú‹µ©Öß8ŽcÖØØ8qâDSSÓììl&“YQQb2™L&!ĺEòÉö6ÇfÐC@Ï(‚¨ÅRÛ<ÌæÊ•+wïÞµ²²ú믿 /\¸À¾ÍŽ.ॄÑgÏž‘Ÿ>}:dÈ6300””¼v훦ôôôÄÅÅŸ~üøÍ›7úúú\\Àc£¾¾¾ïß¿×××_ºtiTT”‡‡G{KFFFFDDlÚ´IWW×ÊÊ*99yÀ€-–Ù½{·««ëôéÓ³³³SRRTTTdddŽ?¾yófMMÍñãÇûûû“ËÇÇÇ+)) 8ÐÀÀ€N§[YYýtslV= Ós…‹‹‹­­-kïòáÇ{õêE^v €Nóóó“‘‘ÑÓÓSRRzüø1‡kÉÈÈ<~üØÑÑQCCÃÝÝ}Ò¤Iaaa\\ gðÆá¦­­­­­mxxx7××ܹsÇÁÁ!ôøñcòT6ÐQ_¿~566 [¶lBèãÇ&&&»wï†-ò¡çÏŸ[ZZ"„nÞ¼éèèHu9@à9::Þ¾}!TTTW!݇§÷¦€— g”+TUU÷íÛ·víÚ¼¼<„··wsssdd¤––F366>þ<±ü¹s猌ŒÄÅÅlmm(-ÐyÐ3 xîM„3Ê-žžžçÏŸŸ;wîŒ3^¾|Iœ¸zõê[·n%&&jkkß½{×ËË«_¿~œ9sæŽ;¦L™B§Ó[ßíhÓ0JÞ¨ AgÓsÑþýû—.]züøq55µÚÚÚ={ö<|øpĈ¡9sæÜ»w/11Ñ××·©©iÊ”)ššš!ª tôŒÞ€az ´`˜ž‹”””.\د_¿™3g"„Þ¿O§Ó-,,°%$$”””ÚÚÚN:uïÞ½åååT€ßAB †é¹KLLŒ¼‹ñ––””à,.\¸ ""rçÎË—/>|X__???ŸÒª=£€7 Œ¡=£ÝÇÈȈF£]¿~½õ,‘±cÇFEE½zõJVV–ýý/ü Â(à 8 -8f´ûÈÉÉ-_¾|åÊ•’’’666ÕÕÕ·nÝÒÖÖîß¿JJÊ„ ÓÒÒJKK Du±øZ÷öŒN™2%  [7Ñ ÝZ•§§çâÅ‹»©qÐ!Ð3Ú­6mÚ¡­­ýË/¿¤¤¤hiiÉÊÊÞ¾}ÛÆÆ¦_¿~Ë—/ŽŽž8q"Õ•€œ×¬YÕ¦:zÉíN¬@Ò¥žQggg]]Ý]»vµ·ÀرcûöíÛÅFº®Å&8© 8f”»ÂÃÃY¿P1 l±Ø­[·xZ ÛÀ0=àÓéÄ-[¸Ø WšâbU€ŸÁ0==¿#<Æé0ýåË—MLLÄÅÅåää,--KJJ¼½½¯_¿K\Ø%//ÏÁÁÁÏÏÏËËKYYÙÙÙýï€8Žã;wîÔÓÓ£Ñhªªª+W®Dµn„u£­lll RSS“””9räƒÈ…‡ÊÚ½êîîîçç׿&X«rppð÷÷_´h‘¦¦fŸ>}V¬XA—úúú¹sçÊÉÉ)++‡††¶7þN§Ó.\(//¯¬¬Üb †Mµ8Žoß¾]GG‡F£éèè|xóæÍ´´´²²²äää6Û_¾|ù­[·’““Ÿ?þáÃÖÅØT~äÈ‘ƒÄÄÄŸ9s¦½·šý íazø\ssstttvvöÇétúŒ3Ú\ìàÁƒsæÌ)**º{÷îž={öíÛÇ:ËÍÍ­¬¬,>>!râĉ?þø£   ,,láÂ…wïÞmÑÚ²eË.^¼xæÌ™ÌÌÌAƒÙÛÛûöM1>¼sçΣGž}ñâÅ8ŽWUU‰‰‰]ºt‰˜Î`0455-ZÔ¢ÂÚÚZ ‰?ÿü“xJ§Ó•••‰ÅØT[__/--ššJ¶îàà€·ÿVƒNÈÈÈ þÈ/^¼Hu-ð³gψ¿ù›7oR]K‡}øð!TZZÚbº““ù4,,lðàÁä,WWWr±k}úô)9å÷ߟ5kŽãNNN«W¯ÆÿÝ3Ÿ;wŽX ©©ICCcË–-íS[[K£Ñ®\¹BL¯ªª’–– c³96«bL!TTTDu-@˜qt̨žžž£££¹¹ù¸qãlll¦OŸNÜë¯5##£6§gee544Œ;–ÓŒÜVƒ999MMM£G&žbfeeõöíÛŽ¶Ù‚¶¶6ù¸wïÞ•••Ķ ÆÈ‘#‰é¢¢¢fff­×ÍÎΦÓéVVVÄSFÜ‘}µYYYõõõvvv¬M¢Ž¼Õà§`˜>—žžž^SSC|N¿~ýª¤¤Ôb±!C†°>Þ´iù”õ;‚صŽ5ŠuÝ{Ú{f111 bÏÜf1MMMäN^^^~ðàÁì7—ÝÞ*€6q4LaØ7nܸ1|øð¤¤$==½'Ož´¹$yƒná¼ÁÃÙœç+â9Ù…Éd¢ÿ-’F£ýt-¢¶œœÖŸïÞ½Cy«ÁOÁ0=üŒÁ`ØÛÛKJJž9s&==ýáÇèßjë%ÉÇMMM¬»\ÖÇÄ'ýãǬ»ÖÖÃô*†ÍN¾½Íuî{?Áqb€78= Ã0kkë°°°´´4##£³gÏ"„DEE9üš700’’js§Ày#ÄÉOd8ÃqüÉ“'ä/f…ªª*rá‚‚‚Nl‚u[bbbÏŸ?'ž2™ÌôôôÖ‹éè興ˆdff’SÈžZ6ÕJII]½zµÍM·ùVƒN€žQøYAAÁçÏŸ7nÜ8lØ0uuõòòòö–dýMþôéSÖŽRV’’’—/_f³Q===qqq²Aƒ‘––6xðàöŠÑÖÖÆ0ŒÜÉ3 ¢ã€ÍæØ¬"p ŒÞà(Œ¾yófóæÍ_¾|¹}ûvAAžžB¨ÿþ/^¼(...//gÿ}/--MܯåôéÓÅÅÅ/_¾Œ‹‹#fqÞˆœœœÏªU«îܹSXX¸|ùòüüüßÿ˜kggwæÌ™ŠŠ Ç8Àúáç|$yyùY³f=}ú´¨¨ÈÏÏ5é’úôéãææJÌݵkWaaáO«•––^µjUHHÈ¡C‡ŠŠŠ²³³ãââ>Ìæ­—v€Ÿ©©©III7•-//gsQú7oÞlܸñóçÏþùç¡C‡ˆ+¥´&##³bÅŠµk×=zôãÇïÞ½Û»w/qn(INNnîܹ÷îÝËÏÏ_¼xq}}ýœ9sÚ+FQQÑÕÕuýúõµµµ8ŽGFF‡r±Ù›Umâ(ŒÊÊʦ¦¦Ž;¶_¿~óæÍûý÷ß.\ˆò÷÷g0:::JJJ¬=‘mŠŠŠZ»vmpp°¶¶¶““y§5²k×.OOÏ™3g<}úôæÍ›úúúĬ   !C† 8PWW·¸¸˜<캣› íÛ·ÏÊÊjܸqæææJJJãÆ“””l½X||¼–––ŽŽŽžž^nn®‹‹ 'Õ†‡‡oݺ566vРA§OŸ&]mï­=£ð399¹ãÇGGGkjj:::¶1B¿ÿþ{NN޾¾þÒ¥K7lØðÛo¿µ·ddddddäæÍ›uuu­¬¬®^½:`À€ËìÞ½ÛÕÕuúô鯯ÆÙÙÙ)))***lЉWRR8p ACCƒµµõO7Çf@ktqÇq}}ýeË–±Ù]~“ŸŸO\Éëĉl¾½ÏŸ?·´´DݼyÓÑÑ‘êr¸ÃÖÖÖÖÖn§I‰)S¦Ç!õïߟêr€Ðê®;0 7oÞ|üøÑ¢¾¾~ÇŽ_¾|™:u*ÕE {F_¼x!--Mm1ð@NNÕ%@‡AmƒÁX»vmnn.†aÇONNVSS£º(Ðd¥¶8pà £í233c=MØ îÝ»Gu €îa-²gtĈüñµÅÀK­ÏÚ à'=à £@h‘aTFFÆÄÄ„ÚbÐ&N/z€À!ÓÃ%# `/ xÂ(ZpÑ{è £€7 Œ¡E†Q]at+Áû¶öôôôöö¦º  gàÝFÝÝÝçÏŸÏÝ6mllÆŒÃãAL€Žrvv º ~{QÀ‚w6½¯¯/Õ%ÁÃô:.!!Au púmíìì¼pကMMMyyy//¯?~³˜LfHHˆ¦¦¦„„ÄàÁƒ/\¸@L÷öö>þ|||<†a†µwáâ={öèëëÓh´~ýúEDD477#„¾ÿ®©©¹fÍb™¢¢¢>}úÄÄÄ ÿ¦ONN:t(F“––6lØË—/9Ü(è `˜>çàààçççå奬¬ìììŒÂq|ûöí:::4MGGçàÁƒä >>>rrrÊÊÊááážžž‹/&f :t×®]ä’îîî~~~Äc6 ^¾|ÙÄÄD\\\NNÎÒÒ²¤¤ÄÛÛûúõë±±±Ä7/Þþ=£€GpÎ899IJJ;vŒÉd~úôIKK+22’˜¨¨¨xåÊ•ÂÂÂèèh ÃnݺEÌš:uª›f£££ põêÕâââÛ·o³6›šš*&&–’’ÒÔÔ4zôèñãÇ777ã8îáá1gÎÇ+++%%%###‹ŠŠŠŠŠ.^¼˜™™ÉÉFAQ]]Mü‘=šêZm°··§Ñh'Nœ`2™µµµ8އ††êëëߺu«¸¸øâÅ‹}úôIJJ"öññÑÖÖ~ôèQaaá¼yóäää-ZDÌ255ݹs'ÙìÔ©S—,YB}:±ÍÍÍ¥º Ì:0Lommíåå…ÒÐÐððð¸sçÎúõëöïß¿cÇŽI“&!„Ö­[÷äÉ“˜˜˜qãÆý´ÁÆÆÆÍ›7Ÿ>}ÚÉÉ !¤©©¹aÆõë×#„lmmW¯^={öl77·¼¼¼ŒŒŒ¿SËËËüø1a„þýû#„ˆ AÏ(üÏÞÞþ·ß~CÉÈÈ444lß¾ýúõë¶¶¶!MMÍ7oÞ9rÄÃ㦦&11ñÌ™3VVV¡ƒþõ×_?mœMƒeeet:}âĉýúõC 8°;_¥Àƒ½(èV£ºººäãÞ½{WVV"„rrr~üøÁzF‘µµ5ëp ÙÙÙ555ÄÐ ‰5q†‡‡ß¸qcß¾}ׯ_WQQi±ºŽŽÎ¤I“¬¬¬ÆŽûË/¿¸»»4ˆó—„3*è®]»öË/¿(((P]èFFFFä㬬¬úúz;;;Ö B999MMM£F"&Š‹‹1â§³iPOOÏÑÑÑÜÜ|ܸq666Ó§O×ÔÔìúËVFA·êÀ·u‹ŽÉ®ÿi‡‡¾zõе«–˜Høüùs^^ž¨¨hNNN›-\¹råîÝ»VVVýõ—¡¡!y¸*w½|ùrìØ±ÿýww4ºí$è UUUÿý÷ÂÂBªkÝELìÿûDˆýNNë—»wï~ÚH‹¯'ò{„Mƒ†Ý¸qãÆÇOJJÒÓÓ{òä _—p€gotµëHOOORRòÑ£Gä” 2„x,**Ê.[Ð××—••½víZ›s™LæÌ™3---Ö¬Yóúõë6=ztppðƒœœœŽ?þÓvTUUÕÈ‘#SSSW¬XÁ­6oÀ0½ 333ûñãÇôôô¼¼¼8 %@ JII]½zµõ,===qqñgÏžO›šš^¼xAÎUPP¨ªª"Ÿü´A„†aÖÖÖaaaiiiFFFgÏžEÜþtð“ðFWè””Ô’%KBBB®]»VTT´iÓ¦ääd2·iii½|ù²¨¨¨¼¼¼©©©Åº’’’ë×¯ŠŠÚ³gOaaa^^Þ©S§"##‰¹6lÈËË;vìØ¬Y³<<xð`²€ë Œ‚na´,,,ˆ£¹“’’ž?Nu9  S¦L>|8B(&&†õºƒ,--EÐ-*€ÌÍÍ[O¤Ñh...ÑÑÑ7oÞ,///((8{öìš5käååy_$B£¢¢¢õD2Œ6440 ÞV„„ÑŽÙ¸q£””Žã+V¬€_Š|ð 6 „þùçŸØØXbbss3q=WWW*ë׿a£***ëÖ­#®ÎÃûª>?~ôööîׯßçÏŸ‰)­º¡_~ù…ø^,,,œ5ky·-@WŒ1ÂÉÉ !ÿñãGÖcFãââJJJt‹‚nO!**ºmÛ6„Peee‹Ðø„ƒƒƒµµ5ùtݺu¢¢¢Öº‚£äÄ€€âÆÙýõWDDeÅ \ÈÎÑ7’étú–-[BúúúÔU„„ÑNúõ×_Ç:tèPVVÕå€6¿äåågÍšEm1 +ˆs˜¼½½ÕÕÕY§ÇÅÅ#„"##“““©)ábff6iÒ$„ÐÑ£Gëë뉉—.]"N±_¿~=ü°\‡Áõ‰:-33sèСL&sÒ¤IW®\¡ºÐ ‰ÆÆF[[ÛÔÔTªk×ØØØ»wï·oßjkk·˜•maaQ]]Ý»wïÿüç?­tÔ«W¯ÌÌÌp×ÓÓËÉÉA)++—––êéé½{÷Â(à:èí<„ÐÕ«WïÞ½Ku9 %Ç]\\$$$,X@u- Kh4Zttt›AS__ÿèÑ£†UTT¸»»744ð¾<„̰aÃ\\\ByyyÄâš$ÁÁÁDAw€žÑ.ùöíÛ Aƒjjj†šžžÇt󉿿æsçÎEFFfff"„0 suu 555¥º4ÐIÍÍÍl>_«W¯&Îgòöö†‹ÐuoÞ¼6lkBÐÕÕ}ÿþ½˜˜…Ua%JÜËtެ¬,BèÎ;_¿~0`À°a讨§knn>{öìŒ3öïßOü”'¼ÿþСCªªªV:‡ýM\ÇŽûèÑ£ÂÂÂׯ_«©©µyQçTUU322Þ¿ON‰‰‰!n¶ ×AÏhW544|üøQ]]=''GFF†êŠz¨½¡!eeåU«VMœ8qÇŽÇŽ#®6 ½¤Âª´´ÔÌÌìÓ§O< îÂè´ŒŒŒ¡C‡!AKK+//ºEA7aå®’’’".\RRB\ï ðXssóŸþijjêááA$Q•íÛ·®X±ÂÐÐ0...''gþüùâââ8Ž_¸paذaS§N}óæ Õµ®QVV>{ö,F£ÓéîîîeeeTW€`2d9ÈàççIt£\0cÆ ¢fûöíÄÅ/o´C V¬X!--M.9`Àˆ¤BÏÒÒrçΡâââ™3g2™Lª+@°§1!„ÜÜܨ­7¦çŽGY[[ã8çOðFëAy••+Wúúú²fÐ6}øð!::î…Õœ9sBk×®e½j7Ôúðჿ¿?ÕUtLqqñ«W¯BãÆ“’’¢ºœ——?~ü8ÕUŽá€KÜÝÝB"""/_¾¤ºaÆd2Ïœ9CÞ¬ýÛZWWסv ‰^R¢ ÃÜÜÜ^¿~ÝMež©««#~W`vñâEªËà¿`†—úöíKõ8èèåšüü|ccc:ngg—í]é mô’ ¥üü|ssóÊÊJ…/^ 4ˆêŠ@ÄŽEGG§W¯^T—#´Š‹‹KKKûöí Ž ªÓ°PY±bñ®^ºt‰êZ„ ·zCÛ½¤ÂçêÕ«ÄuIMLLjkk©.€ÿï…/ˆnµlÙ2=£‚N`â¦õë×+**"„V­ZEô´.âü¥®€Ó›„³³spp0B(33sáÂ…T— ]F¹©W¯^aaa¡œœœP]Ž`ãM e‘TÈ„‡‡?!têÔ©Ý»wS]€¶Aå²Å‹ëëë#„"""***¨.G ñ>†²‚H*4DDDNž<9`À„PPPÐãÇ©®@ Œr™¸¸8qìïß¿GEEµ·Xee%‹ÔÆPVI…ƒ¢¢â¹sç$%%›šš¦OŸþõëWª+ЄQî›Ðsè··7†a‘‘‘ä””” øxÑ%âêN?~ Ú¸q#9«G…Qá‹¡¬ ’ –uëÖ9;;#„Î;µ~ÐsÃ(BHRRrûöíåååÝ·‰9sæ 6 !tðàAÖkÊô0*Ü1”DRA!""rüøq„ÐÚµkïÝ»GuEÐÓ‰;–ê81fÌ.6hggW\\ÛbVccãºuëNž-&&V__ïææVUUEuEü\cccPPššš¤¤äÈ‘#e³Eœã0Ú‰2Ø€0*ˆzî0}BBµk×ȧªªªÄ¼^½z!„h4ZLLÌ·oßètúóçÏÙ_dô§”””Î;wòäÉ>}úëÖØ0(ß`àžO`vôèQ}}}„Phh(yÄ|(''§©©iôèÑÄS 쬬ˆ#žÙÌÒÓÓstt477wqqÙ±cǧOŸBYYYõõõvvvØ¿ÂÃÃ?þ̾###NŠá\çʤç†QÞ›9sæÛ·oœœˆ§‚rÌ(ÄÐî‘”ÈËËŸ?^VV–ÉdN›6­±±‘êŠà& ÃnܸqãÆáÇ'%%ééé=yò„¸KNNkÕ»wïØ7%&&ÆùFYŸ¶¸ùK‹é-£<¥®®~íÚµøøxyyyþ£Cy ")åŒ9¢¤¤tþüyFu9´Í)¶ìϾÅ0ÌÚÚ:,,,--ÍÈÈèìÙ³†††RRRW¯^íŽbB ¬'EEEÉlÚõ2€ ƒ0JyóæåææwÇæOC©‘”Z¹¹¹öööT@»äää|||V­ZuçÎÂÂÂåË—çççÿþûïìg½yófóæÍ_¾|¹}ûvAAžžž´´ôªU«BBB:TTT”wøða®ƒ²³³;sæ q&ÆX;;û÷ïÿâÅ‹âââòòrIIÉ.–„D< ´ NQâpz„‹gÓ³9Ŷ½YyyyŽŽŽŠŠŠ¢¢¢šššÁÁÁ ƒ˜µwï^CCCqqqyyy;;»Û·o³)ÃÞÞ~õêÕ¬SØS[[ëáá¡   ­­½víZWWWò¦ÜÜ\sssbÿ–››ÛÑ2Ø€˜„Qð_CùDR„Ü›ž7 Œ "¦0(Ï×`à€pƒ0Ú£A I<“——‡µ…¸ \a´‡‚*ˆ ’x@WW·Í±TMMMªKÂIÃ(ƒÁ º¾1TÐA$ LxF===½½½y°¡šššM›6iiió`s‡b¨ƒƒÃš5kx¶9¡‘€pèp?~‹ƒH$%%Ù¯bcc3fÌâ±»»ûüùó;S)[UUUQQQ X·n]IIÉæÍ›¹¾ Æ?1´›ÔÖÖ¶y„†aœ·ãììÜ¡å)‘€ ãô¦^¬ŒŒŒNžùI¢õõõíÄV8TYY¹{÷î]»vUTTSLLL&OžÜ}[,ÍÍÍç΋ŒŒ$2(BHEEeåÊ•¾¾¾BAIÒÒÒ¯^½"Ÿ>>fff­§———khh×ÂýðáCïÞ½·oߎ㸇‡Çœ9spŸ3gëFSSS9Ùb{×ýçŸBCC{õêE6hnn~ùòåææf_‹pãçë†ÚÛÛöêÕKAA!00ü_KJJ233“’’’––vttd½[ñ¥K—ŒÅÄÄdeeGŽùùóç6ß¹s§ŒŒ ë:¾bÅ UUU ‰×dfÕâï377—M1uuuÞÞÞ²²²JJJ!!!‹-êêû p]Rø\g”7à:£‚ˆkÇŒ***ž8q"&&æÎ; cæÌ™¬Ë$$$L:ÕÇLJض­­mç¶õÏ?ÿ„„„ 80""¢²²!daaqíÚµ´´´É“'cÖõ—#ÐbP>..®W¯^/^¼øã?öîÝ{îÜ9b:ƒÁˆˆˆÈÊÊzþü¹œœœ‹‹ qÿâþùÇÃÃÃÛÛ»   ##cÙ²eœÿGûûûŸ>}:11ñÝ»w–––ãÇÏÊÊj½XBB‚““y]]ÝöŠA-Y²äáÇ7oÞLKK+++KNNæÆ»Â0p@ u¨g´Åº666äÜàà`55µ%K–(++ýú•˜HöŒâ8ÎF9ÄÚ3ZVV¶víZ999rë—.]jhhøñãNollljjb0L&³v‘òso(+{{{GGGò©‹‹ËâÅ‹[/V]]aXFFŽãD|üûï¿Úx‹žÑêêjqqñ'NO›››ŒŒ–.]Úæº¬a”M1UUUbbbd÷ƒÁÐÔÔ䓞QVÔKÚúƒ$Ö» ò-(P zFyzFŽ•••%‡‡‡ß¸qcß¾}ׯ_WQQéDãl$&&Ξ=»¶¶–ubZZÚ”)SجÕÞy-¼Á³B_¾|)--%¯$Àçdž²ȨªªJôp#„òòòÂÃÃ_¼xQ]]Mü~ýúuðàÁzzzŽŽŽæææãƳ±±™>}:‡W¼ËÉÉijj=z4ñÃ0++«·oß"„®\¹âææFLOLLœ9sf‹uÛ+&''‡Á`Œ9’XLTTÔÌ̬ oFwáçcIuuuwíÚõÓ%ÇŽÛ·o_”Ôš@ ‚®3aTJJjèСmÎúüùs^^ž¨¨hNNÎĉ»V[K÷ïß'“¨¸¸xSS'k‚»•ð¿feeÑh4ª iW‹óÞˆÿ#&“éàà`eeuêÔ)555MMM&“‰Â0ìÆ>LMMMJJZ¿~}JJ 1;gìØ±äy]êêê-æ²)F°‘4""BWW·¾¾Çq"’&$$xyyQ]ÝÏý|N ŠþÔ™0Ú&“9sæLKKËY³fÍŸ?ßÖÖ¶uf%ºë¨C‡}ÿþ}ãÆ?~$’¨’’’ƒƒƒ©©)†a-º|Ñ¿1´‡`2™¥¥¥ÕÕÕÕÕÕ¡ÂÂB33³ww÷Ÿ^¦¦8!”••Åþ0 ³¶¶¶¶¶ 377?{ö,'aTOOF£=yò„hÇñ'OžØÛÛ#„dee XfýûdSŒžžž˜˜ØóçÏ]\\BL&3==ÝÉɉ+o×UUUÅÆÆîÚµ«¾¾ž˜2bĈ°°0 ööö¾~ý:B(66!”››‹b2™+V¬øã?p÷ññÙ¾};Ñå?eʔݓ—/_ÎÎΖ””466¾páB‹_8ŽïÚµëÀ>|èÓ§ÏìÙ³·mÛ†jll\·nÝÉ“'+**LMM·mÛfmmM¬âàà`llüãÇëׯ×××Ï;—Øtë"ÓÓÓ·mÛöîÝ; ÃÆŒ³wïÞAƒ°Ù^ƒœÔ=PgÂhCCÃëׯY§˜˜˜ˆ‰‰mذ!///##CEEåöíÛ3fÌHOOo1F¬¥¥uëÖ­¢¢"òh6Žj[´hÑܹsããã7nÜøéÓ§²²²Ó§O¿zõ*44ÔÃÃC€RW7©¯¯?pàÀ¶mÛ¾}û–™™éááabb"@‘TUUUVVöúõë~~~UUUØ¿g)½yóæ¯¿þš8q¢’’RfffAAAë×Û$''çãã³jÕ*UUUmmíØØØüüüóçÏ·¹pÿþý_¼xQ\\,%%¥¬¬Ü^1òòò³fÍ RVVVWWß¼ysUUWÞî"c(yá3Êc(!!!¡¼¼¼Åx\\ÜŠ+^¼x‘‘‘1cÆ KKËiÓ¦±®EœÄåááÁ`0ž>}е:‰-44tïÞ½ûöí3fLiiéË—/‰éþþþW®\ILLÔÑщ?~ü«W¯ÈŸ"GŽ9uêÔ¡C‡rrr,,,ˆM·.òùóç&&&ÕÕÕááá...™™™m~²Úl“ú çêÊ L¡/_¾Ü¿_LL,99™X¬¦¦FWWwþüùøÿžÀTTT4zôhbø¸+—vúñãÇž={444ÈŒŒŒ’’’˜L&‡/DˆÕÕÕmß¾õ˜]“3gÎðÏ›Óâl•E‹yxx/_¾¬­­­®®>dÈ?ÿüSBB⯿þÂqÜ4‡'´áÿ{S{ r~JpoÀ L‚¨Ãa”÷Ú»Î(Žã ±±±jjj¬©ëìÙ³ü“º(Äÿ‘Th4774hÏž=T‚ã‚C ­ÃèÊ•+ɧ‹-òôô$“9¯¹¹ÙÑÑQJJjòäÉ111ÅÅÅ-ÚLOOGåçç·˜þŸÿü!TPP@NY°`­­-¹éU«Vµ¹éEæææþöÛozzzªªªÄ'ëÖ­[-ŠdÓàOë Â(o@D0tˆ¤¤ä²eËòóówîÜ©ªªŠÊÌÌœ6m‡¸ÂMZZzÅŠd$%îMMMÿüóÏN¹ oÞ¼¹zõê·oß —-[öåË—©S§R[RUUUDDÄÀÈqy"†¦¥¥Q>.ω6-a ÷8IDATÏicEœÄvãÆáÇ'%%ééé‘÷Ùê¢Ãå­7þ=¡ ÇñS§N¥§§¿~ýZTT´½ÚÚl°ûêÆÍ˜¨"%%°páÂlݺµ´´´õ5zz,"’úúú ô±¤|ˆÁ`¬]»677ðáÇ'''³öÐóßÊFçÎed›””ÔÝ»wµµµY×bsçE²?»Ž+õƒâáÇ TW!´rrr¨.t˜0„Q‘º–.]º{÷îqãÆQ]HÊufffäe¡($ˆ1”Àz®XŸ>}8Yå§'±IKK/_¾|åÊ•222cÆŒ)++KOO_°`A‡Nbk¯H6'´q¨Ó'á!Cu ða‹ 4-((ˆê*ø Ü A”÷÷÷g0:::JJJœ¬"++›šš:vìØ~ýúÍ›7ï÷ß_¸pa‹e¢¢¢Ö®]¬­­íää”——GLßµk—§§çÌ™3 ž>}zóæM}}ýùõëד'OîܹSCCÃÚÚÚÇǧ£×ñå¤~èþûË>"""$$„ÚRÚóöíÛ!C† „.^¼ÈþfK€s¬"¦@/© ÜÞPz2:þñãGª«è)DEE[®ø™ð Óƒ{A1Á%!!AÞ%À G÷‚BÐå€ö@IùÄP Â(ø/ˆ¤üb(€žÂ(øIùÄP=„QЈ¤T  §0 Ú‘”— †è™ Œ‚Ÿ€HÚÝ †èÉ ŒŽ@$íC»¢±±qæÌ™²²²†ñíY…€§§çâÅ‹©®Ðã@‘”[ †ÆÆÆŽ5JZZZFFfìØ±ÏŸ?ïh çλyóæóçÏËÊÊ »£HVÞÞÞ†a&**ª¬¬üÛo¿}ùò¥‹m:;;p¥<\FA‡A$í ˆ¡„‹/ºººÞºuëòåË¢¢¢öööŸ>}êP þ¯½; ‹âÊ÷8~覡AqÁ¨à‚D͸EqDp{yœ‰d0NÜ‚kÜFP™DqFÍ$j$â¸MŒæÁ¸5ˆqÃ%â8‰ƒ“(¢ˆ{ *(‚D î‹ºéË¥AjËïçEžîS§Îùw‹æÇ9UÝW¯¶nݺmÛ¶ 6´´´¬Þò L;wîüÓO?¥¦¦®^½:)))$$¤z'€WÚÂ… %suîÜ9¹È¯¿þZéZPÚãÇ‘Tæíí½eË–¢¢"¥K3;ÙÙÙ ,¨W¯žñ½’c¨Òu)ïáÇ–––ëׯ7=TPPÖ¸qckkë.]º=zTn|¸££cŸ>}J9räH___ãÓÐÐÐ×^{íEf9rdÉ„/]ºT\\ü÷¿ÿÝÅÅE«Õº¸¸¬ZµÊxÖÎ;Û¶mkeeegg×µk×›7o–*﫯¾êر£­­m¿~ýÒÒÒŒ‡z÷î=uêÔqãÆ999Õ«W/44´¸¸X>ôäÉ“±cÇÚÛÛ;::Λ7/88xüøñ¦ P£þ÷»éO:µvíÚL»/ ªë%¨M|Ç}eðòËÏÏ/..®_¿¾é¡iÓ¦íÞ½{ãÆ®®®Ÿ~úiÿþýOŸ>ݦM›;wFEE%$$œœ?þàÁƒSSSûÖ­[·zõê´´´.]º¼ùæ›o¿ý¶búôéû÷ïOLLtrrZ°`Abbâ°aÞù.À«‹•Q3Ç*©)VC+#88ØËËë×_-ÕþðáC­Vûå—_ÊO‹‹‹½¼¼¦L™"?ŒŒìÚµ«éhŸUñÊè€Ê+R^È´´´”^``àƒ^pÆ€€€iÓ¦Éóòòlmm>lì9þ|yöÂ… Bˆÿþ÷¿åÕfúh4šsçÎ_WPPñèŸþô§ &H’”››kmm½uëV¹½   Q£F¬Œ¨}¬Z¡Úp-iI\ZI¡¡¡Gމ×jµ¥¥¥¥=}ú´{÷îòSFãããsþüùŠ|¾³d^^^íÔ©Ó™3gNž<9a„£GÞ½{÷Åg4ºpáB^^žŸŸŸæ7óçÏ¿yó¦ÂÝݽ_¿~:u>þÈ‘#µ3i© îR¿#YYYUp®Á`ðööBtîÜùܹs¡¡¡ /8c©ö´´4777Ó¾ýöÛï¾ûîðáÃ_}õÕœ9s}úøøøÄÅÅ5iÒÄÂÂÂÙÙ¹¨¨¨¼$I’Ï*õ’u:Ý3_T;«Æ+]TèÕŒ¤ÄÐÊ+..7nÜþýû;Öºuë2û¸»»ëtº'N´jÕJ!IÒ‰'z÷î]ñÈŸU·nÝœœcç+W®4mÚô9êÿàƒSSS½½½Ÿ{FKKKc6õôô´±±ùæ›oBCCM§Óh4={öìÙ³ç¼yó:uê´mÛ¶’a433333óðáÃr.\(™DËãêêjaa‘ššêïï/·œ?þ™o/T;˜WgãžMùª1bÄÖ­[W¬X‘››{æÌ™3gÎüüóÏ¥úØÛÛ‡„„Ìš5ëСCÓ§O¿|ùòûï¿_ñÈŸåçç·eËùrÏU«VýøãÏW@@€»»ûÒ¥K_dÆæÍ›ÿðÃׯ_ÏÊÊÒëõ³fÍŠˆˆX½zuffæÅ‹×®]»fÍ!ÄÙ³g/^|îܹ۷o8pàÊ•+îîî%‹iܸ±Ýž={„999ùË_Lïp2U¿~ý·ÞzkîܹrV^¾|yFFÆó½ðB”»\¯µÞÞÄ-JÏÇô“A###M»„††6jÔH§Ó•úȤòn`ªø¬ÜÜÜàààºu뺸¸|ðÁüãKÞÀ^^Á¥>ÚI’¤ØØXNwûöíçžñÒ¥K:u’/–½té’$IŸ}ö™§§§V«­S§ŽŸŸß$IJOOïׯ_ƒ ,--gÏž]XXXª¼]»v¹¸¸4mÚ´]»v[·nµ¶¶Þ»wo™¯küøñÁÁÁò㜜œaÆ5hÐÀÍÍíý÷ßë­·¸ @íÓH’T;©Bäåå7îå–—wãžMy^a xÙ#)1€êB…b^ÆHJ  zF¡°—%’C¨ „Q˜sޤÄPjafÄÜ")1€šF…Ù1‡HJ  vFa¦”ФÄPjaf­6#)1€ÚGÅK ¦#)1% :T¯×oذAéB^Ô_ÿúד'O9rDéB "fôÑ9@yjî;îùNùWMPPИ1c*îãëëÛ£G§ZÔÚD Â(^ÕI‰¡0õôéS!Äĉ«7ÿÉÃÊV“_|Ô”Ç#©ÌÛÛ{Ë–-EEEÏ<7;;{Á‚õêÕ3ž+ÇÐZ(²o¿ýÖÚÚ:??_’¤{÷îi4š€€ùÐÆ‹‹‹%IŠïÚµ«Á`Ðëõ=zôø÷¿ÿmaÏž=íÛ·×jµ666:tøÏþc:‹iŸ‘#G–ü×ïðáÃ’$„„„Œ?¾qãÆmÚ´‘$)88xäÈ‘ÆqV®\ééé©Óéêׯ,IR™ã”b:laaáœ9sœœœt:··÷Ž;Œ;vìi|Ö»wï &Z±b…»»»V«uvv^°`ñÇþéÓ§S§Nuppppp˜e†QŸ’-Æ0šŸŸo0Œ´âqJ)5l^^ž^¯/ùÒú÷ï/?®|-((°··/ù£»aÆfÍšÉÕÚÚÚ®[·Îx¨}ûö„QækFñÒ«øZÒpm¨òóóKJJB9rÄßß_~š‘‘qõêU???¹ÏÕ«WGÕ¦M›&Mš4iÒäÞ½{·oßB¸ººúøø 8pÑ¢E—.]2¿2}Œ¼½½ËlOKK{üøqŸ>}žï5–6--íÉ“'%ï‹êÙ³ç¹sçª:æÅ‹=z4hÐ ÍoFuãÆ !Ä¥K—òòòJNñÌÛ°ÀF¡åEÒFCÍ¿¿ÿ?ü oÁ÷ìÙÓßßÿðáÇ–¯°B÷íÛ7''gãÆ§N:sæL³fÍŠŠŠäÓwïÞ””äãã³wï^OOÏøøxÓ)*ÓGfeeU¯±òÃj4š’O¥ò?qO¾QïôéÓ%äÆÂÂB!„V«5vÖétÏQ6Ô2Â(TÅ4’Êÿ‡&†š›nݺYYYEFFþîw¿«S§ŽŸŸ_rrò¾}ûŒË¢7oÞLOO_¸pa—.]œœœlmmoݺUr„îݻϞ=ûرc›6m*sÓ>–––•ÿàwwwƒÁpðàAÓCUGJ¯×?~ÜØrìØ±víÚɲ³³‡._¾\ÞDvvv ¦S¸ººZZZž?ÞØröìÙÊWJ!ŒB…äHš‘‘áàà „èß¿?1ÔÜèõúnݺmÚ´Éßß_áêêZ¿~ýøøxcutt¬S§Îž={„ùùù“&M2f²ÔÔÔ¨¨¨3gÎܹs'99ùìÙ³¥Æ/¯O‹-RRR233³²²žù‰Kz½~Ö¬YsçÎ]¿~ýµk×RSS—-[&ªÒ8B›I“&EDD$$$dff.Z´(111,,L>êïï¿}ûö;wî!âãã÷íÛg<±ÔDz½~Μ9QQQ111éééqqq‘‘‘Bˆ:uêüùÏž?þ/¿ü"„ضmÛ±cÇžY(Ž0 Õ²±±‘ÃhÓ¦M•®eðóó+,,”èñ©1Œêõú-[¶|þùçMš4éܹó›o¾éêê*2 ÉÉÉýúõsrr œ7o^©ÁËë3yòdƒÁàîîîè蘜œüÌ"#""¢££—,YâêêÚ«W¯S§NÉíUG±dÉ’ &Œ?ÞÍÍ-..nÛ¶m}ûö•MŸ>Ý×××ÓÓ³eË–xï½÷Œg™N»nݺ6mÚtèÐ!&&¦eË–rçU«VuìØÑËË«uëÖ;wî;vle eñu P³Ö­[_¾|yĈ_|ñ…Òµ€2°2 5“ï 1ÞõÌ ajfii)~»‡ ˜!Â(ÔŒ•QÌajÆÊ(fŽ0 5ce3G…š±2 €™#ŒBÍXÀÌF¡f¬Œ`æ£P3VF0s„Q¨™¼2JÀlF¡flÓ`æ£P3¶é0s„Q¨+£˜9Â(ÔŒ•QÌajÆÊ(fŽ0 5ce3G…š±2 €™#ŒBÍXÀÌF¡f¬Œ`æ£P³J®Œ<¸W¯^’$[Ö¬YãààpãÆš­€WajVɯ]½zõùóçcbbä§×®]›1cÆòåËk¼D^m„Q¨Y%·é7n¼råÊ>ø ==]âëë;jÔ¨âââÈÈÈ-Zètº¶mÛîØ±Cî¿}ûv///­V[·nÝ^½zåçç×ô @­¬”.¨A•¿ièС;vì=zô;3’’’šš*„ß¿ÿÆ]\\’’’FŒѬY³V­Z 6léÒ¥øÃ Ž=Zã/õÒ”¼NP™ððð>úÈ`0äææ>³ó/¿üÒ¶mÛ{÷îmÚ´iذa¹¹¹ 6üî»ï:wî,w=z´Á`˜8q¢··÷õë×ÙÄàűM5«ÒG;9::Ž7®Y³fÆ BüôÓO]ºtÑüfÆ ·nÝòôôìÕ«—§§ç!C>û쳬¬¬š} ¨ajVÕv²²²’󫢸¸XqëÖ-©„øøx ‹C‡íÚµËÓÓsÍš5—/_®¡úP=Â(ÔìE>ôÞËËK§ÓíÙ³Çô………¿¿TTÔéÓ§íìì^´P^UÜÀ5“WF%I***’Wž½½ýôéÓgΜ©×ë}}}>|¸ÿ~—æÍ›úèÑ£GÊ”J°Rº ÚäååuìØñÉ“'yyy‡emÓ/]ºôêÕ«ááá=zôèÞ½»bµ!+£P[[ÛI“& !’’’’’’„ÉÊèýû÷W­Z%„ðóó#‰`£P•ððp{{{!DDD„0YýôÓOåÝùÙ³g+W#ø?„Q¨Š££ã´iÓ„'NœHLL,¹2úðáØ˜!D·nÝz÷î­d•à7„Q¨MXXX½zõ„ÿû^XXûàÁÁ²(æ„0 µqpp˜1c†"%%%11QnÌÏÏ_¶l™â7Þ8p ’õ€£P¡©S§6jÔH-·œ8qâîÝ»Bˆ?üP£Ñ(Y(A#I’Ò5ÕoéÒ¥aaaƧuêÔyøð¡§§gjjªqï(Ž0 uÊÏÏwss»yófÉÆ7>\©’€)–ˆ N666Æ•´Z­ÂÕÕõwÞQ´(PaªÒªU+!ÄÓ§O…áááÆOzf‚0 ÕÒétsçΕ;;;9RÙz€)Â(ÔìÝwßõððBÌœ9S§Ó)](]KTÍÉ“'?®tUЩS§›7o>~üøã?Vº–*;vlݺu•®€ÇÝô¨šèèè9sæ(]…ú]¹rE¾àuc›Ša›ÏéÑ£G–––JW¡6›7o Qº jaÏÉÖÖ–¯2ªvÜexÕ& Â(C€b£P aŠ!Œ@1„Q(†0 ÅF Â(C€b£P aŠ!Œâ%0tèÐQ£F)]¨~VJ€W]PPƒƒÃºuë*èãëë«Õj_|œjTËÓ V„Q˜µ§OŸjµÚ‰'ÖÜà512¨$¶éQ{;tè Óélmmßxã”””Q£FíØ±cýúõF£Ñ9rD1hР1cÆL˜0¡I“&íÚµ&Ûô±±±^^^ÖÖÖ 4:t¨¢ÌqÊd:xQQQDD„³³³µµõ믿oìÜ©S§¨¨(ãÓ3fôéÓ§‚ébbb<<üðÃ'N|òÉ'‡QS¿þúëâÅ‹7oÞ „pvv^°`ADDa€ŠFQK\]]}||üýýÿûß¹¹¹•×ÙÛÛ»Ìö´´´ÇË{åÏ­äàiiiOž<éÑ£‡±¥gϞ˗/¯ê˜/^|ôèÑ AƒJ6j4š©€W׌¢öìÞ½;))ÉÇÇgïÞ½žžž%¯Î,ÅʪMªüà¥Ò¤$Iåõ”/=}ú´T׌ðL„QÔªîݻϞ=ûرc›6mBXZZV>´¹»» ù:ÑRª4NÉõzýñãÇ-ÇŽ3ný;88dgg]¾|¹¼é<<<ìììªZ¯8Â(jIjjjTTÔ™3gîܹ“œœ|öìY!D‹-RRR233³²²ž>}Zñ z½~Ö¬YsçÎ]¿~ýµk×RSS—-[&ªÒ8F666“&MŠˆˆHHHÈÌÌ\´hQbbbXX˜|ÔßßûöíwîÜBÄÇÇïÛ·Ïxb©éôzýœ9s¢¢¢bbb222ÒÓÓãââ"##«ú.ðª!Œ¢– †äää~ýú999Λ7O1yòdƒÁàîîîè蘜œüÌq"""¢££—,YâêêÚ«W¯S§NÉíUÇhÉ’%&L?~¼››[\\ܶmÛŒw/MŸ>Ý×××ÓÓ³eË–xï½÷Œg™N»nݺ6mÚtèÐ!&&¦eË–•/€W“¦‚ËàSÑÑÑòâEEEü2S;üòËáÇ !®\¹ÒªU+¥Ë Æ& Â(T(==]S–2ï| âsF¡B­[·æú^ ¬Œ@1„Q(†0 ÅF Â(C€b£P aŠ!Œ@1„Q(†0 ÅF Â(C€b¬”./«7nXXðËL5»ÿ¾Ò%P«£xN-Z´PºðÒce Šùî† k¦zùIEND®B`‚kamailio-4.0.4/obsolete/rr/doc/rr.xml0000644000000000000000000000145312223032460016151 0ustar rootroot
Jan Janak FhG FOKUS
jan@iptel.org
2003 FhG FOKUS
RR Module
Overview The module contains record routing logic
kamailio-4.0.4/obsolete/rr/doc/Makefile0000644000000000000000000000012312223032460016435 0ustar rootrootdocs = rr.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/rr/doc/params.xml0000644000000000000000000001107012223032460017005 0ustar rootroot
Parameters
<varname>enable_full_lr</varname> (integer) If set to 1 then ;lr=on instead of just ;lr will be used. This is to overcome problems with broken UAs which strip ;lr parameter when generating Route header fields from Record-Route (;lr=on seems to help). Default value is 0 (no). Set <varname>enable_full_lr</varname> parameter ... modparam("rr", "enable_full_lr", 1) ...
<varname>append_fromtag</varname> (integer) if turned on, request's from-tag is appended to record-route; that's useful for understanding whether subsequent requests (such as BYE) come from caller (route's from-tag==BYE's from-tag) or callee (route's from-tag==BYE's to-tag) Default value is 1 (yes). Set <varname>append_fromtag</varname> parameter ... modparam("rr", "append_fromtag", 0) ...
<varname>enable_double_rr</varname> (integer) There are some situations when the server needs to insert two Record-Route header fields instead of one. For example when using two disconnected networks or doing cross-protocol forwarding from UDP->TCP. This parameter enables inserting of 2 Record-Routes. The server will later remove both of them. Default value is 1 (yes). Set <varname>enable_double_rr</varname> parameter ... modparam("rr", "enable_double_rr", 0) ...
<varname>user_part_avp</varname> (string) If this parameter is set, the loose_route call stores the user part of the route URI in the AVP, which is identified by the parameter value. It can be utilized later in the routing script. Default value is NULL (do not insert user part into avps). Set <varname>user_part_avp</varname> parameter, later use ... modparam("rr", "user_part_avp", "route_user") ... route{ ... if (loose_route()) { t_relay(); } if ($route_user == "my_user") { ... } ... }
<varname>next_route_avp</varname> (string) If this parameter is set, the loose_route call stores the URI of the next hop in the AVP, which is identified by the parameter value. It can be then utilized in the routing script. Default value is NULL (do not insert next route into avps). Set <varname>next_route_avp</varname> parameter, later use ... modparam("rr", "next_route_avp", "route_next") ... route{ ... loose_route(); if ($route_next=~"101.102.103.1[0-9][0-9]") { t_relay(); }
<varname>force_send_socket</varname> (integer) If set to 1 and two Record-Route headers have been inserted by the proxy before then the outgoing socket is forced by loose_route() to be the address of the second Route header in the in-dialog requests. Useful on multihomed hosts. Default value is 1 (yes). Set <varname>force_send_socket</varname> parameter ... modparam("rr", "force_send_socket", 1) ...
kamailio-4.0.4/obsolete/rr/loose.c0000644000000000000000000006113412223032460015526 0ustar rootroot/* * Route & Record-Route module, loose routing support * * $Id$ * * Copyright (C) 2001-2004 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) * 2006-01-09 store user part of Route URI in AVP (if required) (mma) * 2009-05-26 Force the send socket if two RR headers were added (Miklos) */ #include #include "../../ut.h" #include "../../str.h" #include "../../dprint.h" #include "../../forward.h" #include "../../data_lump.h" #include "../../parser/parse_rr.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../mem/mem.h" #include "../../dset.h" #include "../../socket_info.h" #include "loose.h" #include "rr_mod.h" #include "avp_cookie.h" #define RR_ERROR -1 /* An error occured while processing route set */ #define RR_DRIVEN 1 /* The next hop is determined from the route set */ #define NOT_RR_DRIVEN -1 /* The next hop is not determined from the route set */ #define MY_RT_PREFIX ROUTE_PREFIX "<" #define MY_RT_PREFIX_LEN (sizeof(MY_RT_PREFIX)-1) #define MY_RT_SUFFIX ">\r\n" #define MY_RT_SUFFIX_LEN (sizeof(MY_RT_SUFFIX)-1) /* * Check if the To-tag is set in the message */ static int has_to_tag(struct sip_msg* msg) { str tag; if (!msg->to && parse_headers(msg, HDR_TO_F, 0) == -1) { LOG(L_ERR, "has_to_tag: Cannot parse To header field\n"); return -1; } if (!msg->to) { LOG(L_ERR, "has_to_tag: To header field not found\n"); return -1; } tag = get_to(msg)->tag_value; if (tag.s == 0 || tag.len == 0) { DBG("has_to_tag: No\n"); return 0; } DBG("has_to_tag: Yes\n"); return 1; } /* * Parse the message and find first occurrence of * Route header field. The function returns -1 or -2 * on a parser error, 0 if there is a Route HF and * 1 if there is no Route HF. */ static inline int find_first_route(struct sip_msg* _m) { if (parse_headers(_m, HDR_ROUTE_F, 0) == -1) { LOG(L_ERR, "find_first_route: Error while parsing headers\n"); return -1; } else { if (_m->route) { if (parse_rr(_m->route) < 0) { LOG(L_ERR, "find_first_route: Error while parsing Route HF\n"); return -2; } return 0; } else { DBG("find_first_route: No Route headers found\n"); return 1; } } } /* * Check if URI is myself */ static inline int is_myself(str* _host, unsigned short _port) { str did; int ret; ret = check_self(_host, _port ? _port : SIP_PORT, 0);/* match all protos*/ if (ret < 0) return 0; if (ret == 0 && dm_get_did) { ret = dm_get_did(&did, _host); if (ret < 0) return 0; else if (ret > 0) { /* as the domain module does not know anything about ports lets check if the port matches any of our listening ports */ ret = check_self_port(_port ? _port : SIP_PORT, 0); } } return ret; } static inline void store_user_in_avps(str* user) { avp_value_t val; if (user_part_avp_ident.name.s.s && user_part_avp_ident.name.s.len && user && user->s && user->len) { val.s = *user; add_avp(user_part_avp_ident.flags | AVP_NAME_STR | AVP_VAL_STR, user_part_avp_ident.name, val); } } static inline void store_next_route_in_avps(str* uri) { avp_value_t val; if (next_route_avp_ident.name.s.s && next_route_avp_ident.name.s.len && uri && uri->s && uri->len) { val.s = *uri; add_avp(next_route_avp_ident.flags | AVP_NAME_STR | AVP_VAL_STR, next_route_avp_ident.name, val); } } /* * Find and parse next Route header field */ static inline int find_next_route(struct sip_msg* _m, struct hdr_field** _hdr) { struct hdr_field* ptr; ptr = (*_hdr)->next; /* Try to find already parsed Route headers */ while(ptr) { if (ptr->type == HDR_ROUTE_T) goto found; ptr = ptr->next; } /* There are no already parsed Route headers, try to find next * occurrence of Route header */ if (parse_headers(_m, HDR_ROUTE_F, 1) == -1) { LOG(L_ERR, "find_next_route: Error while parsing headers\n"); return -1; } if ((_m->last_header->type != HDR_ROUTE_T) || (_m->last_header == *_hdr)) { DBG("find_next_route: No next Route HF found\n"); return 1; } ptr = _m->last_header; found: if (parse_rr(ptr) < 0) { LOG(L_ERR, "find_next_route: Error while parsing Route body\n"); return -2; } *_hdr = ptr; return 0; } /* * Check if the given uri contains lr parameter which marks loose routers */ static inline int is_strict(str* _params) { str s; int i, state = 0; if (_params->len == 0) return 1; s.s = _params->s; s.len = _params->len; for(i = 0; i < s.len; i++) { switch(state) { case 0: switch(s.s[i]) { case ' ': case '\r': case '\n': case '\t': break; case 'l': case 'L': state = 1; break; default: state = 4; break; } break; case 1: switch(s.s[i]) { case 'r': case 'R': state = 2; break; default: state = 4; break; } break; case 2: switch(s.s[i]) { case ';': return 0; case '=': return 0; case ' ': case '\r': case '\n': case '\t': state = 3; break; default: state = 4; break; } break; case 3: switch(s.s[i]) { case ';': return 0; case '=': return 0; case ' ': case '\r': case '\n': case '\t': break; default: state = 4; break; } break; case 4: switch(s.s[i]) { case '\"': state = 5; break; case ';': state = 0; break; default: break; } break; case 5: switch(s.s[i]) { case '\\': state = 6; break; case '\"': state = 4; break; default: break; } break; case 6: state = 5; break; } } if ((state == 2) || (state == 3)) return 0; else return 1; } /* check direction using ftag */ static int get_direction(struct sip_msg* _m, str* _params) { str s, ftag; int i, state = 0; if (_params->len == 0) return 0; ftag.len = 0; ftag.s = 0; s.s = _params->s; s.len = _params->len; for(i = 0; i < s.len; i++) { switch(state) { case 0: switch(s.s[i]) { case ' ': case '\r': case '\n': case '\t': break; case 'f': case 'F': state = 1; break; default: state = 8; break; } break; case 1: switch(s.s[i]) { case 't': case 'T': state = 2; break; default: state = 8; break; } break; case 2: switch(s.s[i]) { case 'a': case 'A': state = 3; break; default: state = 8; break; } break; case 3: switch(s.s[i]) { case 'g': case 'G': state = 4; break; default: state = 8; break; } break; case 4: switch(s.s[i]) { case ';': return 0; case '=': state = 6; break; case ' ': case '\r': case '\n': case '\t': state = 5; break; default: state = 8; break; } break; case 5: switch(s.s[i]) { case ';': return 0; case '=': state = 6; break; case ' ': case '\r': case '\n': case '\t': break; default: state = 8; break; } break; case 6: switch(s.s[i]) { case '\"': state=101; break; case ';': return 0; case ' ': case '\r': case '\n': case '\t': break; default: state = 100; ftag.s = s.s+i; break; } break; case 8: switch(s.s[i]) { case '\"': state = 9; break; case ';': state = 0; break; default: break; } break; case 9: switch(s.s[i]) { case '\\': state = 10; break; case '\"': state = 8; break; default: break; } break; case 10: state = 9; break; case 100: switch(s.s[i]) { case '\"': return 0; case ';': case ' ': case '\r': case '\n': case '\t': ftag.len = s.s+i - ftag.s; i = s.len; state = 0; break; } break; case 101: /* no escape chars supported in base64 algorithm */ switch(s.s[i]) { case '\"': ftag.len = s.s+i - ftag.s; break; case '\\': state = 102; break; default: break; } break; case 102: state = 101; break; } } if (state == 100) ftag.len = s.s+i - ftag.s; if (ftag.len) { if ((parse_from_header(_m)==0) && get_from(_m)) { /* compare if from.tag == ftag */ if (ftag.len!=get_from(_m)->tag_value.len || strncmp(ftag.s, get_from(_m)->tag_value.s, ftag.len)) return 1; } } return 0; } static void get_avp_cookie_from_uri(str* _params, str *_avp_cookie) { str s; int i, state = 0; _avp_cookie->len = 0; _avp_cookie->s = 0; if (_params->len == 0) return; s.s = _params->s; s.len = _params->len; for(i = 0; i < s.len; i++) { switch(state) { case 0: switch(s.s[i]) { case ' ': case '\r': case '\n': case '\t': break; case 'a': case 'A': state = 1; break; default: state = 7; break; } break; case 1: switch(s.s[i]) { case 'v': case 'V': state = 2; break; default: state = 7; break; } break; case 2: switch(s.s[i]) { case 'p': case 'P': state = 3; break; default: state = 7; break; } break; case 3: switch(s.s[i]) { case ';': return; case '=': state = 5; break; case ' ': case '\r': case '\n': case '\t': state = 4; break; default: state = 7; break; } break; case 4: switch(s.s[i]) { case ';': return; case '=': state = 5; break; case ' ': case '\r': case '\n': case '\t': break; default: state = 7; break; } break; case 5: switch(s.s[i]) { case '\"': state=101; break; case ';': return; case ' ': case '\r': case '\n': case '\t': break; default: state = 100; _avp_cookie->s = s.s+i; break; } break; case 7: switch(s.s[i]) { case '\"': state = 8; break; case ';': state = 0; break; default: break; } break; case 8: switch(s.s[i]) { case '\\': state = 9; break; case '\"': state = 7; break; default: break; } break; case 9: state = 8; break; case 100: switch(s.s[i]) { case '\"': return; case ';': case ' ': case '\r': case '\n': case '\t': _avp_cookie->len = s.s+i - _avp_cookie->s; i = s.len; state = 0; break; } break; case 101: /* no escape chars supported in base64 algorithm */ switch(s.s[i]) { case '\"': _avp_cookie->len = s.s+i - _avp_cookie->s; break; case '\\': state = 102; break; default: break; } break; case 102: state = 101; break; } } if (state == 100) _avp_cookie->len = s.s+i - _avp_cookie->s; } /* * Put Request-URI as last Route header of a SIP * message, this is necessary when forwarding to * a strict router */ static inline int save_ruri(struct sip_msg* _m) { struct lump* anchor; char *s; int len; /* We must parse the whole message header here, * because Request-URI must be saved in last * Route HF in the message */ if (parse_headers(_m, HDR_EOH_F, 0) == -1) { LOG(L_ERR, "save_ruri: Error while parsing message\n"); return -1; } /* Create an anchor */ anchor = anchor_lump(_m, _m->unparsed - _m->buf, 0, 0); if (anchor == 0) { LOG(L_ERR, "save_ruri: Can't get anchor\n"); return -2; } /* Create buffer for new lump */ len = MY_RT_PREFIX_LEN + _m->first_line.u.request.uri.len + MY_RT_SUFFIX_LEN; s = (char*)pkg_malloc(len); if (!s) { LOG(L_ERR, "save_ruri: No memory left\n"); return -3; } /* Create new header field */ memcpy(s, MY_RT_PREFIX, MY_RT_PREFIX_LEN); memcpy(s + MY_RT_PREFIX_LEN, _m->first_line.u.request.uri.s, _m->first_line.u.request.uri.len); memcpy(s + MY_RT_PREFIX_LEN + _m->first_line.u.request.uri.len, MY_RT_SUFFIX, MY_RT_SUFFIX_LEN); DBG("save_ruri: New header: '%.*s'\n", len, ZSW(s)); /* Insert it */ if (insert_new_lump_before(anchor, s, len, 0) == 0) { pkg_free(s); LOG(L_ERR, "save_ruri: Cannot insert lump\n"); return -4; } return 0; } /* * Logic necessary to forward request to strict routers * * Returns 0 on success, negative number on an error */ static inline int handle_sr(struct sip_msg* _m, struct hdr_field* _hdr, rr_t* _r) { str* uri; char* rem_off; int rem_len; uri = &_r->nameaddr.uri; /* Next hop is strict router, save R-URI here */ if (save_ruri(_m) < 0) { LOG(L_ERR, "handle_sr: Error while saving Request-URI\n"); return -1; } /* Put the first Route in Request-URI */ if (rewrite_uri(_m, uri) < 0) { LOG(L_ERR, "handle_sr: Error while rewriting request URI\n"); return -2; } if (!_r->next) { rem_off = _hdr->name.s; rem_len = _hdr->len; } else { rem_off = _hdr->body.s; rem_len = _r->next->nameaddr.name.s - _hdr->body.s; } if (!del_lump(_m, rem_off - _m->buf, rem_len, 0)) { LOG(L_ERR, "handle_sr: Can't remove Route HF\n"); return -9; } return 0; } /* * Find last route in the last Route header field, * if there was a previous route in the last Route header * field, it will be saved in _p parameter */ static inline int find_rem_target(struct sip_msg* _m, struct hdr_field** _h, rr_t** _l, rr_t** _p) { struct hdr_field* ptr, *last; if (parse_headers(_m, HDR_EOH_F, 0) == -1) { LOG(L_ERR, "find_rem_target: Error while parsing message header\n"); return -1; } ptr = _m->route; last = 0; while(ptr) { if (ptr->type == HDR_ROUTE_T) last = ptr; ptr = ptr->next; } if (last) { if (parse_rr(last) < 0) { LOG(L_ERR, "find_rem_target: Error while parsing last Route HF\n"); return -2; } *_h = last; *_p = 0; *_l = (rr_t*)last->parsed; while ((*_l)->next) { *_p = *_l; *_l = (*_l)->next; } return 0; } else { LOG(L_ERR, "find_rem_target: Can't find last Route HF\n"); return 1; } } /* * Previous hop was a strict router, handle this case */ static inline int after_strict(struct sip_msg* _m, struct sip_uri* _pru, int _route_myself) { int res, rem_len; struct hdr_field* hdr; rr_t* rt, *prev; char* rem_off; str* uri; str avp_cookie; get_avp_cookie_from_uri(&_m->parsed_uri.params, &avp_cookie); if (avp_cookie.len > 0) rr_set_avp_cookies(&avp_cookie, get_direction(_m, &_m->parsed_uri.params)); hdr = _m->route; rt = (rr_t*)hdr->parsed; if (_route_myself == 1) { store_user_in_avps(&(_pru->user)); if (!rt->next) { /* No next route in the same header, remove the whole header * field immediately */ if (!del_lump(_m, hdr->name.s - _m->buf, hdr->len, 0)) { LOG(L_ERR, "after_strict: Cannot remove Route HF\n"); return RR_ERROR; } res = find_next_route(_m, &hdr); if (res < 0) { LOG(L_ERR, "after_strict: Error while searching next route\n"); return RR_ERROR; } if (res > 0) { /* No next route found */ DBG("after_strict: No next URI found\n"); return NOT_RR_DRIVEN; } rt = (rr_t*)hdr->parsed; } else rt = rt->next; } if (rt != _m->route->parsed) { uri = &rt->nameaddr.uri; if (parse_uri(uri->s, uri->len, _pru) == -1) { LOG(L_ERR, "after_strict: Error while parsing URI\n"); return RR_ERROR; } } else { uri = &rt->nameaddr.uri; } store_next_route_in_avps(uri); if (is_strict(&(_pru->params))) { DBG("after_strict: Next hop: '%.*s' is strict router\n", uri->len, ZSW(uri->s)); /* Previous hop was a strict router and the next hop is strict * router too. There is no need to save R-URI again because it * is saved already. In fact, in this case we will behave exactly * like a strict router. */ /* Note: when there is only one Route URI left (endpoint), it will * always be a strict router because endpoints don't use ;lr parameter * In this case we will simply put the URI in R-URI and forward it, which * will work perfectly */ if (rewrite_uri(_m, uri) < 0) { LOG(L_ERR, "after_strict: Error while rewriting request URI\n"); return RR_ERROR; } if (rt->next) { rem_off = hdr->body.s; rem_len = rt->next->nameaddr.name.s - hdr->body.s; } else { rem_off = hdr->name.s; rem_len = hdr->len; } if (!del_lump(_m, rem_off - _m->buf, rem_len, 0)) { LOG(L_ERR, "after_strict: Cannot remove Route HF\n"); return RR_ERROR; } } else { DBG("after_strict: Next hop: '%.*s' is loose router\n", uri->len, ZSW(uri->s)); if (set_dst_uri(_m, uri) < 0) { LOG(L_ERR, "after_strict: Error while setting dst_uri\n"); return RR_ERROR; } /* Next hop is a loose router - Which means that is is not endpoint yet * In This case we have to recover from previous strict routing, that * means we have to find the last Route URI and put in in R-URI and * remove the last Route URI. */ if (rt != hdr->parsed) { /* There is a previous route uri which was 2nd uri of mine * and must be removed here */ rem_off = hdr->body.s; rem_len = rt->nameaddr.name.s - hdr->body.s; if (!del_lump(_m, rem_off - _m->buf, rem_len, 0)) { LOG(L_ERR, "after_strict: Can't remove Route HF\n"); return RR_ERROR; } } res = find_rem_target(_m, &hdr, &rt, &prev); if (res < 0) { LOG(L_ERR, "after_strict: Error while looking for last Route URI\n"); return RR_ERROR; } else if (res > 0) { /* No remote target is an error */ return RR_ERROR; } uri = &rt->nameaddr.uri; if (rewrite_uri(_m, uri) < 0) { LOG(L_ERR, "after_strict: Can't rewrite R-URI\n"); return RR_ERROR; } /* The first character if uri will be either '<' when it is the only URI in a * Route header field or ',' if there is more than one URI in the header field */ DBG("after_strict: The last route URI: '%.*s'\n", rt->nameaddr.uri.len, ZSW(rt->nameaddr.uri.s)); if (prev) { rem_off = prev->nameaddr.name.s + prev->len; rem_len = rt->nameaddr.name.s + rt->len - rem_off; } else { rem_off = hdr->name.s; rem_len = hdr->len; } if (!del_lump(_m, rem_off - _m->buf, rem_len, 0)) { LOG(L_ERR, "after_strict: Can't remove Route HF\n"); return RR_ERROR; } } return RR_DRIVEN; } static inline int after_loose(struct sip_msg* _m, struct sip_uri* _pru, int _route_myself, int _ruri_myself) { struct hdr_field* hdr; rr_t* rt; int res; #ifdef ENABLE_USER_CHECK int ret; #endif str* uri; str avp_cookie; struct sip_uri parsed_uri; hdr = _m->route; rt = (rr_t*)hdr->parsed; uri = &rt->nameaddr.uri; /* IF the URI was added by me, remove it */ if (_route_myself == 1) { store_user_in_avps(&(_pru->user)); DBG("after_loose: Topmost route URI: '%.*s' is me\n", uri->len, ZSW(uri->s)); get_avp_cookie_from_uri(&(_pru->params), &avp_cookie); if (avp_cookie.len > 0) rr_set_avp_cookies(&avp_cookie, get_direction(_m, &(_pru->params))); if (!rt->next) { /* No next route in the same header, remove the whole header * field immediately */ if (!del_lump(_m, hdr->name.s - _m->buf, hdr->len, 0)) { LOG(L_ERR, "after_loose: Can't remove Route HF\n"); return RR_ERROR; } res = find_next_route(_m, &hdr); if (res < 0) { LOG(L_ERR, "after_loose: Error while finding next route\n"); return RR_ERROR; } if (res > 0) { /* No next route found */ DBG("after_loose: No next URI found\n"); if (_ruri_myself == 1) { /* this a preloaded request. we do not check for the To-tag * because the ACK for a negative reply will contain such * a tag but the original INVITE not */ return NOT_RR_DRIVEN; } else { return RR_DRIVEN; } } rt = (rr_t*)hdr->parsed; } else rt = rt->next; if (enable_double_rr && is_2rr(&(_pru->params))) { if (rr_force_send_socket) { if (parse_uri(rt->nameaddr.uri.s, rt->nameaddr.uri.len, &parsed_uri) < 0 ) { LOG(L_ERR, "after_loose: Error while parsing the second route header\n"); return RR_ERROR; } set_force_socket(_m, grep_sock_info(&parsed_uri.host, parsed_uri.port_no, parsed_uri.proto) ); if (_m->force_send_socket == 0) LOG(L_WARN, "after_loose: send socket cannot be set" " based on the second route header\n"); /* Do not return error because there is still a chance * that the outgoing socket will be correct, especially * if mhomed is turned on. It can happen that the Route HF * contains a domain name as opposed to ip address therefore * the outgoing socket cannot be determined (easily) from the URI. * (Miklos) */ } if (!rt->next) { /* No next route in the same header, remove the whole header * field immediately */ if (!del_lump(_m, hdr->name.s - _m->buf, hdr->len, 0)) { LOG(L_ERR, "after_loose: Can't remove Route HF\n"); return RR_ERROR; } res = find_next_route(_m, &hdr); if (res < 0) { LOG(L_ERR, "after_loose: Error while finding next route\n"); return RR_ERROR; } if (res > 0) { /* No next route found */ DBG("after_loose: No next URI found\n"); /* preloaded routes can not happen with double_rr, so * we were just the last hop with double_rr */ return RR_DRIVEN; } rt = (rr_t*)hdr->parsed; } else rt = rt->next; } if(rt != _m->route->parsed) { uri = &rt->nameaddr.uri; if (parse_uri(uri->s, uri->len, _pru) < 0) { LOG(L_ERR, "after_loose: Error while parsing the next route URI\n"); return RR_ERROR; } } } else { #ifdef ENABLE_USER_CHECK /* check if it the ignored user */ if(ret < 0) return NOT_RR_DRIVEN; #endif DBG("after_loose: Topmost URI is NOT myself\n"); } store_next_route_in_avps(uri); DBG("after_loose: URI to be processed: '%.*s'\n", uri->len, ZSW(uri->s)); if (is_strict(&(_pru->params))) { DBG("after_loose: Next URI is a strict router\n"); if (handle_sr(_m, hdr, rt) < 0) { LOG(L_ERR, "after_loose: Error while handling strict router\n"); return RR_ERROR; } } else { /* Next hop is loose router */ DBG("after_loose: Next URI is a loose router\n"); if (set_dst_uri(_m, uri) < 0) { LOG(L_ERR, "after_loose: Error while setting dst_uri\n"); return RR_ERROR; } /* dst_uri changed, so it makes sense to re-use the current uri for forking */ ruri_mark_new(); /* re-use uri for serial forking */ /* There is a previous route uri which was 2nd uri of mine * and must be removed here */ if (rt != hdr->parsed) { if (!del_lump(_m, hdr->body.s - _m->buf, rt->nameaddr.name.s - hdr->body.s, 0)) { LOG(L_ERR, "after_loose: Can't remove Route HF\n"); return RR_ERROR; } } } return RR_DRIVEN; } /* * Do loose routing as defined in RFC3621 */ int loose_route(struct sip_msg* _m, char* _s1, char* _s2) { struct hdr_field* hdr; struct sip_uri puri; rr_t* rt; int ret; str* uri; if (find_first_route(_m) != 0) { DBG("loose_route: There is no Route HF\n"); return -1; } if (parse_sip_msg_uri(_m) == -1) { LOG(L_ERR, "loose_route: Error while parsing Request URI\n"); return -1; } hdr = _m->route; rt = (rr_t*)hdr->parsed; uri = &rt->nameaddr.uri; if (parse_uri(uri->s, uri->len, &puri) < 0) { LOG(L_ERR, "loose_route: Error while parsing the first route URI\n"); return -1; } if (is_myself(&_m->parsed_uri.host, _m->parsed_uri.port_no)) { DBG("loose_route: RURI is myself\n"); if ((ret = is_myself(&puri.host, puri.port_no)) == 1 && !(enable_double_rr && is_2rr(&puri.params))) { DBG("loose_route: found preloaded loose route\n"); return after_loose(_m, &puri, ret, 1); } else { if (has_to_tag(_m) == 1) { return after_strict(_m, &puri, ret); } else { LOG(L_WARN, "loose_route: pre-loaded strict routing?!\n"); return -1; } } } else { DBG("loose_route: RURI is NOT myself\n"); if (is_myself(&puri.host, puri.port_no)) { return after_loose(_m, &puri, 1, 0); } else { store_next_route_in_avps(uri); LOG(L_WARN, "loose_route: no routing target is local\n"); return -1; } } } kamailio-4.0.4/obsolete/rr/README0000644000000000000000000001204312223032460015114 0ustar rootroot1. RR Module Jan Janak FhG FOKUS Copyright © 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. Parameters 1.2.1. enable_full_lr (integer) 1.2.2. append_fromtag (integer) 1.2.3. enable_double_rr (integer) 1.2.4. user_part_avp (string) 1.2.5. next_route_avp (string) 1.2.6. cookie_secret (string) 1.2.7. force_send_socket (integer) 1.3. Functions 1.3.1. loose_route() 1.3.2. strict_route() -- deprecated 1.3.3. record_route() 1.3.4. record_route_preset(string) 1.1. Overview The module contains record routing logic 1.2. Parameters 1.2.1. enable_full_lr (integer) If set to 1 then ;lr=on instead of just ;lr will be used. This is to overcome problems with broken UAs which strip ;lr parameter when generating Route header fields from Record-Route (;lr=on seems to help). Default value is 0 (no). Example 1. Set enable_full_lr parameter ... modparam("rr", "enable_full_lr", 1) ... 1.2.2. append_fromtag (integer) if turned on, request's from-tag is appended to record-route; that's useful for understanding whether subsequent requests (such as BYE) come from caller (route's from-tag==BYE's from-tag) or callee (route's from-tag==BYE's to-tag) Default value is 1 (yes). Example 2. Set append_fromtag parameter ... modparam("rr", "append_fromtag", 0) ... 1.2.3. enable_double_rr (integer) There are some situations when the server needs to insert two Record-Route header fields instead of one. For example when using two disconnected networks or doing cross-protocol forwarding from UDP->TCP. This parameter enables inserting of 2 Record-Routes. The server will later remove both of them. Default value is 1 (yes). Example 3. Set enable_double_rr parameter ... modparam("rr", "enable_double_rr", 0) ... 1.2.4. user_part_avp (string) If this parameter is set, the loose_route call stores the user part of the route URI in the AVP, which is identified by the parameter value. It can be utilized later in the routing script. Default value is NULL (do not insert user part into avps). Example 4. Set user_part_avp parameter, later use ... modparam("rr", "user_part_avp", "route_user") ... route{ ... if (loose_route()) { t_relay(); } if ($route_user == "my_user") { ... } ... } 1.2.5. next_route_avp (string) If this parameter is set, the loose_route call stores the URI of the next hop in the AVP, which is identified by the parameter value. It can be then utilized in the routing script. Default value is NULL (do not insert next route into avps). Example 5. Set next_route_avp parameter, later use ... modparam("rr", "next_route_avp", "route_next") ... route{ ... loose_route(); if ($route_next=~"101.102.103.1[0-9][0-9]") { t_relay(); } 1.2.6. cookie_secret (string) Secret to distinguish cookies from different servers. Default value is empty string. Example 6. Set cookie_secret parameter, later use ... modparam("rr", "cookie_secret", "bflmpsvz") ... 1.2.7. force_send_socket (integer) If set to 1 and two Record-Route headers have been inserted by the proxy before then the outgoing socket is forced by loose_route() to be the address of the second Route header in the in-dialog requests. Useful on multihomed hosts. Default value is 1 (yes). Example 7. Set force_send_socket parameter ... modparam("rr", "force_send_socket", 1) ... 1.3. Functions 1.3.1. loose_route() The function performs loose routing as defined in RFC3261 and will set Avp value passed in Route header that were created by record_route. If ftag!=tag.from then from/to are flipped. Example 8. loose_route usage ... loose_route(); ... 1.3.2. strict_route() -- deprecated If there are any Route HFs in the message, the function takes the first one, rewrites Request-URI with it's value and removes the first URI from Route HFs. Example 9. strict_route usage ... strict_route(); ... 1.3.3. record_route() The function adds a new Record-Route header field. The header field will be inserted in the message before any other Record-Route header fields. Avp marked using setavpflag, flag dialog_cookie will be inserted as serialized parameter of record-route header. Note that only user class AVPs should be passed as cookies, i.e. domain and global should be avoided. Example 10. record_route usage avpflags dialog_cookie; # must be declared when used in script ... setavpflag($cookie, "dialog_cookie"); setavpflag("$f./^dlg_/", "dialog_cookie"); record_route(); ... 1.3.4. record_route_preset(string) This function will put the string into Record-Route, don't use unless you know what you are doing. Meaning of the parameters is as follows: * string - String to be inserted into the header field. Example 11. record_route_preset usage ... record_route_preset("1.2.3.4:5090"); ... kamailio-4.0.4/obsolete/rr/record.h0000644000000000000000000000350512223032460015666 0ustar rootroot/* * $Id$ * * Route & Record-Route module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-04-04 Extracted from common.[ch] (janakj) */ #ifndef RECORD_H #define RECORD_H #include "../../parser/msg_parser.h" /* * Insert a new Record-Route header field with lr parameter */ int record_route(struct sip_msg* _m, char* _s1, char* _s2); /* * Insert manually created Record-Route header, no checks, no restrictions, * always adds lr parameter, only fromtag is added automatically when requested */ int record_route_preset(struct sip_msg* _m, char* _ip, char* _s2); /* * Insert a new Record-Route header field without lr parameter */ int record_route_strict(struct sip_msg* _m, char* _s1, char* _s2); /* * Remove Record-Route header from message lumps */ int remove_record_route(struct sip_msg* _m, char* _s1, char* _s2); #endif /* RECORD_H */ kamailio-4.0.4/obsolete/rr/rr_mod.h0000644000000000000000000000327212223032460015673 0ustar rootroot/* * Record-Route & Route module interface * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2003-03-15 License added (janakj) * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) */ #ifndef RR_MOD_H #define RR_MOD_H #include "../../usr_avp.h" #include "../domain/domain.h" #include "../../sr_module.h" extern avp_ident_t user_part_avp_ident; extern avp_ident_t next_route_avp_ident; extern int append_fromtag; extern int enable_double_rr; extern int enable_full_lr; extern int rr_force_send_socket; extern domain_get_did_t dm_get_did; extern fparam_t* fparam_username; #define AVP_FLAG_DIALOG_COOKIE "dialog_cookie" #endif /* RR_MOD_H */ kamailio-4.0.4/obsolete/rr/rr_mod.c0000644000000000000000000001526212223032460015670 0ustar rootroot/* * Route & Record-Route module * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* History: * -------- * 2003-03-11 updated to the new module interface (andrei) * 2003-03-16 flags export parameter added (janakj) * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei) * 2003-04-01 Added record_route with ip address parameter (janakj) * 2003-04-14 enable_full_lr parameter introduced (janakj) */ #include #include #include "rr_mod.h" #include "../../sr_module.h" #include "../../ut.h" #include "../../error.h" #include "../../mem/mem.h" #include "loose.h" #include "record.h" #include "avp_cookie.h" #include /* for regex */ #include #include "../../script_cb.h" #include "../../usr_avp.h" #include "../../crc.h" #include "../../select.h" #include "../domain/domain.h" #include "../../select_buf.h" int append_fromtag = 1; int enable_double_rr = 1; /* Enable using of 2 RR by default */ int enable_full_lr = 0; /* Disabled by default */ str add_username = STR_NULL; /* Do not add username by default */ char *cookie_filter = 0; /* filter cookies restored in loose_route, potential security problem */ str user_part_avp = STR_NULL; /* AVP identification where user part of Route URI is stored after loose/strict routing */ str next_route_avp = STR_NULL; /* AVP identification where next route (if exists) would be stored after loose/strict routing */ static str crc_secret_str = STR_NULL; avp_ident_t user_part_avp_ident; avp_ident_t next_route_avp_ident; int rr_force_send_socket = 1; /* Force the send socket if 2 RR was added */ fparam_t* fparam_username = NULL; MODULE_VERSION static int mod_init(void); domain_get_did_t dm_get_did = 0; /* * Exported functions */ /* * I do not want people to use strict routing so it is disabled by default, * you should always use loose routing, if you really need strict routing then * you can replace the last zeroes with REQUEST_ROUTE to enable strict_route and * record_route_strict. Don't do that unless you know what you are really doing ! * Oh, BTW, have I mentioned already that you shouldn't use strict routing ? */ static cmd_export_t cmds[] = { {"loose_route", loose_route, 0, 0, REQUEST_ROUTE}, {"record_route", record_route, 0, 0, REQUEST_ROUTE}, {"record_route_preset", record_route_preset, 1, fixup_str_1, REQUEST_ROUTE}, {"record_route_strict" , record_route_strict, 0, 0, 0 }, {"remove_record_route", remove_record_route, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] ={ {"append_fromtag", PARAM_INT, &append_fromtag }, {"enable_double_rr", PARAM_INT, &enable_double_rr}, {"enable_full_lr", PARAM_INT, &enable_full_lr }, #ifdef ENABLE_USER_CHECK {"ignore_user", PARAM_STR, &i_user }, #endif {"add_username", PARAM_STR, &add_username }, {"cookie_filter", PARAM_STRING, &cookie_filter }, {"cookie_secret", PARAM_STR, &crc_secret_str }, {"user_part_avp", PARAM_STR, &user_part_avp }, {"next_route_avp", PARAM_STR, &next_route_avp }, {"force_send_socket", PARAM_INT, &rr_force_send_socket }, {0, 0, 0 } }; struct module_exports exports = { "rr", cmds, /* Exported functions */ 0, /* RPC methods */ params, /* Exported parameters */ mod_init, /* initialize module */ 0, /* response function*/ 0, /* destroy function */ 0, /* oncancel function */ 0 /* per-child init function */ }; static ABSTRACT_F(select_rrmod) static int select_rr_avpcookie(str* res, struct select* s, struct sip_msg* msg) { str *s2; int ret; s2 = rr_get_avp_cookies(); if (s2) { ret = str_to_static_buffer(res, s2); pkg_free(s2); return ret; } else return -1; } static select_row_t rr_select_table[] = { { NULL, SEL_PARAM_STR, STR_STATIC_INIT("rr"), select_rrmod, SEL_PARAM_EXPECTED}, { select_rrmod, SEL_PARAM_STR, STR_STATIC_INIT("dialog_cookie"), select_rr_avpcookie, 0}, { NULL, SEL_PARAM_INT, STR_NULL, NULL, 0} }; static int mod_init(void) { void* param; DBG("rr - initializing\n"); crc_secret = crcitt_string(crc_secret_str.s, crc_secret_str.len); if (cookie_filter && strlen(cookie_filter)) { cookie_filter_re = (regex_t*)pkg_malloc(sizeof(regex_t)); memset(cookie_filter_re, 0, sizeof(regex_t)); if (regcomp(cookie_filter_re, cookie_filter, REG_EXTENDED|REG_ICASE|REG_NEWLINE) ) { LOG(L_ERR, "ERROR: %s : bad cookie_filter regex '%s'\n", exports.name, cookie_filter); return E_BAD_RE; } } memset (&user_part_avp_ident, 0, sizeof(avp_ident_t)); if (user_part_avp.s && user_part_avp.len) { if (parse_avp_ident(&user_part_avp, &user_part_avp_ident)!=0) { ERR("modparam \"user_part_avp\" : error while parsing\n"); return E_CFG; } } memset (&next_route_avp_ident, 0, sizeof(avp_ident_t)); if (next_route_avp.s && next_route_avp.len) { if (parse_avp_ident(&next_route_avp, &next_route_avp_ident)!=0) { ERR("modparam \"next_route_avp\" : error while parsing\n"); return E_CFG; } } avp_flag_dialog = register_avpflag(AVP_FLAG_DIALOG_COOKIE); if (avp_flag_dialog == 0) { LOG(L_ERR, "ERROR: %s: cannot register avpflag \"%s\"\n", exports.name, AVP_FLAG_DIALOG_COOKIE); return E_CFG; } register_select_table(rr_select_table); dm_get_did = (domain_get_did_t)find_export("get_did", 0, 0); if (!dm_get_did) { DBG("Domain module not found, rr support for multidomain disabled\n"); } if (add_username.s) { param=(void*)add_username.s; if (fixup_var_str_12(¶m,1)<0) { ERR("rr:mod_init:can't fixup add_username parameter\n"); return E_CFG; } fparam_username=(fparam_t*)param; } return 0; } kamailio-4.0.4/obsolete/rr/fix_lumps.h0000644000000000000000000000613712223032460016422 0ustar rootroot/* * $Id$ * * here, we delete message lumps which are generated in * core functions using pkg_malloc and applied to shmem * requests; not doing so would result ugly memory problems * * I admit it is not a nice hack; -jiri * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * ........ * 2006-04-19: copy-pasted form TM module (Miklos) */ #ifndef _FIX_LUMPS_H #define _FIX_LUMPS_H inline static void free_rr_lump( struct lump **list ) { struct lump *prev_lump, *lump, *a, *foo, *next; int first_shmem; first_shmem=1; next=0; prev_lump=0; for(lump=*list;lump;lump=next) { next=lump->next; if (lump->type==HDR_RECORDROUTE_T) { /* may be called from railure_route */ /* if (lump->flags & (LUMPFLAG_DUPED|LUMPFLAG_SHMEM)){ LOG(L_CRIT, "BUG: free_rr_lmp: lump %p, flags %x\n", lump, lump->flags); */ /* ty to continue */ /*}*/ a=lump->before; while(a) { foo=a; a=a->before; if (!(foo->flags&(LUMPFLAG_DUPED|LUMPFLAG_SHMEM))) free_lump(foo); if (!(foo->flags&LUMPFLAG_SHMEM)) pkg_free(foo); } a=lump->after; while(a) { foo=a; a=a->after; if (!(foo->flags&(LUMPFLAG_DUPED|LUMPFLAG_SHMEM))) free_lump(foo); if (!(foo->flags&LUMPFLAG_SHMEM)) pkg_free(foo); } if (first_shmem && (lump->flags&LUMPFLAG_SHMEM)) { /* This is the first element of the shmemzied lump list, we can not unlink it! It wound corrupt the list otherwise if we are in failure_route. -- No problem, only the anchor is left in the list */ LOG(L_DBG, "DEBUG: free_rr_lump: lump %p" \ " is left in the list\n", lump); if (lump->len) LOG(L_CRIT, "BUG: free_rr_lump: lump %p" \ " can not be removed, but len=%d\n", lump, lump->len); prev_lump=lump; } else { if (prev_lump) prev_lump->next = lump->next; else *list = lump->next; if (!(lump->flags&(LUMPFLAG_DUPED|LUMPFLAG_SHMEM))) free_lump(lump); if (!(lump->flags&LUMPFLAG_SHMEM)) pkg_free(lump); } } else { /* store previous position */ prev_lump=lump; } if (first_shmem && (lump->flags&LUMPFLAG_SHMEM)) first_shmem=0; } } #endif kamailio-4.0.4/obsolete/rr/loose.h0000644000000000000000000000240612223032460015530 0ustar rootroot/* * Route & Record-Route module, loose routing support * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef LOOSE_H #define LOOSE_H #include "../../parser/msg_parser.h" /* * Do loose routing as per RFC3621 */ int loose_route(struct sip_msg* _m, char* _s1, char* _s2); #endif /* LOOSE_H */ kamailio-4.0.4/obsolete/rr/avp_cookie.h0000644000000000000000000000320012223032460016517 0ustar rootroot/* * Route & Record-Route module, avp cookie support * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AVP_COOKIE_H #define AVP_COOKIE_H #include "../../sr_module.h" #include "../../usr_avp.h" #include /* for regex */ #include typedef struct avp_save_item_t { unsigned short type; union { str s; regex_t re; int n; } u; } avp_save_item_t; extern regex_t* cookie_filter_re; extern unsigned short crc_secret; extern avp_flags_t avp_flag_dialog; int rr_add_avp_cookie(struct sip_msg *msg, char *param1, char *param2); str *rr_get_avp_cookies(void); void rr_set_avp_cookies(str *enc_cookies, int reverse_direction); #endif // AVP_COOKIE_H kamailio-4.0.4/obsolete/rr/avp_cookie.c0000644000000000000000000001656212223032460016531 0ustar rootroot/* * Route & Record-Route module, avp cookie support * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "avp_cookie.h" #include "../../lib/cds/base64.h" #include "../../crc.h" #include "../../usr_avp.h" #include "../../mem/mem.h" #define AVP_COOKIE_NAME "avp=" #define AVP_COOKIE_BUFFER 1024 #define CRC_LEN 4 unsigned short crc_secret = 0; regex_t* cookie_filter_re = 0; avp_flags_t avp_flag_dialog = 0; #define MAX_AVP_DIALOG_LISTS 4 static unsigned short avp_dialog_lists[MAX_AVP_DIALOG_LISTS] = {AVP_CLASS_URI|AVP_TRACK_FROM, AVP_CLASS_URI|AVP_TRACK_TO, AVP_CLASS_USER|AVP_TRACK_FROM, AVP_CLASS_USER|AVP_TRACK_TO}; typedef char rr_avp_flags_t; str *rr_get_avp_cookies(void) { unsigned short crc, ll; static char buf[AVP_COOKIE_BUFFER]; int len, l, avp_list_no; struct usr_avp *avp; int_str avp_val; str *avp_name; str *result = 0; rr_avp_flags_t avp_flags; struct str_int_data *sid; struct str_str_data *ssd; len = sizeof(crc); for (avp_list_no=0; avp_list_nonext ) { if ( (avp->flags & avp_flag_dialog) == 0) continue; if ((avp->flags&(AVP_NAME_STR|AVP_VAL_STR)) == AVP_NAME_STR) { /* avp type str, int value */ sid = (struct str_int_data*)&avp->d.data[0]; avp_name = &sid->name; } else if ((avp->flags&(AVP_NAME_STR|AVP_VAL_STR)) == (AVP_NAME_STR|AVP_VAL_STR)) { /* avp type str, str value */ ssd = (struct str_str_data*)&avp->d.data[0]; avp_name = &ssd->name; } else avp_name = 0; /* dummy */ get_avp_val(avp, &avp_val); l = sizeof(rr_avp_flags_t); if (avp->flags & AVP_NAME_STR ) l += avp_name->len+sizeof(unsigned short); else l += sizeof(avp->id); if (avp->flags & AVP_VAL_STR ) l += avp_val.s.len+sizeof(unsigned short); else l += sizeof(avp_val.n); if (len+l > AVP_COOKIE_BUFFER) { LOG(L_ERR, "rr:get_avp_cookies: not enough memory to prepare all cookies\n"); goto brk; } avp_flags = (avp->flags & 0x0F)|(avp_list_no << 4); memcpy(buf+len, &avp_flags, sizeof(rr_avp_flags_t)); len += sizeof(rr_avp_flags_t); if (avp->flags & AVP_NAME_STR) { if (avp_name->len > 0xFFFF) ll = 0xFFFF; else ll = avp_name->len; memcpy(buf+len, &ll, sizeof(ll)); len+= sizeof(ll); memcpy(buf+len, avp_name->s, ll); len+= ll; } else { memcpy(buf+len, &avp->id, sizeof(avp->id)); len+= sizeof(avp->id); } if (avp->flags & AVP_VAL_STR) { if (avp_val.s.len > 0xFFFF) ll = 0xFFFF; else ll = avp_val.s.len; memcpy(buf+len, &ll, sizeof(ll)); len+= sizeof(ll); memcpy(buf+len, avp_val.s.s, ll); len+= ll; } else { memcpy(buf+len, &avp_val.n, sizeof(avp_val.n)); len+= sizeof(avp_val.n); } } } brk: if (len > sizeof(crc)) { result = (str*) pkg_malloc(sizeof(*result) + sizeof(crc) + (len*4)/3 + 3); if (!result) { LOG(L_ERR, "rr:get_avp_cookies: not enough memory\n"); return 0; } result->s = (char*)result + sizeof(*result); crc = crcitt_string_ex(buf+sizeof(crc), len-sizeof(crc), crc_secret); memcpy(&buf, &crc, sizeof(crc)); base64encode(buf, len, result->s, &result->len, 0); DBG("avp_cookies: len=%d, crc=0x%x, base64(%u)='%.*s'\n", len, crc, result->len, result->len, result->s); } return result; } void rr_set_avp_cookies(str *enc_cookies, int reverse_direction) { char *buf; int len, pos; unsigned short crc; struct usr_avp avp; int_str avp_name, avp_val; regmatch_t pmatch; rr_avp_flags_t avp_flags; DBG("rr_set_avp_cookies: enc_cookie(%d)='%.*s'\n", enc_cookies->len, enc_cookies->len, enc_cookies->s); buf = (char*) pkg_malloc((enc_cookies->len*3)/4 + 3); if (!buf) { LOG(L_ERR, "rr:set_avp_cookies: not enough memory\n"); return; } base64decode(enc_cookies->s, enc_cookies->len, buf, &len); if (len <= sizeof(crc)) return; crc = crcitt_string_ex(buf+sizeof(crc), len-sizeof(crc), crc_secret); if (crc != *(unsigned short*) buf) { LOG(L_ERR, "rr:set_avp_cookies: bad CRC when decoding AVP cookie\n"); return; } pos = sizeof(crc); while (pos < len) { memcpy(&avp_flags, buf+pos, sizeof(avp_flags)); if ((avp_flags >> 4) >= MAX_AVP_DIALOG_LISTS) { LOG(L_ERR, "rr:set_avp_cookies: AVP cookies corrupted\n"); break; } avp.flags = (avp_flags & 0x0F) | avp_dialog_lists[avp_flags >> 4]; if (reverse_direction && (avp.flags & (AVP_CLASS_DOMAIN|AVP_CLASS_USER|AVP_CLASS_URI)) ) { avp.flags ^= AVP_TRACK_ALL; /* flip from/to flags */ } pos+= sizeof(rr_avp_flags_t); if (avp.flags & AVP_NAME_STR) { avp_name.s.len = 0; memcpy(&avp_name.s.len, buf+pos, sizeof(unsigned short)); avp_name.s.s = buf+pos+sizeof(unsigned short); pos+= sizeof(unsigned short)+avp_name.s.len; DBG("rr:set_avp_cookies: found cookie '%.*s'\n", avp_name.s.len, avp_name.s.s); } else { memcpy(&avp.id, buf+pos, sizeof(avp.id)); pos+= sizeof(avp.id); avp_name.n = avp.id; DBG("rr:set_avp_cookies: found cookie #%d\n", avp_name.n); } if (pos >= len) { LOG(L_ERR, "rr:set_avp_cookies: AVP cookies corrupted\n"); break; } if (avp.flags & AVP_VAL_STR) { avp_val.s.len = 0; memcpy(&avp_val.s.len, buf+pos, sizeof(unsigned short)); avp_val.s.s = buf+pos+sizeof(unsigned short); pos+= sizeof(unsigned short)+avp_val.s.len; } else { memcpy(&avp_val.n, buf+pos, sizeof(avp_val.n)); pos+= sizeof(avp_val.n); } if (pos > len) { LOG(L_ERR, "rr:set_avp_cookies: AVP cookies corrupted\n"); break; } /* filter cookie */ if (cookie_filter_re) { if (avp.flags & AVP_NAME_STR) { char savec; savec = avp_name.s.s[avp_name.s.len]; avp_name.s.s[avp_name.s.len] = 0; if (regexec(cookie_filter_re, avp_name.s.s, 1, &pmatch, 0) != 0) { DBG("rr:set_avp_cookies: regex doesn't match (str)\n"); avp_name.s.s[avp_name.s.len] = savec; continue; } avp_name.s.s[avp_name.s.len] = savec; } else { char buf[25]; snprintf(buf, sizeof(buf)-1, "i:%d", avp_name.n); buf[sizeof(buf)-1]=0; if (regexec(cookie_filter_re, buf, 1, &pmatch, 0) != 0) { DBG("rr:set_avp_cookies: regex doesn't match (int)\n"); continue; } } } /* set avp from cookie */ DBG("rr:set_avp_cookies: adding AVP\n"); if ( add_avp(avp.flags|avp_flag_dialog, avp_name, avp_val)!=0 ) { LOG(L_ERR, "ERROR: rr:set_avp_cookies: add_avp failed\n"); } } pkg_free(buf); } kamailio-4.0.4/obsolete/rr/Makefile0000644000000000000000000000043312223032460015674 0ustar rootroot# $Id$ # # rr module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=rr.so LIBS= SERLIBPATH=../../lib SER_LIBS=$(SERLIBPATH)/cds/ser_cds DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/maxfwd/0000755000000000000000000000000012223032460015077 5ustar rootrootkamailio-4.0.4/obsolete/maxfwd/doc/0000755000000000000000000000000012223032460015644 5ustar rootrootkamailio-4.0.4/obsolete/maxfwd/doc/functions.xml0000644000000000000000000000463212223032460020403 0ustar rootroot
Functions
<function>maxfwd_process(max_value)</function> If no Max-Forward header is present in the received request, a header will be added having the original value equal with "max_value". An OK code is returned by the function. If a Max-Forward header is already present, its value will be decremented. If after this operation its value will be positive non-zero, an OK code will be returned. Otherwise (for a zero value) an error code will be returned. Note that an error code will be also returned if the SIP message couldn't be parsed or if the Max-Forward header's body invalid (non numerical string or negative numerical value). Meaning of the parameters is as follows: max_value - Value to be added if there is no Max-Forwards header field in the message. <function>maxfwd_process</function> usage # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!maxfwd_process(10)) { sl_send_reply(483, "Too many hops"); drop; };
<function>maxfwd_at_least(min_value)</function> Test is there is enough hops in Max-forward header, i.e. we know how many hops is required to reach target. Returns true if Max-Forward header is present in the received request and the value is greater than "min_value". Meaning of the parameters is as follows: min_value - Min. number of required hops <function>maxfwd_at_least</function> usage if (!maxfwd_at_least(3)) { sl_send_reply(483, "Too many hops"); drop; };
kamailio-4.0.4/obsolete/maxfwd/doc/maxfwd.xml0000644000000000000000000000170012223032460017652 0ustar rootroot
Bogdan Iancu FhG FOKUS
iancu@fokus.fraunhofer.de
2003 FhG FOKUS
Maxfwd Module
Overview The module implements all the operations regarding MaX-Forward header field, like adding it (if not present) or decrementing and checking the value of the existent one.
kamailio-4.0.4/obsolete/maxfwd/doc/Makefile0000644000000000000000000000012712223032460017304 0ustar rootrootdocs = maxfwd.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/maxfwd/doc/params.xml0000644000000000000000000000124112223032460017647 0ustar rootroot
Parameters
<varname>max_limit</varname> (integer) Max.value of Max-forward, if received value is greater then value is decraesed to max_limit. Value of 0 disables this functionality. Default value is 16.
kamailio-4.0.4/obsolete/maxfwd/README0000644000000000000000000000364712223032460015771 0ustar rootroot1. Maxfwd Module Bogdan Iancu FhG FOKUS Copyright © 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. Functions 1.2.1. maxfwd_process(max_value) 1.2.2. maxfwd_at_least(min_value) 1.1. Overview The module implements all the operations regarding MaX-Forward header field, like adding it (if not present) or decrementing and checking the value of the existent one. 1.2. Functions 1.2.1. maxfwd_process(max_value) If no Max-Forward header is present in the received request, a header will be added having the original value equal with "max_value". An OK code is returned by the function. If a Max-Forward header is already present, its value will be decremented. If after this operation its value will be positive non-zero, an OK code will be returned. Otherwise (for a zero value) an error code will be returned. Note that an error code will be also returned if the SIP message couldn't be parsed or if the Max-Forward header's body invalid (non numerical string or negative numerical value). Meaning of the parameters is as follows: * max_value - Value to be added if there is no Max-Forwards header field in the message. Example 1. maxfwd_process usage # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!maxfwd_process(10)) { sl_send_reply(483, "Too many hops"); drop; }; 1.2.2. maxfwd_at_least(min_value) Test is there is enough hops in Max-forward header, i.e. we know how many hops is required to reach target. Returns true if Max-Forward header is present in the received request and the value is greater than "min_value". Meaning of the parameters is as follows: * min_value - Min. number of required hops Example 2. maxfwd_at_least usage if (!maxfwd_at_least(3)) { sl_send_reply(483, "Too many hops"); drop; }; kamailio-4.0.4/obsolete/maxfwd/maxfwd.c0000644000000000000000000001144412223032460016535 0ustar rootroot/* * $Id$ * * maxfwd module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2003-03-11 updated to the new module interface (andrei) * 2003-03-16 flags export parameter added (janakj) * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei) * 2004-08-15 max value of max-fwd header is configurable via max_limit * module param (bogdan) * 2008-02-26 support for cfg API (tma) */ #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "mf_funcs.h" #include "../../cfg/cfg.h" MODULE_VERSION #define MODULE_NAME "maxfwd" struct cfg_group_maxfwd { int max_limit; }; static struct cfg_group_maxfwd default_maxfwd_cfg = { max_limit:16 }; static void *maxfwd_cfg = &default_maxfwd_cfg; static cfg_def_t maxfwd_cfg_def[] = { {"max_limit", CFG_VAR_INT, 0, 255, 0, 0, "Max. maxfwd limit"}, {0, 0, 0, 0, 0, 0} }; static int process_maxfwd_header(struct sip_msg* msg, char* str, char* str2); static int check_lowlimit(struct sip_msg* msg, char* str, char* str2); static int mod_init(void); static cmd_export_t cmds[]={ {MODULE_NAME"_process", process_maxfwd_header, 1, fixup_var_int_1, REQUEST_ROUTE}, {MODULE_NAME"_at_least", check_lowlimit, 1, fixup_var_int_1, REQUEST_ROUTE}, /* backward compatability only */ {"mf_process_maxfwd_header", process_maxfwd_header, 1, fixup_var_int_1, REQUEST_ROUTE}, {"process_maxfwd", process_maxfwd_header, 1, fixup_var_int_1, REQUEST_ROUTE}, {"mf_lowlimit", check_lowlimit, 1, fixup_var_int_1, REQUEST_ROUTE}, {0,0,0,0,0} }; static param_export_t params[]={ {"max_limit", PARAM_INT, &default_maxfwd_cfg.max_limit}, {0,0,0} }; #ifdef STATIC_MAXFWD struct module_exports MODULE_NAME##_exports = { #else struct module_exports exports= { #endif MODULE_NAME, cmds, 0, /* RPC methods */ params, mod_init, (response_function) 0, (destroy_function) 0, 0, 0 /* per-child init function */ }; static int mod_init(void) { DBG(MODULE_NAME": initializing\n"); /* declare the configuration */ if (cfg_declare(MODULE_NAME, maxfwd_cfg_def, &default_maxfwd_cfg, cfg_sizeof(maxfwd), &maxfwd_cfg)) { ERR(MODULE_NAME": mod_init: failed to declare the configuration\n"); return E_UNSPEC; } return E_OK; } static int process_maxfwd_header(struct sip_msg* msg, char* str1, char* str2) { int val, tmp; str mf_value; int max_limit; val = is_maxfwd_present(msg, &mf_value); switch (val) { case -1: if (get_int_fparam(&tmp, msg, (fparam_t*) str1) < 0) return -1; if (tmp < 0 || tmp > 255) { ERR(MODULE_NAME": number (%d) beyond range <0,255>\n", tmp); return -1; } if (tmp == 0) return 0; max_limit = cfg_get(maxfwd, maxfwd_cfg, max_limit); if ( max_limit && tmp > max_limit) { ERR(MODULE_NAME": default value (%d) greater than max.limit (%d)\n", tmp, max_limit); return -1; } add_maxfwd_header(msg, tmp); break; case -2: break; case 0: return -1; default: max_limit = cfg_get(maxfwd, maxfwd_cfg, max_limit); if (max_limit && val > max_limit){ DBG(MODULE_NAME": process_maxfwd_header: " "value %d decreased to %d\n", val, max_limit); val = max_limit+1; } if ( decrement_maxfwd(msg, val, &mf_value)!=1 ) ERR(MODULE_NAME": process_maxfwd_header: " "decrement failed\n"); } return 1; } /* check if the current Max Forwards value is below/above a certain threshold */ static int check_lowlimit(struct sip_msg* msg, char* str1, char* str2) { int val, lowlimit; str mf_value; val = is_maxfwd_present(msg, &mf_value); switch (val) { case -2: /* parsing error */ return -1; case -1: /* header not present */ return 1; default: if (get_int_fparam(&lowlimit, msg, (fparam_t*) str1) < 0) return -1; DBG(MODULE_NAME": check_low_limit(%d): current=%d\n", lowlimit, val); return ((val >= 0) && (val < lowlimit))?-1:1; } } kamailio-4.0.4/obsolete/maxfwd/mf_funcs.h0000644000000000000000000000256312223032460017056 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _MF_FUNCS_H #define _MF_FUNCS_H #include "../../parser/msg_parser.h" #include "../../dprint.h" #include "../../config.h" #include "../../str.h" int decrement_maxfwd( struct sip_msg* msg, int nr_val, str *str_val ); int add_maxfwd_header( struct sip_msg* msg , unsigned int val ); int is_maxfwd_present( struct sip_msg* msg, str *mf_value ); #endif kamailio-4.0.4/obsolete/maxfwd/mf_funcs.c0000644000000000000000000000776012223032460017055 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ---------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2002-01-28 scratchpad removed (jiri) * 2004-08-15 max value of max-fwd header is configurable (bogdan) */ #include #include #include "../../comp_defs.h" #include "mf_funcs.h" #include "../../mem/mem.h" #include "../../ut.h" #include "../../data_lump.h" #define MF_HDR "Max-Forwards: " #define MF_HDR_LEN (sizeof(MF_HDR) - 1) /* looks for the MAX FORWARDS header returns the its value, -1 if is not present or -2 for error */ int is_maxfwd_present( struct sip_msg* msg , str *foo) { int x, err; /* lookup into the message for MAX FORWARDS header*/ if ( !msg->maxforwards ) { if ( parse_headers( msg , HDR_MAXFORWARDS_F, 0 )==-1 ){ LOG( L_ERR , "ERROR:maxfwd:is_maxfwd_present :" " parsing MAX_FORWARD header failed!\n"); return -2; } if (!msg->maxforwards) { DBG("DEBUG: is_maxfwd_present: max_forwards header not found!\n"); return -1; } } /* if header is present, trim to get only the string containing numbers */ trim_len( foo->len , foo->s , msg->maxforwards->body ); /* convert from string to number */ x = str2s( foo->s,foo->len,&err); if (err){ LOG(L_ERR, "ERROR:maxfwd:is_maxfwd_present:" " unable to parse the max forwards number !\n"); return -2; } DBG("DEBUG:maxfwd:is_maxfwd_present: value = %d \n",x); return x; } int decrement_maxfwd( struct sip_msg* msg , int x, str *s) { int i; /* double check */ if ( !msg->maxforwards ) { LOG( L_ERR , "ERROR: decrement_maxfwd :" " MAX_FORWARDS header not found !\n"); goto error; } /*rewriting the max-fwd value in the message (buf and orig)*/ x--; for(i = s->len - 1; i >= 0; i--) { s->s[i] = (x % 10) + '0'; x /= 10; if (x==0) { i = i - 1; break; } } while(i >= 0) s->s[i--] = ' '; return 1; error: return -1; } int add_maxfwd_header( struct sip_msg* msg , unsigned int val ) { unsigned int len; char *buf; struct lump* anchor; /* double check just to be sure */ if ( msg->maxforwards ) { LOG( L_ERR , "ERROR: add_maxfwd_header :" " MAX_FORWARDS header already exists (%p) !\n",msg->maxforwards); goto error; } /* constructing the header */ len = MF_HDR_LEN /*"MAX-FORWARDS: "*/+ CRLF_LEN + 3/*val max on 3 digits*/; buf = (char*)pkg_malloc( len ); if (!buf) { LOG(L_ERR, "ERROR : add_maxfwd_header : No memory left\n"); return -1; } memcpy( buf , MF_HDR, MF_HDR_LEN ); len = MF_HDR_LEN ; len += btostr( buf+len , val ); memcpy( buf+len , CRLF , CRLF_LEN ); len +=CRLF_LEN; /*inserts the header at the beginning of the message*/ anchor = anchor_lump(msg, msg->headers->name.s - msg->buf, 0 , 0); if (anchor == 0) { LOG(L_ERR, "ERROR: add_maxfwd_header :" " Error, can't get anchor\n"); goto error1; } if (insert_new_lump_before(anchor, buf, len, 0) == 0) { LOG(L_ERR, "ERROR: add_maxfwd_header : " "Error, can't insert MAX-FORWARDS\n"); goto error1; } return 1; error1: pkg_free( buf ); error: return -1; } kamailio-4.0.4/obsolete/maxfwd/Makefile0000644000000000000000000000035512223032460016542 0ustar rootroot# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=maxfwd.so LIBS= DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/options/0000755000000000000000000000000012223032460015304 5ustar rootrootkamailio-4.0.4/obsolete/options/doc/0000755000000000000000000000000012223032460016051 5ustar rootrootkamailio-4.0.4/obsolete/options/doc/functions.xml0000644000000000000000000000274712223032460020615 0ustar rootroot
Functions
<function>options_reply()</function> This function checks if the request method is OPTIONS and if the request URI does not contain an username. If both is true the request will be answered stateless with "200 OK" and the capabilities from the modules parameters. It sends "500 Server Internal Error" for some errors and returns false if it is called for a wrong request. The check for the request method and the missing username is optional because it is also done by the function itself. But you should not call this function outside the myself check because in this case the function could answer OPTIONS requests which are sent to you as outbound proxy but with an other destination then your proxy (this check is currently missing in the function). <function>options_reply</function> usage
kamailio-4.0.4/obsolete/options/doc/options.xml0000644000000000000000000000320712223032460020270 0ustar rootroot
Nils Ohlmeier FhG Fokus
nils@iptel.org
2003 FhG Fokus
Options Module
Overview This module provides a function to answer OPTIONS requests which are directed to the server itself. This means an OPTIONS request which has the address of the server in the request URI, and no username in the URI. The request will be answered with a 200 OK which the capabilities of the server. To answer OPTIONS request directed to your server is the easiest way for is-alive-tests on the SIP (application) layer from remote (similar to ICMP echo requests, also known as "ping", on the network layer).
Dependencies The following modules must be loaded before this module: sl - Stateless replies.
kamailio-4.0.4/obsolete/options/doc/Makefile0000644000000000000000000000013012223032460017503 0ustar rootrootdocs = options.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/options/doc/params.xml0000644000000000000000000001003412223032460020054 0ustar rootroot
Parameters
<varname>accept</varname> (string) This parameter is the content of the Accept header field. Note: it is not clearly written in RFC3261 if a proxy should accept any content (the default "*/*") because it does not care about content. Or if it does not accept any content, which is "". Default value is "*/*". Set <varname>accept</varname> parameter ... modparam("options", "accept", "application/*") ...
<varname>accept_encoding</varname> (string) This parameter is the content of the Accept-Encoding header field. Please do not change the default value because SER does not support any encodings yet. Default value is "". Set <varname>accept_encoding</varname> parameter ... modparam("options", "accept_encoding", "gzip") ...
<varname>accept_language</varname> (string) This parameter is the content of the Accept-Language header field. You can set any language code which you prefer for error descriptions from other devices, but presumably there are not much devices around which support other languages then the default English. Default value is "en". Set <varname>accept_language</varname> parameter ... modparam("options", "accept_language", "de") ...
<varname>supported</varname> (string) This parameter is the content of the Supported header field Here you can enter the extensions which are supported by your SER config. Normally you should only use values here which are registered with the IANA. Default value is "". Set <varname>supported</varname> parameter ... modparam("options", "supported", "100rel") ...
<varname>contact</varname> (string) The content of this parameter will be inserted into the reply as the URI of the Contact header. If this parameter is empty (default case) then no Contact header in included in the reply. If this parameter is set to "dstip" the reply contains a Contact header with the IP, port and transport of the socket where the request was sent to. If this parameter is set to "ruri" the reply contains a Contact header with the same URI as the request URI of the original OPTIONS request. Default value is "". Set <varname>contact</varname> parameter ... modparam("options", "contact", "example.com") ...
<varname>contact_param</varname> (string) This parameter is appended to the value of the Contact header is case it is set. If just this parameter is set but the contact module parameter not, then the contact parameter is automatically set to dstip. Default value is "". Set <varname>contact_param</varname> parameter ... modparam("options", "contact_param", "lr=on") ...
kamailio-4.0.4/obsolete/options/README0000644000000000000000000001102212223032460016160 0ustar rootroot1. Options Module Nils Ohlmeier FhG Fokus Copyright © 2003 FhG Fokus __________________________________________________________________ 1.1. Overview 1.2. Dependencies 1.3. Parameters 1.3.1. accept (string) 1.3.2. accept_encoding (string) 1.3.3. accept_language (string) 1.3.4. supported (string) 1.3.5. contact (string) 1.3.6. contact_param (string) 1.4. Functions 1.4.1. options_reply() 1.1. Overview This module provides a function to answer OPTIONS requests which are directed to the server itself. This means an OPTIONS request which has the address of the server in the request URI, and no username in the URI. The request will be answered with a 200 OK which the capabilities of the server. To answer OPTIONS request directed to your server is the easiest way for is-alive-tests on the SIP (application) layer from remote (similar to ICMP echo requests, also known as "ping", on the network layer). 1.2. Dependencies The following modules must be loaded before this module: * sl - Stateless replies. 1.3. Parameters 1.3.1. accept (string) This parameter is the content of the Accept header field. Note: it is not clearly written in RFC3261 if a proxy should accept any content (the default "*/*") because it does not care about content. Or if it does not accept any content, which is "". Default value is "*/*". Example 1. Set accept parameter ... modparam("options", "accept", "application/*") ... 1.3.2. accept_encoding (string) This parameter is the content of the Accept-Encoding header field. Please do not change the default value because SER does not support any encodings yet. Default value is "". Example 2. Set accept_encoding parameter ... modparam("options", "accept_encoding", "gzip") ... 1.3.3. accept_language (string) This parameter is the content of the Accept-Language header field. You can set any language code which you prefer for error descriptions from other devices, but presumably there are not much devices around which support other languages then the default English. Default value is "en". Example 3. Set accept_language parameter ... modparam("options", "accept_language", "de") ... 1.3.4. supported (string) This parameter is the content of the Supported header field Here you can enter the extensions which are supported by your SER config. Normally you should only use values here which are registered with the IANA. Default value is "". Example 4. Set supported parameter ... modparam("options", "supported", "100rel") ... 1.3.5. contact (string) The content of this parameter will be inserted into the reply as the URI of the Contact header. If this parameter is empty (default case) then no Contact header in included in the reply. If this parameter is set to "dstip" the reply contains a Contact header with the IP, port and transport of the socket where the request was sent to. If this parameter is set to "ruri" the reply contains a Contact header with the same URI as the request URI of the original OPTIONS request. Default value is "". Example 5. Set contact parameter ... modparam("options", "contact", "example.com") ... 1.3.6. contact_param (string) This parameter is appended to the value of the Contact header is case it is set. If just this parameter is set but the contact module parameter not, then the contact parameter is automatically set to dstip. Default value is "". Example 6. Set contact_param parameter ... modparam("options", "contact_param", "lr=on") ... 1.4. Functions 1.4.1. options_reply() This function checks if the request method is OPTIONS and if the request URI does not contain an username. If both is true the request will be answered stateless with "200 OK" and the capabilities from the modules parameters. It sends "500 Server Internal Error" for some errors and returns false if it is called for a wrong request. The check for the request method and the missing username is optional because it is also done by the function itself. But you should not call this function outside the myself check because in this case the function could answer OPTIONS requests which are sent to you as outbound proxy but with an other destination then your proxy (this check is currently missing in the function). Example 7. options_reply usage ... if (uri==myself) { if ((method==OPTIONS) && (! uri=~"sip:.*[@]+.*")) { options_reply(); } } ... kamailio-4.0.4/obsolete/options/mod_options.c0000644000000000000000000002524412223032460020011 0ustar rootroot/* * $Id$ * * Options Reply Module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) */ #include #include "mod_options.h" #include "../../sr_module.h" #include "../../config.h" #include "../../modules/sl/sl.h" #include "../../mem/mem.h" #include "../../data_lump_rpl.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_uri.h" MODULE_VERSION static str acpt_body = STR_STATIC_INIT("*/*"); static str acpt_enc_body = STR_STATIC_INIT(""); static str acpt_lan_body = STR_STATIC_INIT("en"); static str supt_body = STR_STATIC_INIT(""); static str contact = STR_STATIC_INIT(""); static str cont_param = STR_STATIC_INIT(""); #define ADD_CONT_OFF 0 #define ADD_CONT_MODPARAM 1 #define ADD_CONT_IP 2 #define ADD_CONT_RURI 3 static int add_cont = ADD_CONT_OFF; /* * sl API structure for stateless reply */ sl_api_t slb; static int mod_init(void); static int opt_reply(struct sip_msg* _msg, char* _foo, char* _bar); /* * Exported functions */ static cmd_export_t cmds[] = { {"options_reply", (cmd_function)opt_reply, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"accept", PARAM_STR, &acpt_body}, {"accept_encoding", PARAM_STR, &acpt_enc_body}, {"accept_language", PARAM_STR, &acpt_lan_body}, {"supported", PARAM_STR, &supt_body}, {"contact", PARAM_STR, &contact}, {"contact_param", PARAM_STR, &cont_param}, {0, 0, 0} }; /* * Module description */ struct module_exports exports = { "options", /* Module name */ cmds, /* Exported functions */ 0, /* RPC methods */ params, /* Exported parameters */ mod_init, /* Initialization function */ 0, /* Response function */ 0, /* Destroy function */ 0, /* OnCancel function */ 0 /* Child init function */ }; /* * initialize module */ static int mod_init(void) { DBG("options - initializing\n"); /* bind the SL API */ if (sl_load_api(&slb)!=0) { LM_ERR("cannot bind to SL API\n"); return -1; } if (contact.len > 0) { LOG(L_DBG, "contact: '%.*s'\n", contact.len, contact.s); add_cont = ADD_CONT_MODPARAM; if (strncasecmp("dstip", contact.s, contact.len) == 0) { contact.s = NULL; contact.len = 0; add_cont = ADD_CONT_IP; } else if (strncasecmp("ruri", contact.s, contact.len) == 0) { contact.s = NULL; contact.len = 0; add_cont = ADD_CONT_RURI; } /* more possible candidates here could be the To * or topmost Route URI */ } if (cont_param.len > 0 && add_cont == ADD_CONT_OFF) { /* by default we add the IP */ add_cont = ADD_CONT_IP; } return 0; } /* * calculates and returns the length of the Contact header to be inserted. */ static int contact_length(struct sip_msg* _msg) { int ret = 0; if (add_cont == ADD_CONT_OFF) { return 0; } ret = CONT_STR_LEN + CRLF_LEN + 1; if (add_cont == ADD_CONT_MODPARAM) { ret += contact.len; } else if (add_cont == ADD_CONT_IP) { ret += _msg->rcv.bind_address->name.len; ret += 1; ret += _msg->rcv.bind_address->port_no_str.len; switch (_msg->rcv.bind_address->proto) { case PROTO_NONE: case PROTO_UDP: break; case PROTO_TCP: case PROTO_TLS: ret += TRANSPORT_PARAM_LEN + 3; break; case PROTO_SCTP: ret += TRANSPORT_PARAM_LEN + 4; break; default: LOG(L_CRIT, "contact_length(): unsupported proto (%d)\n", _msg->rcv.bind_address->proto); } } else if (add_cont == ADD_CONT_RURI) { if (parse_sip_msg_uri(_msg) != 1) { LOG(L_WARN, "add_contact(): failed to parse ruri\n"); } if (_msg->parsed_orig_ruri_ok) { ret += _msg->parsed_orig_ruri.host.len; if (_msg->parsed_orig_ruri.port.len > 0) { ret += 1; ret += _msg->parsed_orig_ruri.port.len; } } else if (_msg->parsed_uri_ok){ ret += _msg->parsed_uri.host.len; if (_msg->parsed_uri.port.len > 0) { ret += 1; ret += _msg->parsed_uri.port.len; } } } if (cont_param.len > 0) { if (*(cont_param.s) != ';') { ret += 1; } ret += cont_param.len; } return ret; } /* * inserts the Contact header at _dst and returns the number of written bytes */ static int add_contact(struct sip_msg* _msg, char* _dst) { int ret = 0; memcpy(_dst, CONT_STR, CONT_STR_LEN); ret += CONT_STR_LEN; if (add_cont == ADD_CONT_MODPARAM) { memcpy(_dst + ret, contact.s, contact.len); ret += contact.len; } else if (add_cont == ADD_CONT_IP) { memcpy(_dst + ret, _msg->rcv.bind_address->name.s, _msg->rcv.bind_address->name.len); ret += _msg->rcv.bind_address->name.len; memcpy(_dst + ret, ":", 1); ret += 1; memcpy(_dst + ret, _msg->rcv.bind_address->port_no_str.s, _msg->rcv.bind_address->port_no_str.len); ret += _msg->rcv.bind_address->port_no_str.len; switch (_msg->rcv.bind_address->proto) { case PROTO_NONE: case PROTO_UDP: break; case PROTO_TCP: memcpy(_dst + ret, TRANSPORT_PARAM, TRANSPORT_PARAM_LEN); ret += TRANSPORT_PARAM_LEN; memcpy(_dst + ret, "tcp", 3); ret += 3; break; case PROTO_TLS: memcpy(_dst + ret, TRANSPORT_PARAM, TRANSPORT_PARAM_LEN); ret += TRANSPORT_PARAM_LEN; memcpy(_dst + ret, "tls", 3); ret += 3; break; case PROTO_SCTP: memcpy(_dst + ret, TRANSPORT_PARAM, TRANSPORT_PARAM_LEN); ret += TRANSPORT_PARAM_LEN; memcpy(_dst + ret, "sctp", 4); ret += 3; break; default: LOG(L_CRIT, "add_contact(): unknown transport protocol (%d)\n", _msg->rcv.bind_address->proto); } } else if (add_cont == ADD_CONT_RURI) { /* the parser was called by the contact_length function above */ if (_msg->parsed_orig_ruri_ok) { memcpy(_dst + ret, _msg->parsed_orig_ruri.host.s, _msg->parsed_orig_ruri.host.len); ret += _msg->parsed_orig_ruri.host.len; if (_msg->parsed_orig_ruri.port.len > 0) { memcpy(_dst + ret, ":", 1); ret += 1; memcpy(_dst + ret, _msg->parsed_orig_ruri.port.s, _msg->parsed_orig_ruri.port.len); ret += _msg->parsed_orig_ruri.port.len; } } else if (_msg->parsed_uri_ok){ memcpy(_dst + ret, _msg->parsed_uri.host.s, _msg->parsed_uri.host.len); ret += _msg->parsed_uri.host.len; if (_msg->parsed_uri.port.len > 0) { memcpy(_dst + ret, ":", 1); ret += 1; memcpy(_dst + ret, _msg->parsed_uri.port.s, _msg->parsed_uri.port.len); ret += _msg->parsed_uri.port.len; } } } if (cont_param.len > 0) { if (*(cont_param.s) != ';') { memcpy(_dst + ret, ";", 1); ret += 1; } memcpy(_dst + ret, cont_param.s, cont_param.len); ret += cont_param.len; } memcpy(_dst + ret, ">", 1); ret += 1; memcpy(_dst + ret, CRLF, CRLF_LEN); ret += CRLF_LEN; return ret; } /* * assembles and send the acctual reply */ static int opt_reply(struct sip_msg* _msg, char* _foo, char* _bar) { str rpl_hf; int ret; int offset = 0; int cont_len = 0; if (_msg->REQ_METHOD != METHOD_OPTIONS) { LOG(L_ERR, "options_reply(): called for non-OPTIONS request\n"); return 0; } /* ruri == server address check has to be done in the script */ if (_msg->parsed_uri_ok != 1) { if (parse_sip_msg_uri(_msg) != 1) { LOG(L_WARN, "opt_reply(): failed to parse ruri\n"); } } if (_msg->parsed_uri.user.len != 0) { LOG(L_ERR, "options_reply(): wont reply because ruri contains" " a username\n"); return 0; } /* calculate the length and allocated the mem */ rpl_hf.len = ACPT_STR_LEN + ACPT_ENC_STR_LEN + ACPT_LAN_STR_LEN + SUPT_STR_LEN + 4 * CRLF_LEN + acpt_body.len + acpt_enc_body.len + acpt_lan_body.len + supt_body.len; if (add_cont) { cont_len = contact_length(_msg); rpl_hf.len += cont_len; } rpl_hf.s = (char*)pkg_malloc(rpl_hf.len); if (!rpl_hf.s) { LOG(L_CRIT, "options_reply(): out of memory\n"); goto error; } /* create the header fields */ memcpy(rpl_hf.s, ACPT_STR, ACPT_STR_LEN); offset = ACPT_STR_LEN; memcpy(rpl_hf.s + offset, acpt_body.s, acpt_body.len); offset += acpt_body.len; memcpy(rpl_hf.s + offset, CRLF, CRLF_LEN); offset += CRLF_LEN; memcpy(rpl_hf.s + offset, ACPT_ENC_STR, ACPT_ENC_STR_LEN); offset += ACPT_ENC_STR_LEN; memcpy(rpl_hf.s + offset, acpt_enc_body.s, acpt_enc_body.len); offset += acpt_enc_body.len; memcpy(rpl_hf.s + offset, CRLF, CRLF_LEN); offset += CRLF_LEN; memcpy(rpl_hf.s + offset, ACPT_LAN_STR, ACPT_LAN_STR_LEN); offset += ACPT_LAN_STR_LEN; memcpy(rpl_hf.s + offset, acpt_lan_body.s, acpt_lan_body.len); offset += acpt_lan_body.len; memcpy(rpl_hf.s + offset, CRLF, CRLF_LEN); offset += CRLF_LEN; memcpy(rpl_hf.s + offset, SUPT_STR, SUPT_STR_LEN); offset += SUPT_STR_LEN; memcpy(rpl_hf.s + offset, supt_body.s, supt_body.len); offset += supt_body.len; memcpy(rpl_hf.s + offset, CRLF, CRLF_LEN); offset += CRLF_LEN; if (cont_len > 0) { ret = add_contact(_msg, rpl_hf.s + offset); if (ret != cont_len) { LOG(L_CRIT, "options_reply(): add_contact (%i) != contact_length" \ "(%i)\n", ret, cont_len); goto error; } else { offset += cont_len; } } #ifdef EXTRA_DEBUG if (offset != rpl_hf.len) { LOG(L_CRIT, "options_reply(): headerlength (%i) != offset (%i)\n", rpl_hf.len, offset); abort(); } else { DBG("options_reply(): successfully build OPTIONS reply\n"); } #endif if (add_lump_rpl( _msg, rpl_hf.s, rpl_hf.len, LUMP_RPL_HDR|LUMP_RPL_NODUP)!=0) { /* the memory is freed when _msg is destroyed */ if (slb.zreply(_msg, 200, "OK") < 0) { LOG(L_ERR, "options_reply(): failed to send 200 via sl reply\n"); return 0; } else { return 1; } } else { LOG(L_ERR, "options_reply(): add_lump_rpl failed\n"); } error: if (rpl_hf.s) { pkg_free(rpl_hf.s); } if (slb.zreply(_msg, 500, "Server internal error") < 0) { LOG(L_ERR, "options_reply(): failed to send 500 via send_reply\n"); } return 0; } kamailio-4.0.4/obsolete/options/mod_options.h0000644000000000000000000000301412223032460020005 0ustar rootroot/* * $Id$ * * Options Reply Module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _MOD_OPTIONS_H #define _MOD_OPTIONS_H #define ACPT_STR "Accept: " #define ACPT_STR_LEN (sizeof(ACPT_STR) - 1) #define ACPT_ENC_STR "Accept-Encoding: " #define ACPT_ENC_STR_LEN (sizeof(ACPT_ENC_STR) - 1) #define ACPT_LAN_STR "Accept-Language: " #define ACPT_LAN_STR_LEN (sizeof(ACPT_LAN_STR) - 1) #define SUPT_STR "Supported: " #define SUPT_STR_LEN (sizeof(SUPT_STR) - 1) #define CONT_STR "Contact: * * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../../sr_module.h" #include "dbase.h" MODULE_VERSION /* * Oracle database module interface */ static cmd_export_t cmds[] = { {"db_use_table", (cmd_function)db_use_table, 2, 0, 0}, {"db_init", (cmd_function)db_init, 1, 0, 0}, {"db_close", (cmd_function)db_close, 2, 0, 0}, {"db_query", (cmd_function)db_query, 2, 0, 0}, {"db_raw_query", (cmd_function)db_raw_query, 2, 0, 0}, {"db_free_result", (cmd_function)db_free_result, 2, 0, 0}, {"db_insert", (cmd_function)db_insert, 2, 0, 0}, {"db_delete", (cmd_function)db_delete, 2, 0, 0}, {"db_update", (cmd_function)db_update, 2, 0, 0}, {0, 0, 0, 0, 0} }; struct module_exports exports = { "oracle", cmds, 0, /* module paramers */ 0, /* module initialization function */ 0, /* response function*/ 0, /* destroy function */ 0, /* oncancel function */ 0 /* per-child init function */ }; kamailio-4.0.4/obsolete/oracle/README0000644000000000000000000001036612223032460015744 0ustar rootroot WARNING! This module is experimental and may crash SER or create unexpected results. You use the module at your own risk. Please submit bugs at http://bugs.sip-router.org/. ================= Preliminary notes ================= Copyright (C) 2005 RingCentral Inc. Dependencies ------------ The following binaries were used during the development and testing: instantclient-basic-linux32-10.1.0.4-20050525.zip instantclient-sdk-linux32-10.1.0.4-20050525.zip WARNING! 10.2.0.1 version is unstable, and is not recommended for Debian Linux. (You do not need to install 'sdk' package if using binary SER distribution.) instantclient-sqlplus-linux32-10.1.0.4-20050525.zip might be required in future for automated creation of SER DB. The packages could be downloaded from "Downloads" section of www.oracle.com web site. (You'll be asked to register first.) Unzip the contents of the packages to /usr/local and make /usr/local/instantclient symbolic link to the created folder. Also make the following symlink: libclntsh.so -> libclntsh.so.10.1. Core sources patching --------------------- Recompile SER with the following macros added to the proper part of Makefile.defs below "#os specific stuff": DEFS += -DLINUX -D_GNU_SOURCE -D_REENTRANT LIBS += -lpthread I took them from Makefile example of the instantclient SDK. (Probably, -DLINUX could be omitted.) There were some warnings about redefinition of _XOPEN_SOURCE, _XOPEN_SOURCE_EXTENDED, and __USE_XOPEN_EXTENDED macros inside daemonize.c. So, I protected them with '#ifndef _XOPEN_SOURCE' condition. Getting started from command line --------------------------------- export CVSROOT=:pserver:anonymous@cvs.berlios.de:/cvsroot/ser cvs -z4 co -r rel_0_9_0 experimental/oracle cvs -z4 co -r rel_0_9_0 sip_router cp -a experimental/oracle sip_router/modules/ # download instantclient archives to $HOME as described above, then: sudo unzip $HOME/instantclient-sdk-linux32-10.1.0.5-20060511.zip -d /usr/local sudo unzip $HOME/instantclient-basic-linux32-10.1.0.5-20060511.zip -d /usr/local sudo ln -s libclntsh.so.10.1 /usr/local/instantclient10_1/libclntsh.so sudo ln -s instantclient10_1 /usr/local/instantclient cd sip_router # patch Makefile.defs and daemonize.c as described above, then: make modules=modules/oracle modules Usage ----- For basic SER set up add/change the following lines to your ser.cfg: fifo_db_url="oracle://username:passwd@/ser" loadmodule "/path/to/ser/lib/ser/modules/oracle.so" modparam("usrloc", "db_url", "oracle://username:passwd@/ser") modparam("auth_db", "db_url", "oracle://username:passwd@/ser") Additional SER modules may require similar changes to ser.cfg. See documentation for corresponding modules. db_url format: "oracle://username:password@/dbname" Additional parameters are taken from tnsnames.ora file which must be located inside $TNS_ADMIN folder. Example contents of tnsnames.ora: -- begin -- SER = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = )(PORT = )) ) (CONNECT_DATA = (SERVICE_NAME = ) ) ) -- end -- Example 'version' table schema (NUMBER precision is important!): -- begin -- Name Null? Type ----------------------------------------- -------- ---------------------------- TABLE_NAME NOT NULL VARCHAR2(64) TABLE_VERSION NOT NULL NUMBER(5) -- end -- Implementation -------------- OCI API is utilized. It is recommended to use Oracle Instant Client package. db_raw_query() is not implemented. TODO ---- - commit patch for core sources - poorly tested with respect to call stress-load. - insert will fail if more than single date is inserted. - BLOBs are limited to 4000 bytes, and untested. - returned column names are not filled with proper data. - timezone related stuff should be reviewed - Review why checks for NULL argument to pkg_free() are required? - remove const prefix from db_val_t:val.string_val - yet not robust against hackish parameters. - use pool of connections, (like in mysql?) - additional TODOs are marked with XXX throughout the code. Contacts -------- Dmitry Semyonov (primary), (secondary). kamailio-4.0.4/obsolete/oracle/prepare.h0000644000000000000000000000364212223032460016672 0ustar rootroot/* * Copyright (C) 2005 RingCentral Inc. * Created by Dmitry Semyonov * * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _PREPARE_H #define _PREPARE_H #include "../../db/db_key.h" #include "../../db/db_val.h" #include "../../db/db_op.h" void prepare_where(db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n); /* * prepare_select(), prepare_insert(), prepare_delete(), and prepare_update() * functions automatically begin new statement. Other functions continue * to build current statement. */ void prepare_select(db_key_t* _c, int _nc); void prepare_from(const char* _t); void prepare_order_by(db_key_t _o); void prepare_insert(const char* _t); void prepare_insert_columns(db_key_t* _k, int _n); void prepare_insert_values(db_val_t* _v, int _n); void prepare_delete(const char* _t); void prepare_update(const char* _t); void prepare_update_set(db_key_t* _k, db_val_t* _v, int _n); const char *prepared_sql(void); size_t prepared_sql_len(void); #endif /* _PREPARE_H */ kamailio-4.0.4/obsolete/oracle/utils.c0000644000000000000000000000707612223032460016374 0ustar rootroot/* * Copyright (C) 2005 RingCentral Inc. * Created by Dmitry Semyonov * * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "../../dprint.h" #include "common.h" #include "utils.h" sb4 checkerr(dvoid *errhp, sword status, int line) { sb4 errcode = 0; if (status == OCI_SUCCESS) return 0; #define CASE_LOG_STR(_err) \ case _err: \ ERR("Error - " #_err); \ break switch (status) { CASE_LOG_STR(OCI_SUCCESS_WITH_INFO); CASE_LOG_STR(OCI_NEED_DATA); CASE_LOG_STR(OCI_NO_DATA); CASE_LOG_STR(OCI_INVALID_HANDLE); CASE_LOG_STR(OCI_STILL_EXECUTING); CASE_LOG_STR(OCI_CONTINUE); case OCI_ERROR: { text errbuf[512]; errbuf[0] = '\0'; (void) OCIErrorGet(errhp, 1, NULL, &errcode, errbuf, sizeof(errbuf), OCI_HTYPE_ERROR); /* Special case. (Search 24334 in dbase.c for explanation.) */ if (errcode == 24334) { DBG1("ORA-24334. Most likely not an error.\n"); return 24334; } ERR("Error - %.*s", sizeof(errbuf), errbuf); break; } default: ERR("Error - unhandled status: %d", status); } ERR(". Line %d\n", line); return errcode; } const char *sqlt_to_str(ub2 sqlt) { #define CASE_STR(_val) case _val: return #_val switch (sqlt) { CASE_STR(SQLT_CHR); CASE_STR(SQLT_STR); CASE_STR(SQLT_INT); CASE_STR(SQLT_UIN); CASE_STR(SQLT_FLT); CASE_STR(SQLT_DAT); CASE_STR(SQLT_DATE); CASE_STR(SQLT_TIMESTAMP); CASE_STR(SQLT_TIMESTAMP_TZ); CASE_STR(SQLT_TIMESTAMP_LTZ); CASE_STR(SQLT_NUM); default: return "Unknown SQLT_xxx"; } } /* * URL example: "oracle://serro:47serro11@localhost:port/ser" * XXX: Buffer lengths are not checked yet! */ int parse_sql_url(const char *_sqlurl, char *sz_user, char *sz_passwd, char *sz_db) { /* Skip 'oracle://' part */ while ( *_sqlurl != '\0' && *_sqlurl++ != ':' ); if ( *_sqlurl++ != '/' || *_sqlurl++ != '/' ) return -1; /* Get username */ do { *sz_user++ = *_sqlurl++; } while ( *_sqlurl != ':' && *_sqlurl != '\0' ); if ( *_sqlurl++ != ':' ) return -2; *sz_user = '\0'; /* Get password */ do { *sz_passwd++ = *_sqlurl++; } while ( *_sqlurl != '@' && *_sqlurl != '\0' ); if ( *_sqlurl++ != '@' ) return -3; *sz_passwd = '\0'; /* Skip 'localhost:port' part */ while ( *_sqlurl != '\0' && *_sqlurl++ != '/' ); if ( *_sqlurl == '\0' ) return -4; /* Get db */ do { *sz_db++ = *_sqlurl++; } while ( *_sqlurl != '\0' ); *sz_db = '\0'; return 0; } kamailio-4.0.4/obsolete/oracle/utils.h0000644000000000000000000000257712223032460016402 0ustar rootroot/* * Copyright (C) 2005 RingCentral Inc. * Created by Dmitry Semyonov * * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _UTILS_H_ #define _UTILS_H_ int checkerr(dvoid *errhp, sword status, int line); #define OCICHECK(_errhp, _status) checkerr(_errhp, _status, __LINE__) const char *sqlt_to_str(ub2 sqlt); int parse_sql_url(const char *sqlurl, char *sz_user, char *sz_passwd, char *sz_db); #endif /* _UTILS_H_ */ kamailio-4.0.4/obsolete/oracle/dbase.h0000644000000000000000000000502412223032460016306 0ustar rootroot/* * Core functions. * See db/db.h for description. * * Copyright (C) 2005 RingCentral Inc. * Created by Dmitry Semyonov * * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef DBASE_H #define DBASE_H #include #include "../../lib/srdb2/db_con.h" #include "../../lib/srdb2/db_res.h" #include "../../db/db_key.h" #include "../../db/db_op.h" #include "../../db/db_val.h" /* A pointer to this structure is assigned to CON_TAIL(_h). */ typedef struct _ora_con_t { union { OCIEnv *ptr; dvoid *dvoid_ptr; } env; union { OCIError *ptr; dvoid *dvoid_ptr; } err; union { OCISvcCtx *ptr; dvoid *dvoid_ptr; } svc; union { OCIStmt *ptr; dvoid *dvoid_ptr; } stmt; } ora_con_t; #define CON_ORA(_h) ((ora_con_t *)(CON_TAIL(_h))) typedef struct _ora_param_t { void *p_data; ub4 size; ub2 type; OCIInd ind; } ora_param_t; int db_use_table(db_con_t* _h, const char* _t); db_con_t* db_init(const char* _sqlurl); void db_close(db_con_t* _h); int db_query(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc, db_key_t _o, db_res_t** _r); int db_raw_query(db_con_t* _h, char* _s, db_res_t** _r); int db_free_result(db_con_t* _h, db_res_t* _r); int db_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n); int db_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n); int db_update(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un); #endif /* DBASE_H */ kamailio-4.0.4/obsolete/oracle/dbase.c0000644000000000000000000007407512223032460016315 0ustar rootroot/* * Core functions. * See db/db.h for description. * * @todo add paranoid checks for sql statement buffer overflow, and protect * the checks with macro which is switched off by default. * * Copyright (C) 2005 RingCentral Inc. * Created by Dmitry Semyonov * * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../ut.h" #include "common.h" #include "dbase.h" #include "utils.h" #include "prepare.h" static ora_param_t ora_params[ORA_PARAMS_MAX]; static sb2 ora_bind_inds[ORA_BINDS_MAX]; static int bind_values(db_con_t* _h, db_val_t* _v, int _n, int bound_count); static int define_params(db_con_t* _h, int _nc); static void free_params(void); static int map_int_param_type_and_size_to_ext(db_con_t* _h, dvoid* p_param, int ora_param_num); static int init_db_res(db_res_t** _r, int params_num); static int convert_row(int params_num, db_res_t* _r); static db_type_t sqlt_to_db_type(ub2 sqlt); static int oci_prepare(db_con_t *_h); static int oci_execute(db_con_t *_h, ub4 iters); #if !DB_PREALLOC_SQLT_HANDLE static int oci_cleanup(db_con_t *_h); #endif int db_use_table(db_con_t* _h, const char* _t) { if (_h == NULL || _t == NULL) { ERR("%s: invalid (NULL) argument(s)\n", __FUNCTION__); return -1; } DBG("%s(%s)\n", __FUNCTION__, _t); CON_TABLE(_h) = _t; /* XXX: shouldn't we copy the string here instead? */ return 0; } db_con_t* db_init(const char* _sqlurl) { db_con_t *p_db_con = NULL; ora_con_t *p_ora_con = NULL; sword rc = OCI_ERROR; int res = -1; char sz_user[32]; char sz_passwd[32]; char sz_db[32]; LOG(L_ALERT, "WARNING! This module is experimental and may crash SER " "or create unexpected results. You use the module at your own " "risk. Please submit bugs at http://bugs.sip-router.org/\n"); if (_sqlurl == NULL) { ERR("%s: invalid (NULL) argument\n", __FUNCTION__); goto db_init_err; } DBG("%s(%s)\n", __FUNCTION__, _sqlurl); if ((res = parse_sql_url(_sqlurl, sz_user, sz_passwd, sz_db)) < 0) { ERR("%s: Error %d while parsing %s\n", __FUNCTION__, res, _sqlurl); goto db_init_err; } p_db_con = pkg_malloc(sizeof(db_con_t)); p_ora_con = pkg_malloc(sizeof(ora_con_t)); if (p_db_con == NULL || p_ora_con == NULL) { ERR("%s: no memory\n", __FUNCTION__); goto db_init_err; } memset(p_db_con, 0, sizeof(db_con_t)); memset(p_ora_con, 0, sizeof(ora_con_t)); CON_TABLE(p_db_con) = NULL; /* CON_ORA() could not be used as lvalue due to deprecated lvalue casts. */ CON_TAIL(p_db_con) = (unsigned long)p_ora_con; rc = OCIEnvCreate(&(p_ora_con->env.ptr), OCI_DEFAULT, NULL, NULL, NULL, NULL, 0, NULL); if (rc != OCI_SUCCESS) { OCICHECK(p_ora_con->env.ptr, rc); goto db_init_err; } rc = OCIHandleAlloc(p_ora_con->env.ptr, &(p_ora_con->err.dvoid_ptr), OCI_HTYPE_ERROR, 0, NULL); if (rc != OCI_SUCCESS) { OCICHECK(p_ora_con->err.ptr, rc); goto db_init_err; } rc = OCIHandleAlloc(p_ora_con->env.ptr, &(p_ora_con->svc.dvoid_ptr), OCI_HTYPE_SVCCTX, 0, NULL); if (rc != OCI_SUCCESS) { OCICHECK(p_ora_con->env.ptr, rc); goto db_init_err; } rc = OCILogon(p_ora_con->env.ptr, p_ora_con->err.ptr, &(p_ora_con->svc.ptr), (OraText *)sz_user, strlen(sz_user), (OraText *)sz_passwd, strlen(sz_passwd), (OraText *)sz_db, strlen(sz_db)); if (rc != OCI_SUCCESS) { OCICHECK(p_ora_con->err.ptr, rc); goto db_init_err; } #if DB_PREALLOC_SQLT_HANDLE rc = OCIHandleAlloc(p_ora_con->env.ptr, &(p_ora_con->stmt.dvoid_ptr), OCI_HTYPE_STMT, 0, NULL); if (rc != OCI_SUCCESS) { OCICHECK(p_ora_con->env.ptr, rc); goto db_init_err; } #endif return p_db_con; db_init_err: #if DB_PREALLOC_SQLT_HANDLE if (p_ora_con->stmt.ptr != NULL) OCICHECK(p_ora_con->env.ptr, OCIHandleFree(p_ora_con->stmt.ptr, OCI_HTYPE_STMT)); #endif if (p_ora_con->svc.ptr != NULL) OCICHECK(p_ora_con->env.ptr, OCIHandleFree(p_ora_con->svc.ptr, OCI_HTYPE_SVCCTX)); if (p_ora_con->err.ptr != NULL) OCICHECK(p_ora_con->env.ptr, OCIHandleFree(p_ora_con->err.ptr, OCI_HTYPE_ERROR)); if (p_ora_con) { pkg_free(p_ora_con); p_ora_con = NULL; } if (p_db_con) { pkg_free(p_db_con); p_db_con = NULL; } ERR("%s failed\n", __FUNCTION__); return NULL; } void db_close(db_con_t* _h) { sword rc = OCI_ERROR; if (_h == NULL) { ERR("%s: invalid (NULL) argument\n", __FUNCTION__); return; } DBG("%s\n", __FUNCTION__); #if DB_PREALLOC_SQLT_HANDLE rc = OCIHandleFree(CON_ORA(_h)->stmt.ptr, OCI_HTYPE_STMT); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->env.ptr, rc); } #endif rc = OCILogoff(CON_ORA(_h)->svc.ptr, CON_ORA(_h)->err.ptr); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); } /* Note: svc.ptr handle was released by OCILogoff() function. */ rc = OCIHandleFree(CON_ORA(_h)->err.ptr, OCI_HTYPE_ERROR); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->env.ptr, rc); } rc = OCITerminate(OCI_DEFAULT); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->env.ptr, rc); } if (CON_ORA(_h) != NULL) { pkg_free(CON_ORA(_h)); CON_TAIL(_h) = (unsigned long)NULL; } if (_h != NULL) { pkg_free(_h); _h = NULL; } } int db_query(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc, db_key_t _o, db_res_t** _r) { sword rc = OCI_ERROR; int params_num; *_r = NULL; /* Required for processing under err_cleanup label, and simply to be on the safe side. */ if (_h == NULL) { ERR("%s: invalid (NULL) argument(s)\n", __FUNCTION__); return -1; } if (ora_params[0].p_data != NULL) { ERR("%s: Nested calls to db_query() are not allowed!\n", __FUNCTION__); return -1; } DBG("%s\n", __FUNCTION__); prepare_select(_c, _nc); prepare_from(CON_TABLE(_h)); prepare_where(_k, _op, _v, _n); prepare_order_by(_o); DBG("%.*s\n", prepared_sql_len(), prepared_sql()); rc = oci_prepare(_h); if (rc != 0) return -1; rc = bind_values(_h, _v, _n, 0); if (rc < 0) goto err_cleanup; rc = oci_execute(_h, 0); if (rc != 0) goto err_cleanup; params_num = define_params(_h, _nc); if (params_num <= 0) { rc = -1; goto err_cleanup; } rc = init_db_res(_r, params_num); if (rc != 0) goto err_cleanup; while ((rc = OCIStmtFetch(CON_ORA(_h)->stmt.ptr, CON_ORA(_h)->err.ptr, 1, 0, 0)) == OCI_SUCCESS) { rc = convert_row(params_num, *_r); if (rc != 0) goto err_cleanup; } if (rc != OCI_SUCCESS && rc != OCI_NO_DATA) { OCICHECK(CON_ORA(_h)->err.ptr, rc); rc = -1; goto err_cleanup; } else { rc = 0; } if (RES_ROW_N(*_r) == 0) { DBG("%s: no data\n", __FUNCTION__); } err_cleanup: #if !DB_PREALLOC_SQLT_HANDLE (void)oci_cleanup(_h); #endif if (rc != 0) { (void)db_free_result(_h, *_r); return -1; } else { return 0; } } int db_raw_query(db_con_t* _h, char* _s, db_res_t** _r) { ERR("%s: unimplemented\n", __FUNCTION__); *_r = NULL; /* Just in case */ return -1; } int db_free_result(db_con_t* _h, db_res_t* _r) { db_row_t *p_row; db_val_t *p_val; if (_h == NULL) { ERR("%s: invalid (NULL) argument(s)\n", __FUNCTION__); return -1; } DBG("%s\n", __FUNCTION__); free_params(); if (_r == NULL) return 0; /* Nothing else to free. */ if (RES_ROW_N(_r) < 0) { ERR("%s: invalid RES_ROW_N(_r) value!\n", __FUNCTION__); } p_row = RES_ROWS(_r); while (RES_ROW_N(_r)-- > 0) { p_val = ROW_VALUES(p_row); while (ROW_N(p_row)--) { if (VAL_NULL(p_val) == 0) { if (VAL_TYPE(p_val) == DB_STRING && VAL_STRING(p_val) != NULL) { pkg_free((void *)VAL_STRING(p_val)); /* XXX Get rid of cast */ VAL_STRING(p_val) = NULL; DBG("%s: DB_STRING memory released\n", __FUNCTION__); } else if (VAL_TYPE(p_val) == DB_BLOB && VAL_BLOB(p_val).s != NULL) { pkg_free(VAL_BLOB(p_val).s); VAL_BLOB(p_val).s = NULL; DBG("%s: DB_BLOB memory released\n", __FUNCTION__); } else if (VAL_TYPE(p_val) == DB_STR && VAL_STR(p_val).s != NULL) { pkg_free(VAL_STR(p_val).s); VAL_STR(p_val).s = NULL; DBG("%s: DB_STR memory released\n", __FUNCTION__); } } else { DBG("%s: NULL value skipped\n", __FUNCTION__); } ++p_val; } pkg_free(ROW_VALUES(p_row)); ROW_VALUES(p_row) = NULL; DBG("%s: row memory released\n", __FUNCTION__); ++p_row; } if (RES_ROWS(_r) != NULL) { pkg_free(RES_ROWS(_r)); RES_ROWS(_r) = NULL; DBG("%s: rows memory released\n", __FUNCTION__); } if (RES_TYPES(_r) != NULL) { pkg_free(RES_TYPES(_r)); RES_TYPES(_r) = NULL; DBG("%s: types memory released\n", __FUNCTION__); } if (RES_NAMES(_r) != NULL) { pkg_free(RES_NAMES(_r)); RES_NAMES(_r) = NULL; DBG("%s: names memory released\n", __FUNCTION__); } return 0; } int db_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n) { sword rc; if (_h == NULL || _k == NULL || _v == NULL || _n <= 0) { ERR("%s: invalid (NULL) argument(s)\n", __FUNCTION__); return -1; } DBG("%s\n", __FUNCTION__); prepare_insert(CON_TABLE(_h)); prepare_insert_columns(_k, _n); prepare_insert_values(_v, _n); DBG("%.*s\n", prepared_sql_len(), prepared_sql()); rc = oci_prepare(_h); if (rc != 0) goto err_cleanup; rc = bind_values(_h, _v, _n, 0); if (rc < 0) goto err_cleanup; rc = oci_execute(_h, 1); if (rc != 0) goto err_cleanup; err_cleanup: #if !DB_PREALLOC_SQLT_HANDLE (void)oci_cleanup(_h); #endif return rc != 0 ? -1 : 0; } int db_delete(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n) { sword rc; if (_h == NULL) { ERR("%s: invalid (NULL) argument\n", __FUNCTION__); return -1; } DBG("%s\n", __FUNCTION__); prepare_delete(CON_TABLE(_h)); prepare_where(_k, _op, _v, _n); DBG("%.*s\n", prepared_sql_len(), prepared_sql()); rc = oci_prepare(_h); if (rc != 0) goto err_cleanup; rc = bind_values(_h, _v, _n, 0); if (rc < 0) goto err_cleanup; rc = oci_execute(_h, 1); if (rc != 0) goto err_cleanup; err_cleanup: #if !DB_PREALLOC_SQLT_HANDLE (void)oci_cleanup(_h); #endif return rc != 0 ? -1 : 0; } int db_update(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un) { sword rc; if (_h == NULL || _uk == NULL || _uv == NULL || _un <= 0) { ERR("%s: invalid argument(s)\n", __FUNCTION__); return -1; } DBG("%s\n", __FUNCTION__); prepare_update(CON_TABLE(_h)); prepare_update_set(_uk, _uv, _un); prepare_where(_k, _op, _v, _n); DBG("%.*s\n", prepared_sql_len(), prepared_sql()); rc = oci_prepare(_h); if (rc != 0) goto err_cleanup; rc = bind_values(_h, _uv, _un, 0); if (rc < 0) goto err_cleanup; rc = bind_values(_h, _v, _n, rc); if (rc < 0) goto err_cleanup; rc = oci_execute(_h, 1); if (rc != 0) goto err_cleanup; err_cleanup: #if !DB_PREALLOC_SQLT_HANDLE (void)oci_cleanup(_h); #endif return rc != 0 ? -1 : 0; } /* Return: number of bound values (it could be different from _n parameter * due to null values), or -1 in case of failure. */ static int bind_values(db_con_t* _h, db_val_t* _v, int _n, int bound_count) { sword rc; int i; OCIBind *p_bnd; dvoid *p_bind_val; sb4 bind_val_sz; ub2 bind_val_type; /* XXX Will fail if more than single date is inserted! * It is necessary to allocate them dynamically! */ static unsigned char ora_date[7] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; /* Oracle date format */ for (i = 0; i < _n; ++i, ++_v) { if (VAL_NULL(_v)) { DBG("%s: (null)\n", __FUNCTION__); /* 'null' is used in queries. So, nothing to bind. But we should keep correct index and 'for' condition. */ --i; --_n; continue; } else { ora_bind_inds[i] = OCI_IND_NOTNULL; switch (VAL_TYPE(_v)) { case DB_INT: p_bind_val = &VAL_INT(_v); bind_val_sz = sizeof(VAL_INT(_v)); bind_val_type = SQLT_INT; DBG("%s: DB_INT - %d\n", __FUNCTION__, VAL_INT(_v)); break; case DB_DOUBLE: p_bind_val = &VAL_DOUBLE(_v); bind_val_sz = sizeof(VAL_DOUBLE(_v)); bind_val_type = SQLT_FLT; DBG("%s: DB_DOUBLE - %e\n", __FUNCTION__, VAL_DOUBLE(_v)); break; case DB_STRING: p_bind_val = &VAL_STRING(_v); bind_val_sz = strlen(VAL_STRING(_v)); /* XXX length should not exceed 4000 bytes here */ bind_val_type = SQLT_CHR; DBG("%s: DB_STRING - %s\n", __FUNCTION__, VAL_STRING(_v)); break; case DB_STR: p_bind_val = VAL_STR(_v).s; bind_val_sz = VAL_STR(_v).len; /* XXX length should not exceed 4000 bytes here */ bind_val_type = SQLT_CHR; DBG("%s: DB_STR - %.*s\n", __FUNCTION__, VAL_STR(_v).len, ZSW(VAL_STR(_v).s) ); break; case DB_DATETIME: { struct tm *p_tm = localtime(&VAL_TIME(_v)); if (p_tm == NULL) { ERR("%s: time_t -> struct tm conversion failure %ld\n", __FUNCTION__, VAL_TIME(_v)); return -1; } p_tm->tm_year += 1900; /* tm_year is stored as (Year - 1900) value. */ ora_date[0] = p_tm->tm_year / 100 + 100; ora_date[1] = p_tm->tm_year % 100 + 100; ora_date[2] = p_tm->tm_mon + 1; /* tm_mon is from [0-11] interval */ ora_date[3] = p_tm->tm_mday; ora_date[4] = p_tm->tm_hour + 1; ora_date[5] = p_tm->tm_min + 1; ora_date[6] = p_tm->tm_sec + 1; /* XXX: Should tm_isdst, etc. be processed? */ p_bind_val = ora_date; bind_val_sz = sizeof(ora_date); bind_val_type = SQLT_DAT; DBG("%s: DB_DATETIME - %s\n", __FUNCTION__, ctime(&VAL_TIME(_v))); break; } case DB_BLOB: p_bind_val = VAL_BLOB(_v).s; bind_val_sz = VAL_BLOB(_v).len; /* XXX length should not exceed 4000 bytes here (?) */ bind_val_type = SQLT_VCS; /* XXX review */ DBG("%s: DB_BLOB, length %d\n", __FUNCTION__, VAL_BLOB(_v).len); break; case DB_BITMAP: p_bind_val = &VAL_BITMAP(_v); bind_val_sz = sizeof(VAL_BITMAP(_v)); bind_val_type = SQLT_UIN; DBG("%s: DB_BITMAP - %#x\n", __FUNCTION__, VAL_BITMAP(_v)); break; default: ERR("%s: Unsupported DB value type - %d\n", __FUNCTION__, VAL_TYPE(_v)); return -1; } } p_bnd = NULL; /* XXX Not used so far */ rc = OCIBindByPos(CON_ORA(_h)->stmt.ptr, &p_bnd, CON_ORA(_h)->err.ptr, bound_count + i + 1, p_bind_val, bind_val_sz, bind_val_type, &ora_bind_inds[i], 0, 0, 0, 0, OCI_DEFAULT); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } } return _n; } /* Used by db_query(). * free_params() must be called after the query is finished. * Return: number of parameters or -1 in case of failure; * (free_params() must be called in both cases). * @sa free_params() */ static int define_params(db_con_t* _h, int _nc) { OCIDefine *p_dfn = NULL; dvoid *p_param = NULL; int ora_params_num = 0; sword rc; #if DB_DEBUG int i = 0; for (; i < sizeof(ora_params) / sizeof(ora_params[0]); ++i) { if (ora_params[i].p_data != NULL) { ERR("%s: Internal logic is broken!\n", __FUNCTION__); return -1; } } #endif /* XXX handle case when _nc is defined separately to avoid extra call * to OCIParamGet() */ while ((rc = OCIParamGet(CON_ORA(_h)->stmt.ptr, OCI_HTYPE_STMT, CON_ORA(_h)->err.ptr, &p_param, ora_params_num + 1)) == OCI_SUCCESS) { if (ora_params_num == ORA_PARAMS_MAX) { /* XXX log more info */ ERR("%s: Too many table columns!\n", __FUNCTION__); return -1; } rc = map_int_param_type_and_size_to_ext(_h, p_param, ora_params_num); if (rc != 0) return -1; ora_params[ora_params_num].p_data = pkg_malloc(ora_params[ora_params_num].size); p_dfn = NULL; /* XXX currently unused */ rc = OCIDefineByPos(CON_ORA(_h)->stmt.ptr, &p_dfn, CON_ORA(_h)->err.ptr, ora_params_num + 1, ora_params[ora_params_num].p_data, ora_params[ora_params_num].size, ora_params[ora_params_num].type, &ora_params[ora_params_num].ind, 0, 0, OCI_DEFAULT); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } ++ora_params_num; } if (rc != OCI_SUCCESS) { /* * OCI_ERROR + ORA-24334 are returned in case of invalid pos parameter of * OCIParamGet(). This is the only(?) way to handle 'select *' * statement while using non-scrollable cursor. */ if (OCICHECK(CON_ORA(_h)->err.ptr, rc) != 24334 || rc != OCI_ERROR) { return -1; } } DBG("%s: ora_params_num = %d\n", __FUNCTION__, ora_params_num); return ora_params_num; } /* A companion for define_params() function * @sa define_params() */ static void free_params(void) { int i = 0; int freed_count = 0; DBG("%s\n", __FUNCTION__); for (; i < sizeof(ora_params) / sizeof(ora_params[0]); ++i) { if (ora_params[i].p_data != NULL) { pkg_free(ora_params[i].p_data); ora_params[i].p_data = NULL; freed_count++; } } DBG("%s: %d parameter(s) freed\n", __FUNCTION__, freed_count); } /* * Called by define_params(). * Uses and updates global ora_params[ora_params_num]. * Defines the conversion rules that will be applied during fetching. * Don't forget to update sqlt_to_db_type() if output types are changed. */ static int map_int_param_type_and_size_to_ext(db_con_t* _h, dvoid* p_param, int ora_params_num) { sword rc; rc = OCIAttrGet(p_param, OCI_DTYPE_PARAM, &ora_params[ora_params_num].type, 0, OCI_ATTR_DATA_TYPE, CON_ORA(_h)->err.ptr); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } DBG1("%s: Oracle type %s\n", __FUNCTION__, sqlt_to_str(ora_params[ora_params_num].type)); switch (ora_params[ora_params_num].type) { case SQLT_STR: case SQLT_VCS: case SQLT_CHR: { rc = OCIAttrGet(p_param, OCI_DTYPE_PARAM, &ora_params[ora_params_num].size, 0, OCI_ATTR_DATA_SIZE, CON_ORA(_h)->err.ptr); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } DBG1("%s: SQLT_CHR/VCS/STR size %d\n", __FUNCTION__, ora_params[ora_params_num].size); ora_params[ora_params_num].type = SQLT_STR; break; } case SQLT_INT: ora_params[ora_params_num].size = sizeof(int); break; case SQLT_UIN: ora_params[ora_params_num].size = sizeof(unsigned int); break; case SQLT_FLT: ora_params[ora_params_num].size = sizeof(double); break; case SQLT_DATE: case SQLT_TIMESTAMP: case SQLT_TIMESTAMP_TZ: case SQLT_TIMESTAMP_LTZ: /* * Let OCI make a conversion of date types * to the one that we understand. */ ora_params[ora_params_num].type = SQLT_DAT; ora_params[ora_params_num].size = 7; break; case SQLT_DAT: ora_params[ora_params_num].size = 7; break; case SQLT_NUM: { sb2 precision; sb1 scale; rc = OCIAttrGet(p_param, OCI_DTYPE_PARAM, &precision, 0, OCI_ATTR_PRECISION, CON_ORA(_h)->err.ptr); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } rc = OCIAttrGet(p_param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, CON_ORA(_h)->err.ptr); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } /* * Depending on precision and scale attributes we should decide * what external number type to use. */ DBG1("%s: SQLT_NUM precision %d, scale %d\n", __FUNCTION__, precision, scale); if (precision < 9 && scale == 0) { /* * MAX_INT (= 134217727) occupies 9 digits. So, maximum integer * number supported is 99 999 999 (8 digits). */ ora_params[ora_params_num].type = SQLT_INT; ora_params[ora_params_num].size = sizeof(int); } else { ora_params[ora_params_num].type = SQLT_FLT; ora_params[ora_params_num].size = sizeof(double); } break; } default: ERR("%s: Unsupported data type: %d!\n", __FUNCTION__, ora_params[ora_params_num].type); return -1; } DBG1("%s: External type %s, size %d\n", __FUNCTION__, sqlt_to_str(ora_params[ora_params_num].type), ora_params[ora_params_num].size); return 0; } /* @sa db_free_result() */ static int init_db_res(db_res_t** _r, int params_num) { static db_res_t res; int i; *_r = &res; RES_NAMES(*_r) = (db_key_t *)pkg_malloc(sizeof(db_key_t) * params_num); RES_TYPES(*_r) = (db_type_t *)pkg_malloc(sizeof(db_type_t) * params_num); if (RES_NAMES(*_r) == NULL || RES_TYPES(*_r) == NULL) { ERR("%s: No memory for %d bytes!\n", __FUNCTION__, (sizeof(db_key_t) + sizeof(db_type_t)) * params_num); if ( RES_NAMES(*_r) != NULL ) { pkg_free(RES_NAMES(*_r)); RES_NAMES(*_r) = NULL; } if ( RES_TYPES(*_r) != NULL ) { pkg_free(RES_TYPES(*_r)); RES_TYPES(*_r) = NULL; } return -1; } RES_COL_N(*_r) = params_num; RES_ROWS(*_r) = NULL; RES_ROW_N(*_r) = 0; for (i = 0; i < params_num; i++) { RES_NAMES(*_r)[i] = NULL; /* XXX unimplemented */ RES_TYPES(*_r)[i] = sqlt_to_db_type(ora_params[i].type); } return 0; } /* * Only limited subset of SQLT_* types is allowed! The subset comes from * map_int_param_type_and_size_to_ext() function */ static db_type_t sqlt_to_db_type(ub2 sqlt) { switch (sqlt) { case SQLT_STR: return DB_STR; case SQLT_INT: return DB_INT; case SQLT_UIN: return DB_BITMAP; case SQLT_FLT: return DB_DOUBLE; case SQLT_DAT: return DB_DATETIME; default: ERR("%s: Internal logic is broken!\n", __FUNCTION__); return -1; } } /* @sa db_free_result() */ static int convert_row(int params_num, db_res_t* _r) { static int allocated_rows; int i; db_val_t *p_val; ora_param_t *p_param; DBG("%s: params_num = %d, _r = %p\n", __FUNCTION__, params_num, _r); /* Detect initial invocation. */ if (RES_ROWS(_r) == NULL) { allocated_rows = 0; } /* * Since we don't know number of rows aforetime, we aggressively * pre-allocate them to avoid multiple memory re-allocations. * The exact pre-allocation algorithm should be adjusted according * to actual queries. Let's use factor 10 for the beginning. */ if (RES_ROW_N(_r) == allocated_rows) { void *tmp_ptr; allocated_rows *= 10; ++allocated_rows; /* Handling initial 0 value. */ /* * We can't assign directly to RES_ROWS(_r) here, since we don't want * to loose original pointer in case of realloc failure. */ tmp_ptr = pkg_realloc(RES_ROWS(_r), sizeof(db_row_t) * allocated_rows); if (tmp_ptr == NULL) { ERR("%s: No memory for %d bytes!\n", __FUNCTION__, sizeof(db_row_t) * allocated_rows); return -1; } else { RES_ROWS(_r) = tmp_ptr; } } ROW_N(RES_ROWS(_r) + RES_ROW_N(_r)) = params_num; ROW_VALUES(RES_ROWS(_r) + RES_ROW_N(_r)) = p_val = pkg_malloc(sizeof(db_val_t) * params_num); ++RES_ROW_N(_r); p_param = ora_params; for (i = 0; i < params_num; i++, p_val++, p_param++) { VAL_TYPE(p_val) = sqlt_to_db_type(p_param->type); if (p_param->ind == OCI_IND_NULL) { VAL_NULL(p_val) = 1; DBG("OCI_IND_NULL, type %s\n", sqlt_to_str(p_param->type)); continue; } else if (p_param->ind != OCI_IND_NOTNULL) { ERR("ind = %d, type %s\n", p_param->ind, sqlt_to_str(p_param->type)); } VAL_NULL(p_val) = 0; switch (p_param->type) { case SQLT_STR: { /* We construct correct value of type DB_STR and at the same time of type DB_STRING (as long as _union_ is used inside db_val_t structure and string pointer is the _first_ variable of _str structure). This is necessary since the callers tend to ignore returned value types. Reported type is DB_STR. */ size_t buflen = strlen(p_param->p_data) + 1; VAL_STR(p_val).s = pkg_malloc(buflen); if (VAL_STR(p_val).s == NULL) { /* * Set number of coulumns to actually processed value. * db_free_result() must honor this number during cleanup. */ ROW_N(RES_ROWS(_r) + RES_ROW_N(_r) - 1) = i; return -1; } memcpy(VAL_STR(p_val).s, p_param->p_data, buflen); VAL_STR(p_val).len = buflen - 1; DBG("SQLT_STR: %.*s\n", VAL_STR(p_val).len, ZSW(VAL_STR(p_val).s)); break; } case SQLT_INT: VAL_INT(p_val) = *(int *)(p_param->p_data); DBG("SQLT_INT: %d\n", *(int *)(p_param->p_data)); break; case SQLT_UIN: VAL_BITMAP(p_val) = *(unsigned int *)(p_param->p_data); DBG("SQLT_UIN: %#x\n", *(unsigned int *)(p_param->p_data)); break; case SQLT_FLT: VAL_DOUBLE(p_val) = *(double *)(p_param->p_data); DBG("SQLT_FLT: %e\n", *(double *)(p_param->p_data)); break; case SQLT_DAT: { struct tm tm; time_t time; char *p_ora_date = p_param->p_data; tm.tm_year = (*p_ora_date++ - 100) * 100; /* * Second step is necessary here, since single step operation * could result in wrong value due to double pointer++ operation. * * 1900 is substracted since tm_year is stored as (Year - 1900) value. */ tm.tm_year += *p_ora_date++ - 100 - 1900; tm.tm_mon = *p_ora_date++ - 1; /* tm_mon is from [0-11] interval */ tm.tm_mday = *p_ora_date++; tm.tm_hour = *p_ora_date++ - 1; tm.tm_min = *p_ora_date++ - 1; tm.tm_sec = *p_ora_date++ - 1; tm.tm_isdst = -1; /* XXX: unknown DST. Is it correct? */ /* XXX Is it necessary to preset tm_gmtoff, tm_zone? */ tm.tm_gmtoff = 0; tm.tm_zone = 0; time = mktime(&tm); if (time == -1) { ERR("%s: SQLT_DAT -> time_t conversion failed\n", __FUNCTION__); return -1; } VAL_TIME(p_val) = time; DBG("SQLT_DAT: %s\n", ctime(&time)); break; } default: /* All unsupported types must had been catched before. */ ERR("%s: Internal logic is broken: %d!\n", __FUNCTION__, p_param->type); return -1; } } return 0; } /* Allocate and prepare SQL statement */ static int oci_prepare(db_con_t *_h) { DBG("%s\n", __FUNCTION__); #if !DB_PREALLOC_SQLT_HANDLE sword rc = OCIHandleAlloc(CON_ORA(_h)->env.ptr, (dvoid **)&(CON_ORA(_h)->stmt.ptr), OCI_HTYPE_STMT, 0, NULL); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->env.ptr, rc); return -1; } #else sword #endif rc = OCIStmtPrepare(CON_ORA(_h)->stmt.ptr, CON_ORA(_h)->err.ptr, (OraText *)prepared_sql(), prepared_sql_len(), OCI_NTV_SYNTAX, OCI_DEFAULT); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); #if !DB_PREALLOC_SQLT_HANDLE if (CON_ORA(_h)->stmt.ptr != NULL) { OCICHECK(CON_ORA(_h)->env.ptr, OCIHandleFree(CON_ORA(_h)->stmt.ptr, OCI_HTYPE_STMT)); } #endif return -2; } return 0; } /* 'iters' must be set to 0 for 'select' statement, and to 1 for others. */ static int oci_execute(db_con_t *_h, ub4 iters) { DBG("%s\n", __FUNCTION__); sword rc = OCIStmtExecute(CON_ORA(_h)->svc.ptr, CON_ORA(_h)->stmt.ptr, CON_ORA(_h)->err.ptr, iters, 0, NULL, NULL, OCI_COMMIT_ON_SUCCESS); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } return 0; } #if !DB_PREALLOC_SQLT_HANDLE static int oci_cleanup(db_con_t *_h) { DBG("%s\n", __FUNCTION__); sword rc = OCIHandleFree(CON_ORA(_h)->stmt.ptr, OCI_HTYPE_STMT); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->env.ptr, rc); return -1; } return 0; } #endif kamailio-4.0.4/obsolete/oracle/prepare.c0000644000000000000000000002166012223032460016665 0ustar rootroot/* * It is assumed throughout this file that sqlbuf size is large enough. * Any paranoid checks should be performed outside. * * The code looks a bit ugly. This is for speed. * Please, don't add calls to sprintf(), etc. * * Copyright (C) 2005 RingCentral Inc. * Created by Dmitry Semyonov * * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "common.h" #include "prepare.h" #if SQL_STMT_BUFF_SZ < 1024 #warning It is not recommended to set SQL_STMT_BUFF_SZ to less than 1K #endif static char sqlbuf[SQL_STMT_BUFF_SZ]; static char *sqlbuf_pos = NULL; static const char sql_eq_char = '='; /* * ' is null ' or * ' is not null ' * depending on _op. */ void prepare_null(db_op_t *_op) { static const char sql_str_eq[] = " is null"; static const size_t sql_str_eq_len = sizeof(sql_str_eq) - 1; static const char sql_str_ne[] = " is not null"; static const size_t sql_str_ne_len = sizeof(sql_str_ne) - 1; if (_op == NULL || strcmp(*_op, OP_EQ) == 0) { memcpy(sqlbuf_pos, sql_str_eq, sql_str_eq_len); sqlbuf_pos += sql_str_eq_len; } else { memcpy(sqlbuf_pos, sql_str_ne, sql_str_ne_len); sqlbuf_pos += sql_str_ne_len; } } /* * where :a and :b and ... :n * or none. */ void prepare_where(db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n) { static const char sql_str[] = "where "; static const size_t sql_str_len = sizeof(sql_str) - 1; char sql_bind_char = 'a'; /* XXX: assert(_n < 'z' - 'a' + 1) */ if (_n > 0) { size_t cur_len; memcpy(sqlbuf_pos, sql_str, sql_str_len); sqlbuf_pos += sql_str_len; cur_len = strlen(*_k); memcpy(sqlbuf_pos, *_k++, cur_len); sqlbuf_pos += cur_len; if (VAL_NULL(_v++)) { prepare_null(_op); } else { /* Default operation is '=' */ if (_op == NULL) { *sqlbuf_pos++ = sql_eq_char; } else { cur_len = strlen(*_op); memcpy(sqlbuf_pos, *_op, cur_len); sqlbuf_pos += cur_len; } *sqlbuf_pos++ = ':'; *sqlbuf_pos++ = sql_bind_char++; } if (_op != NULL) ++_op; while (--_n) { *sqlbuf_pos++ = ' '; *sqlbuf_pos++ = 'a'; *sqlbuf_pos++ = 'n'; *sqlbuf_pos++ = 'd'; *sqlbuf_pos++ = ' '; cur_len = strlen(*_k); memcpy(sqlbuf_pos, *_k++, cur_len); sqlbuf_pos += cur_len; if (VAL_NULL(_v++)) { prepare_null(_op); } else { /* Default operation is '=' */ if (_op == NULL) { *sqlbuf_pos++ = sql_eq_char; } else { cur_len = strlen(*_op); memcpy(sqlbuf_pos, *_op, cur_len); sqlbuf_pos += cur_len; } *sqlbuf_pos++ = ':'; *sqlbuf_pos++ = sql_bind_char++; } if (_op != NULL) ++_op; } *sqlbuf_pos++ = ' '; } } /* * ^select ,,..., * or * ^select * */ void prepare_select(db_key_t* _c, int _nc) { static const char sql_str[] = "select "; static const size_t sql_str_len = sizeof(sql_str) - 1; memcpy(sqlbuf, sql_str, sql_str_len); sqlbuf_pos = sqlbuf + sql_str_len; if (_nc <= 0) { *sqlbuf_pos++ = '*'; } else { size_t cur_len; cur_len = strlen(*_c); memcpy(sqlbuf_pos, *_c++, cur_len); sqlbuf_pos += cur_len; while (--_nc) { *sqlbuf_pos++ = ','; cur_len = strlen(*_c); memcpy(sqlbuf_pos, *_c++, cur_len); sqlbuf_pos += cur_len; } } *sqlbuf_pos++ = ' '; } /* from */ void prepare_from(const char* _t) { static const char sql_str[] = "from "; static const size_t sql_str_len = sizeof(sql_str) - 1; size_t cur_len; memcpy(sqlbuf_pos, sql_str, sql_str_len); sqlbuf_pos += sql_str_len; cur_len = strlen(_t); memcpy(sqlbuf_pos, _t, cur_len); sqlbuf_pos += cur_len; *sqlbuf_pos++ = ' '; } /* * order by * or none */ void prepare_order_by(db_key_t _o) { if (_o != NULL) { static const char sql_str[] = "order by "; static const size_t sql_str_len = sizeof(sql_str) - 1; size_t cur_len; memcpy(sqlbuf_pos, sql_str, sql_str_len); sqlbuf_pos += sql_str_len; cur_len = strlen(_o); memcpy(sqlbuf_pos, _o, cur_len); sqlbuf_pos += cur_len; *sqlbuf_pos++ = ' '; } } /* ^insert into
*/ void prepare_insert(const char* _t) { static const char sql_str[] = "insert into "; static const size_t sql_str_len = sizeof(sql_str) - 1; size_t cur_len; memcpy(sqlbuf, sql_str, sql_str_len); sqlbuf_pos = sqlbuf + sql_str_len; cur_len = strlen(_t); memcpy(sqlbuf_pos, _t, cur_len); sqlbuf_pos += cur_len; *sqlbuf_pos++ = ' '; } /* (, , ..., ) */ void prepare_insert_columns(db_key_t* _k, int _n) { size_t cur_len; *sqlbuf_pos++ = '('; cur_len = strlen(*_k); memcpy(sqlbuf_pos, *_k++, cur_len); sqlbuf_pos += cur_len; while (--_n) { *sqlbuf_pos++ = ','; cur_len = strlen(*_k); memcpy(sqlbuf_pos, *_k++, cur_len); sqlbuf_pos += cur_len; } *sqlbuf_pos++ = ')'; *sqlbuf_pos++ = ' '; } /* values (:a, :b, ..., :n) */ void prepare_insert_values(db_val_t* _v, int _n) { static const char sql_str[] = "values ("; static const size_t sql_str_len = sizeof(sql_str) - 1; char sql_bind_char = 'a'; memcpy(sqlbuf_pos, sql_str, sql_str_len); sqlbuf_pos += sql_str_len; if (VAL_NULL(_v++)) { *sqlbuf_pos++ = 'n'; *sqlbuf_pos++ = 'u'; *sqlbuf_pos++ = 'l'; *sqlbuf_pos++ = 'l'; } else { *sqlbuf_pos++ = ':'; *sqlbuf_pos++ = sql_bind_char++; } while (--_n) { *sqlbuf_pos++ = ','; if (VAL_NULL(_v++)) { *sqlbuf_pos++ = 'n'; *sqlbuf_pos++ = 'u'; *sqlbuf_pos++ = 'l'; *sqlbuf_pos++ = 'l'; } else { *sqlbuf_pos++ = ':'; *sqlbuf_pos++ = sql_bind_char++; } } *sqlbuf_pos++ = ')'; *sqlbuf_pos++ = ' '; } /* ^delete from
*/ void prepare_delete(const char* _t) { static const char sql_str[] = "delete from "; static const size_t sql_str_len = sizeof(sql_str) - 1; size_t cur_len; memcpy(sqlbuf, sql_str, sql_str_len); sqlbuf_pos = sqlbuf + sql_str_len; cur_len = strlen(_t); memcpy(sqlbuf_pos, _t, cur_len); sqlbuf_pos += cur_len; *sqlbuf_pos++ = ' '; } /* ^update
*/ void prepare_update(const char* _t) { static const char sql_str[] = "update "; static const size_t sql_str_len = sizeof(sql_str) - 1; size_t cur_len; memcpy(sqlbuf, sql_str, sql_str_len); sqlbuf_pos = sqlbuf + sql_str_len; cur_len = strlen(_t); memcpy(sqlbuf_pos, _t, cur_len); sqlbuf_pos += cur_len; *sqlbuf_pos++ = ' '; } /* set =:a,=:b,...,=:n */ void prepare_update_set(db_key_t* _k, db_val_t* _v, int _n) { size_t cur_len; char sql_bind_char = 'a'; *sqlbuf_pos++ = 's'; *sqlbuf_pos++ = 'e'; *sqlbuf_pos++ = 't'; *sqlbuf_pos++ = ' '; cur_len = strlen(*_k); memcpy(sqlbuf_pos, *_k++, cur_len); sqlbuf_pos += cur_len; *sqlbuf_pos++ = sql_eq_char; if (VAL_NULL(_v++)) { *sqlbuf_pos++ = 'n'; *sqlbuf_pos++ = 'u'; *sqlbuf_pos++ = 'l'; *sqlbuf_pos++ = 'l'; } else { *sqlbuf_pos++ = ':'; *sqlbuf_pos++ = sql_bind_char++; } while (--_n) { *sqlbuf_pos++ = ','; cur_len = strlen(*_k); memcpy(sqlbuf_pos, *_k++, cur_len); sqlbuf_pos += cur_len; *sqlbuf_pos++ = sql_eq_char; if (VAL_NULL(_v++)) { *sqlbuf_pos++ = 'n'; *sqlbuf_pos++ = 'u'; *sqlbuf_pos++ = 'l'; *sqlbuf_pos++ = 'l'; } else { *sqlbuf_pos++ = ':'; *sqlbuf_pos++ = sql_bind_char++; } } *sqlbuf_pos++ = ' '; } const char *prepared_sql(void) { /* OCI statements should be null terminated according to OCI docs. */ *sqlbuf_pos = '\0'; return sqlbuf; } /* without trailing null character */ size_t prepared_sql_len(void) { return sqlbuf_pos - sqlbuf; } kamailio-4.0.4/obsolete/oracle/Makefile0000644000000000000000000000112012223032460016510 0ustar rootroot # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen = NAME = oracle.so ORACLE_ROOT = /usr/lib/oracle/xe/app/oracle/product/10.2.0/client ORACLE_LIBS = $(ORACLE_ROOT)/lib ORACLE_INCLUDES = $(ORACLE_ROOT)/rdbms/public # ORACLE_ROOT = /usr/local/instantclient # ORACLE_LIBS = $(ORACLE_ROOT) # ORACLE_INCLUDES = $(ORACLE_ROOT)/sdk/include DEFS += -I$(ORACLE_INCLUDES) LIBS += -L$(ORACLE_LIBS) -lclntsh DEFS+=-DSER_MOD_INTERFACE SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/oracle/common.h0000644000000000000000000000333712223032460016525 0ustar rootroot/* * Common definitions. * * Copyright (C) 2005 RingCentral Inc. * Created by Dmitry Semyonov * * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _COMMON_H_ #define _COMMON_H_ #define DB_DEBUG 1 #define DB_PREALLOC_SQLT_HANDLE 1 /* * Values are not included directly into the statements, * so we can safely use relatively small buffer size. */ #define SQL_STMT_BUFF_SZ 2048 /* Maximum supported number of columns in a table */ #define ORA_PARAMS_MAX 128 #define ORA_BINDS_MAX ('z' - 'a' + 1) #ifdef DB_DEBUG #define DBG1(fmt, args...) LOG(L_DBG, fmt, ## args) #endif #warning "\ This module is experimental and may crash SER or create unexpected \ results. You use the module at your own risk. Please submit bugs at \ http://tracker.iptel.org" #endif /* _COMMON_H_ */ kamailio-4.0.4/obsolete/pa/0000755000000000000000000000000012223032460014211 5ustar rootrootkamailio-4.0.4/obsolete/pa/watcher.c0000644000000000000000000004334612223032460016024 0ustar rootroot/* * Presence Agent, watcher structure and related functions * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "paerrno.h" #include "../../lib/srdb2/db.h" #include "../../dprint.h" #include "../../parser/parse_event.h" #include "../../mem/shm_mem.h" #include "../../trim.h" #include "../../ut.h" #include "pa_mod.h" #include "watcher.h" #include "presentity.h" #include "auth.h" #include "ptime.h" str watcher_status_names[] = { [WS_PENDING] = STR_STATIC_INIT("pending"), [WS_ACTIVE] = STR_STATIC_INIT("active"), [WS_REJECTED] = STR_STATIC_INIT("rejected"), [WS_TERMINATED] = STR_STATIC_INIT("terminated"), [WS_PENDING_TERMINATED] = STR_STATIC_INIT("terminated"), STR_NULL }; str watcher_event_names[] = { [WE_SUBSCRIBE] = STR_STATIC_INIT("subscribe"), [WE_APPROVED] = STR_STATIC_INIT("approved"), [WE_DEACTIVATED] = STR_STATIC_INIT("deactivated"), [WE_PROBATION] = STR_STATIC_INIT("probation"), [WE_REJECTED] = STR_STATIC_INIT("rejected"), [WE_TIMEOUT] = STR_STATIC_INIT("timeout"), [WE_GIVEUP] = STR_STATIC_INIT("giveup"), [WE_NORESOURCE] = STR_STATIC_INIT("noresource"), STR_NULL }; const char *event_package2str(int et) /* FIXME: change working with this to enum ?*/ { /* added due to incorrect package handling */ switch (et) { case EVENT_PRESENCE: return "presence"; case EVENT_PRESENCE_WINFO: return "presence.winfo"; /*case EVENT_XCAP_CHANGE: return ...; */ default: return "unknown"; } } int str2event_package(const char *epname) { /* work only with packages supported by PA! */ if (strcmp(epname, "presence") == 0) return EVENT_PRESENCE; if (strcmp(epname, "presence.winfo") == 0) return EVENT_PRESENCE_WINFO; return -1; /* unsupported */ } /*int event_package_from_string(str *epname) { int i; for (i = 0; event_package_name[i]; i++) { if (strcasecmp(epname->s, event_package_name[i]) == 0) { return i; } } return 0; }*/ watcher_status_t watcher_status_from_string(str *wsname) { int i; for (i = 0; watcher_status_names[i].len; i++) { if (str_nocase_equals(wsname, &watcher_status_names[i]) == 0) { return i; } } return 0; } static watcher_event_t watcher_event_from_string(str *wename) { int i; for (i = 0; watcher_event_names[i].len; i++) { if (str_nocase_equals(wename, &watcher_event_names[i]) == 0) { return i; } } return 0; } /* be sure s!=NULL */ /* compute a hash value for a string */ unsigned int compute_hash(unsigned int _h, char* s, int len) { #define h_inc h+=v^(v>>3); char* p; register unsigned v; register unsigned h = _h; for(p=s; p<=(s+len-4); p+=4) { v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; h_inc; } v=0; for(;p<(s+len); p++) { v<<=8; v+=*p; } h_inc; return h; } /* * Create a new watcher structure but do not write to database */ int new_watcher_no_wb(str* _uri, time_t _e, int event_package, int doc_type, dlg_t* _dlg, str *_dn, str *server_contact, str *id, watcher_t** _w) { watcher_t* watcher; int size; dbid_t dbid; str sid; /* Check parameters */ if (!_uri && !_dlg && !_w) { LOG(L_ERR, "new_watcher(): Invalid parameter value\n"); return -1; } if (!id) { /* generate new database/watcher id (needed for winfo documents!) */ generate_dbid(dbid); sid.s = dbid_strptr(dbid); sid.len = dbid_strlen(dbid); id = &sid; } /* Allocate memory buffer for watcher_t structure and uri string */ size = sizeof(watcher_t) + id->len + _uri->len + _dn->len + server_contact->len; watcher = (watcher_t*)mem_alloc(size); if (!watcher) { paerrno = PA_NO_MEMORY; ERR("No memory left (%d bytes)\n", size); return -1; } memset(watcher, 0, sizeof(watcher_t)); /* Copy ID string */ watcher->id.s = (char*)watcher + sizeof(watcher_t); str_cpy(&watcher->id, id); /* Copy uri string */ watcher->uri.s = after_str_ptr(&watcher->id); str_cpy(&watcher->uri, _uri); /* Copy display_name string */ watcher->display_name.s = after_str_ptr(&watcher->uri); str_cpy(&watcher->display_name, _dn); /* Copy server_contact string */ watcher->server_contact.s = after_str_ptr(&watcher->display_name); str_cpy(&watcher->server_contact, server_contact); watcher->document_index = 0; watcher->event_package = event_package; watcher->expires = _e; /* Expires value */ watcher->preferred_mimetype = doc_type; /* Accepted document type */ watcher->dialog = _dlg; /* Dialog handle */ watcher->event = WE_SUBSCRIBE; watcher->status = WS_PENDING; *_w = watcher; return 0; } static int set_watcher_db_data(presentity_t *_p, watcher_t *watcher, db_key_t *cols, db_val_t *vals, int *col_cnt, str *dialog_str /* destination for dialog string -> must be freed after ! */ ) { int n_cols = 0; char *package = (char*)event_package2str(watcher->event_package); str dialog; /* serialized dialog */ str_clear(dialog_str); if (dlg_func.dlg2str(watcher->dialog, &dialog) != 0) { LOG(L_ERR, "Error while serializing dialog\n"); return -1; } cols[n_cols] = col_w_uri; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val = watcher->uri; n_cols++; cols[n_cols] = col_package; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val.s = package; vals[n_cols].val.str_val.len = strlen(package); n_cols++; cols[n_cols] = col_s_id; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val = watcher->id; n_cols++; cols[n_cols] = col_status; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val = watcher_status_names[watcher->status]; n_cols++; cols[n_cols] = col_event; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val = watcher_event_names[watcher->event]; n_cols++; cols[n_cols] = col_display_name; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val.s = watcher->display_name.s; vals[n_cols].val.str_val.len = watcher->display_name.len; n_cols++; cols[n_cols] = col_accepts; vals[n_cols].type = DB_INT; vals[n_cols].nul = 0; vals[n_cols].val.int_val = watcher->preferred_mimetype; n_cols++; cols[n_cols] = col_expires; vals[n_cols].type = DB_DATETIME; vals[n_cols].nul = 0; vals[n_cols].val.time_val = watcher->expires; n_cols++; cols[n_cols] = col_dialog; vals[n_cols].type = DB_BLOB; vals[n_cols].nul = 0; vals[n_cols].val.blob_val = dialog; n_cols++; cols[n_cols] = col_server_contact; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val = watcher->server_contact; n_cols++; cols[n_cols] = col_pres_id; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val = _p->pres_id; n_cols++; cols[n_cols] = col_doc_index; vals[n_cols].type = DB_INT; vals[n_cols].nul = 0; vals[n_cols].val.int_val = watcher->document_index; n_cols++; *col_cnt = n_cols; if (dialog_str) *dialog_str = dialog; return 0; } static int db_add_watcher(presentity_t *_p, watcher_t *watcher) { str_t tmp; db_key_t query_cols[20]; db_val_t query_vals[20]; int n_query_cols = 0; if (!use_db) return 0; str_clear(&tmp); if (pa_dbf.use_table(pa_db, watcherinfo_table) < 0) { LOG(L_ERR, "db_add_watcher: Error in use_table\n"); return -1; } if (set_watcher_db_data(_p, watcher, query_cols, query_vals, &n_query_cols, &tmp) != 0) { return -1; } /* insert new record into database */ if (pa_dbf.insert(pa_db, query_cols, query_vals, n_query_cols) < 0) { LOG(L_ERR, "db_add_watcher: Error while inserting watcher\n"); str_free_content(&tmp); return -1; } str_free_content(&tmp); return 0; } int db_update_watcher(presentity_t *_p, watcher_t *watcher) { str tmp; db_key_t query_cols[20]; db_val_t query_vals[20]; int n_query_cols = 0; db_key_t keys[] = { col_s_id }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = watcher->id } } }; if (!use_db) return 0; str_clear(&tmp); if (pa_dbf.use_table(pa_db, watcherinfo_table) < 0) { LOG(L_ERR, "db_update_watcher: Error in use_table\n"); return -1; } if (set_watcher_db_data(_p, watcher, query_cols, query_vals, &n_query_cols, &tmp) != 0) { return -1; } if (pa_dbf.update(pa_db, keys, ops, k_vals, query_cols, query_vals, 1, n_query_cols) < 0) { LOG(L_ERR, "Error while updating watcher in DB\n"); str_free_content(&tmp); return -1; } str_free_content(&tmp); return 0; } static inline int db_remove_watcher(struct presentity *_p, watcher_t *w) { db_key_t keys[] = { col_s_id }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = w->id } } }; if (!use_db) return 0; if (pa_dbf.use_table(pa_db, watcherinfo_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.delete(pa_db, keys, ops, k_vals, 1) < 0) { ERR("Error while deleting watcher from DB\n"); return -1; } return 0; } /* * Read watcherinfo table from database for presentity _p */ int db_read_watcherinfo(presentity_t *_p, db_con_t* db) { db_key_t query_cols[5]; db_op_t query_ops[5]; db_val_t query_vals[5]; str dialog = STR_NULL; dlg_t *dlg = NULL; db_key_t result_cols[11]; db_res_t *res; int r = 0; int n_query_cols = 1; int n_result_cols = 0; int w_uri_col, s_id_col, event_package_col, status_col, watcher_event_col, display_name_col, accepts_col, expires_col, dialog_col, server_contact_col, doc_index_col; if (!use_db) return 0; /* LOG(L_ERR, "db_read_watcherinfo starting\n");*/ query_cols[0] = col_pres_id; query_ops[0] = OP_EQ; query_vals[0].type = DB_STR; query_vals[0].nul = 0; query_vals[0].val.str_val = _p->pres_id; result_cols[w_uri_col = n_result_cols++] = col_w_uri; result_cols[s_id_col = n_result_cols++] = col_s_id; result_cols[event_package_col = n_result_cols++] = col_package; result_cols[status_col = n_result_cols++] = col_status; result_cols[display_name_col = n_result_cols++] = col_display_name; result_cols[accepts_col = n_result_cols++] = col_accepts; result_cols[expires_col = n_result_cols++] = col_expires; result_cols[watcher_event_col = n_result_cols++] = col_event; result_cols[dialog_col = n_result_cols++] = col_dialog; result_cols[server_contact_col = n_result_cols++] = col_server_contact; result_cols[doc_index_col = n_result_cols++] = col_doc_index; if (pa_dbf.use_table(db, watcherinfo_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.query (db, query_cols, query_ops, query_vals, result_cols, n_query_cols, n_result_cols, 0, &res) < 0) { ERR("Error while querying watcherinfo\n"); return -1; } if (res && (res->n > 0)) { /* fill in tuple structure from database query result */ int i; dlg = NULL; for (i = 0; i < res->n; i++) { db_row_t *row = &res->rows[i]; db_val_t *row_vals = ROW_VALUES(row); str w_uri = STR_NULL; str s_id = STR_NULL; char *event_package_str = NULL; int event_package = EVENT_PRESENCE; str watcher_event_str = STR_NULL; watcher_event_t watcher_event = WE_SUBSCRIBE; int accepts = row_vals[accepts_col].val.int_val; time_t expires = row_vals[expires_col].val.time_val; int doc_index = row_vals[doc_index_col].val.int_val; str status = STR_NULL; str display_name = STR_NULL; str server_contact = STR_NULL; watcher_t *watcher = NULL; if (!row_vals[w_uri_col].nul) { w_uri.s = (char *)row_vals[w_uri_col].val.string_val; w_uri.len = strlen(w_uri.s); } if (!row_vals[s_id_col].nul) { s_id.s = (char *)row_vals[s_id_col].val.string_val; s_id.len = strlen(s_id.s); } if (!row_vals[event_package_col].nul) { event_package_str = (char *)row_vals[event_package_col].val.string_val; event_package = str2event_package(event_package_str); } if (!row_vals[status_col].nul) { status.s = (char *)row_vals[status_col].val.string_val; status.len = strlen(status.s); } if (!row_vals[watcher_event_col].nul) { watcher_event_str.s = (char *)row_vals[watcher_event_col].val.string_val; watcher_event_str.len = strlen(watcher_event_str.s); watcher_event = watcher_event_from_string(&watcher_event_str); } if (!row_vals[display_name_col].nul) { display_name.s = (char *)row_vals[display_name_col].val.string_val; display_name.len = strlen(display_name.s); } if (!row_vals[dialog_col].nul) { dialog = row_vals[dialog_col].val.blob_val; dlg = (dlg_t*)mem_alloc(sizeof(*dlg)); if (!dlg) { LOG(L_ERR, "db_read_watcher: Can't allocate dialog\n"); r = -1; } else if (dlg_func.str2dlg(&dialog, dlg) != 0) { LOG(L_ERR, "db_read_watcher: Error while deserializing dialog\n"); r = -1; } } if (!row_vals[server_contact_col].nul) { server_contact.s = (char *)row_vals[server_contact_col].val.string_val; server_contact.len = strlen(server_contact.s); } DBG("creating watcher\n"); if (new_watcher_no_wb(&w_uri, expires, event_package, accepts, dlg, &display_name, &server_contact, &s_id, &watcher) == 0) { watcher->status = watcher_status_from_string(&status); watcher->event = watcher_event; watcher->document_index = doc_index; r = append_watcher(_p, watcher, 0); if (r < 0) { ERR("Error while adding watcher\n"); free_watcher(watcher); } } else r = -1; } } pa_dbf.free_result(db, res); return r; } /* * Release a watcher structure */ void free_watcher(watcher_t* _w) { tmb.free_dlg(_w->dialog); mem_free(_w); } /* * Update a watcher structure */ int update_watcher(struct presentity *p, watcher_t* _w, time_t _e, struct sip_msg *m) { watcher_status_t old = _w->status; /* old status of subscription */ tmb.dlg_request_uas(_w->dialog, m, IS_TARGET_REFRESH); _w->expires = _e; _w->flags |= WFLAG_SUBSCRIPTION_CHANGED; /* actualize watcher's status according to time */ if (_w->expires <= act_time) { /* ERR("Updated watcher to expire: %.*s\n", _w->uri.len, _w->uri.s); */ _w->expires = 0; set_watcher_terminated_status(_w); } if ((old != _w->status) && (_w->event_package == EVENT_PRESENCE)) { /* changed only when presence watcher changes status */ /* FIXME: it could be changed when expires time changes too, * but we don't send expiration in watcherinf notify thus * it is worthless */ p->flags |= PFLAG_WATCHERINFO_CHANGED; } if (use_db) return db_update_watcher(p, _w); else return 0; } int is_watcher_terminated(watcher_t *w) { if (!w) return -1; if ((w->status == WS_TERMINATED) || (w->status == WS_REJECTED) || (w->status == WS_PENDING_TERMINATED)) return 1; return 0; } int is_watcher_authorized(watcher_t *w) { if (!w) return 0; switch (w->status) { case WS_PENDING: ; case WS_PENDING_TERMINATED: ; case WS_REJECTED: return 0; case WS_ACTIVE: ; case WS_TERMINATED: return 1; } return 0; } void set_watcher_terminated_status(watcher_t *w) { if (!w) return; switch (w->status) { case WS_REJECTED: break; case WS_PENDING: ; w->status = WS_PENDING_TERMINATED; break; case WS_PENDING_TERMINATED: break; case WS_TERMINATED: break; case WS_ACTIVE: ; w->status = WS_TERMINATED; break; } } int append_watcher(presentity_t *_p, watcher_t *_w, int add_to_db) { if (add_to_db && use_db) { if (db_add_watcher(_p, _w) != 0) { ERR("Error while adding watcher\n"); paerrno = PA_INTERNAL_ERROR; return -5; } } if (_w->event_package == EVENT_PRESENCE_WINFO) { DOUBLE_LINKED_LIST_ADD(_p->first_winfo_watcher, _p->last_winfo_watcher, _w); } else { DOUBLE_LINKED_LIST_ADD(_p->first_watcher, _p->last_watcher, _w); /* changed only when presence watcher added */ _p->flags |= PFLAG_WATCHERINFO_CHANGED; DEBUG("setting PFLAG_WATCHERINFO_CHANGED\n"); } return 0; } void remove_watcher(presentity_t* _p, watcher_t *w) { if (!w) return; if (use_db) db_remove_watcher(_p, w); if (w->event_package == EVENT_PRESENCE_WINFO) DOUBLE_LINKED_LIST_REMOVE(_p->first_winfo_watcher, _p->last_winfo_watcher, w); else { DOUBLE_LINKED_LIST_REMOVE(_p->first_watcher, _p->last_watcher, w); _p->flags |= PFLAG_WATCHERINFO_CHANGED; } } /* returns 0 if equal dialog IDs */ static int cmp_dlg_ids(dlg_id_t *a, dlg_id_t *b) { if (!a) { if (!b) return -1; else return 0; } if (!b) return 1; if (str_case_equals(&a->call_id, &b->call_id) != 0) return 1; if (str_case_equals(&a->rem_tag, &b->rem_tag) != 0) return 1; /* case sensitive ? */ if (str_case_equals(&a->loc_tag, &b->loc_tag) != 0) return 1; /* case sensitive ? */ return 0; } /* Find a watcher in the list via dialog identifier */ int find_watcher_dlg(struct presentity* _p, dlg_id_t *dlg_id, int _et, watcher_t** _w) { watcher_t* watcher; /* first look for watchers */ if (!dlg_id) return -1; if (_et != EVENT_PRESENCE_WINFO) watcher = _p->first_watcher; else watcher = _p->first_winfo_watcher; while(watcher) { if (watcher->dialog) { if ((cmp_dlg_ids(&watcher->dialog->id, dlg_id) == 0) && (watcher->event_package == _et)) { *_w = watcher; return 0; } } watcher = watcher->next; } return 1; } kamailio-4.0.4/obsolete/pa/extension_elements.h0000644000000000000000000000134512223032460020275 0ustar rootroot#ifndef __EXTENSION_ELEMENTS_H #define __EXTENSION_ELEMENTS_H #include "presentity.h" /* EXTENSION ELEMENT functions */ int db_update_extension_element(presentity_t *p, pa_extension_element_t *n); void add_extension_element(presentity_t *_p, pa_extension_element_t *n); void free_pa_extension_element(pa_extension_element_t *n); void remove_extension_element(presentity_t *_p, pa_extension_element_t *n); int remove_extension_elements(presentity_t *p, str *etag); pa_extension_element_t *create_pa_extension_element(str *etag, str *element, time_t expires, str *dbid); int db_read_extension_elements(presentity_t *p, db_con_t* db); pa_extension_element_t *extension_element2pa(extension_element_t *n, str *etag, time_t expires); #endif kamailio-4.0.4/obsolete/pa/pdomain.c0000644000000000000000000001514412223032460016011 0ustar rootroot/* * Presence Agent, domain support * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2003-03-11 converted to the new locking scheme: locking.h (andrei) */ #include "pdomain.h" #include "paerrno.h" #include "presentity.h" #include "../../ut.h" #include "../../dprint.h" #include "../../mem/shm_mem.h" #include #include "pa_mod.h" #include /* * Hash function */ static inline unsigned int hash_func(pdomain_t* _d, unsigned char* _s, int _l) { unsigned int res = 0, i; for(i = 0; i < _l; i++) { res += _s[i]; } return res % _d->size; } /* * Create a new domain structure * _n is pointer to str representing * name of the domain, the string is * not copied, it should point to str * structure stored in domain list * _s is hash table size */ int new_pdomain(str* _n, int _s, pdomain_t** _d, register_watcher_t _r, unregister_watcher_t _u) { int i; pdomain_t* ptr; ptr = (pdomain_t*)mem_alloc(sizeof(pdomain_t)); if (!ptr) { paerrno = PA_NO_MEMORY; LOG(L_ERR, "new_pdomain(): No memory left\n"); return -1; } memset(ptr, 0, sizeof(pdomain_t)); ptr->table = (hslot_t*)mem_alloc(sizeof(hslot_t) * _s); if (!ptr->table) { paerrno = PA_NO_MEMORY; LOG(L_ERR, "new_pdomain(): No memory left 2\n"); mem_free(ptr); return -2; } ptr->name = _n; for(i = 0; i < _s; i++) { init_slot(ptr, &ptr->table[i]); } ptr->size = _s; lock_init(&ptr->lock); ptr->users = 0; ptr->expired = 0; ptr->reg = _r; ptr->unreg = _u; *_d = ptr; return 0; } /* * Free all memory allocated for * the domain */ void free_pdomain(pdomain_t* _d) { int i; lock_pdomain(_d); if (_d->table) { for(i = 0; i < _d->size; i++) { deinit_slot(_d->table + i); } mem_free(_d->table); } unlock_pdomain(_d); mem_free(_d); } int timer_pdomain(pdomain_t* _d) { struct presentity* presentity, *t; time_t start, stop; PROF_START(pa_timer_pdomain) lock_pdomain(_d); start = time(NULL); presentity = _d->first; while(presentity) { if (timer_presentity(presentity) < 0) { LOG(L_ERR, "timer_pdomain(): Error in timer_pdomain\n"); unlock_pdomain(_d); PROF_STOP(pa_timer_pdomain) return -1; } /* Remove the entire record * if it is empty */ if ( (!presentity->first_watcher) && (!presentity->first_winfo_watcher) && (!presentity->data.first_tuple) && (!presentity->data.first_note) && (!presentity->data.first_unknown_element) && (!presentity->first_qsa_subscription) && (presentity->ref_cnt == 0)) { LOG(L_DBG, "timer_pdomain(): removing empty presentity\n"); t = presentity; presentity = presentity->next; release_presentity(t); } else { presentity = presentity->next; } } stop = time(NULL); if (stop - start > 1) WARN("timer_pdomain took %d seconds\n", (int) (stop - start)); unlock_pdomain(_d); PROF_STOP(pa_timer_pdomain) return 0; } static int in_pdomain = 0; /* this only works with single or multiprocess execution model, but not multi-threaded */ /* * Get lock if this process does not already have it */ void lock_pdomain(pdomain_t* _d) { DBG("lock_pdomain\n"); if (!in_pdomain++) lock_get(&_d->lock); } /* * Release lock */ void unlock_pdomain(pdomain_t* _d) { DBG("unlock_pdomain\n"); in_pdomain--; if (!in_pdomain) lock_release(&_d->lock); } /* * Find a presentity in domain according to uid */ int find_presentity_uid(pdomain_t* _d, str* uid, struct presentity** _p) { unsigned int sl, i; struct presentity* p; int res = 1; if ((!uid) || (!_p)) return -1; sl = hash_func(_d, (unsigned char *)uid->s, uid->len); p = _d->table[sl].first; for(i = 0; i < _d->table[sl].n; i++) { if ((p->uuid.len == uid->len) && !memcmp(p->uuid.s, uid->s, uid->len)) { *_p = p; res = 0; break; } p = p->next; } return res; /* Nothing found */ } /* * contact will be NULL if user is offline */ static void callback(str* _user, str *_contact, int state, void* data) { mq_message_t *msg; tuple_change_info_t *info; if ((!_user) || (!_contact) || (!data)) { ERROR_LOG("callback(): error!\n"); } /* asynchronous processing */ msg = create_message_ex(sizeof(tuple_change_info_t)); if (!msg) { LOG(L_ERR, "can't create message with tuple status change\n"); return; } set_data_destroy_function(msg, (destroy_function_f)free_tuple_change_info_content); info = get_message_data(msg); if (state == 0) info->state = presence_tuple_closed; else info->state = presence_tuple_open; str_dup(&info->user, _user); str_dup(&info->contact, _contact); if (data) push_message(&((struct presentity*)data)->mq, msg); } void add_presentity(pdomain_t* _d, struct presentity* _p) { unsigned int sl; sl = hash_func(_d, (unsigned char *)_p->uuid.s, _p->uuid.len); slot_add(&_d->table[sl], _p, &_d->first, &_d->last); if (use_callbacks) { DBG("! registering callback to %.*s, %p\n", _p->uuid.len, _p->uuid.s,_p); _d->reg(&_p->data.uri, &_p->uuid, (void*)callback, _p); } if (subscribe_to_users) { TRACE("! subscribing to %.*s, %p\n", _p->uuid.len, _p->uuid.s,_p); subscribe_to_user(_p); } } void remove_presentity(pdomain_t* _d, struct presentity* _p) { if (use_callbacks) { DBG("! unregistering callback to %.*s, %p\n", _p->uuid.len, _p->uuid.s,_p); _d->unreg(&_p->data.uri, &_p->uuid, (void*)callback, _p); DBG("! unregistered callback to %.*s, %p\n", _p->uuid.len, _p->uuid.s,_p); } if (subscribe_to_users) { DBG("! unsubscribing from %.*s, %p\n", _p->uuid.len, _p->uuid.s,_p); unsubscribe_to_user(_p); } LOG(L_DBG, "remove_presentity _p=%p p_uri=%.*s\n", _p, _p->data.uri.len, _p->data.uri.s); slot_rem(_p->slot, _p, &_d->first, &_d->last); /* remove presentity from database */ } kamailio-4.0.4/obsolete/pa/status_query.h0000644000000000000000000000026512223032460017135 0ustar rootroot#ifndef __STATUS_QUERY_H #define __STATUS_QUERY_H int target_online(struct sip_msg* _m, char* _domain, char* _s2); /* add other query functions: get_online_contact, ... */ #endif kamailio-4.0.4/obsolete/pa/tuple_notes.c0000644000000000000000000000732312223032460016723 0ustar rootroot#include "presentity.h" #include "pa_mod.h" #include "tuple_notes.h" #include /* DB manipulation */ static int db_add_tuple_note(presentity_t *p, presence_tuple_t *t, presence_note_t *n) { db_key_t cols[20]; db_val_t vals[20]; int n_updates = 0; if (!use_db) return 0; /* set data */ cols[n_updates] = col_pres_id; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = p->pres_id; n_updates++; cols[n_updates] = col_tupleid; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = t->data.id; n_updates++; cols[n_updates] = col_note; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = n->value; n_updates++; cols[n_updates] = col_lang; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = n->lang; n_updates++; /* run update */ if (pa_dbf.use_table(pa_db, tuple_notes_table) < 0) { LOG(L_ERR, "db_add_pres_note: Error in use_table\n"); return -1; } if (pa_dbf.insert(pa_db, cols, vals, n_updates) < 0) { LOG(L_ERR, "db_add_pres_note: Can't insert record\n"); return -1; } return 0; } int db_add_tuple_notes(presentity_t *p, presence_tuple_t *t) { presence_note_t *n; n = t->data.first_note; while (n) { db_add_tuple_note(p, t, n); n = n->next; } return 0; } int db_remove_tuple_notes(presentity_t *p, presence_tuple_t *t) { db_key_t keys[] = { col_pres_id, col_tupleid }; db_op_t ops[] = { OP_EQ, OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = p->pres_id } }, { DB_STR, 0, { .str_val = t->data.id } } }; if (!use_db) return 0; if (pa_dbf.use_table(pa_db, tuple_notes_table) < 0) { LOG(L_ERR, "db_remove_tuple_notes: Error in use_table\n"); return -1; } if (pa_dbf.delete(pa_db, keys, ops, k_vals, 2) < 0) { LOG(L_ERR, "db_remove_tuple_notes: Can't delete record\n"); return -1; } return 0; } int db_update_tuple_notes(presentity_t *p, presence_tuple_t *t) { db_remove_tuple_notes(p, t); db_add_tuple_notes(p, t); return 0; } int db_read_tuple_notes(presentity_t *p, presence_tuple_t *t, db_con_t* db) { db_key_t keys[] = { col_pres_id, col_tupleid }; db_op_t ops[] = { OP_EQ, OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = p->pres_id } }, { DB_STR, 0, { .str_val = t->data.id } } }; int i; int r = 0; db_res_t *res = NULL; db_key_t result_cols[] = { col_note, col_lang }; if (!use_db) return 0; if (pa_dbf.use_table(db, tuple_notes_table) < 0) { LOG(L_ERR, "db_read_tuple_notes: Error in use_table\n"); return -1; } if (pa_dbf.query (db, keys, ops, k_vals, result_cols, 2, sizeof(result_cols) / sizeof(db_key_t), 0, &res) < 0) { LOG(L_ERR, "db_read_tuple_notes(): Error while querying notes\n"); return -1; } if (!res) return 0; /* ? */ for (i = 0; i < res->n; i++) { presence_note_t *n = NULL; db_row_t *row = &res->rows[i]; db_val_t *row_vals = ROW_VALUES(row); str note = STR_NULL; str lang = STR_NULL; #define get_str_val(i,dst) do{if(!row_vals[i].nul){dst.s=(char*)row_vals[i].val.string_val;dst.len=strlen(dst.s);}}while(0) get_str_val(0, note); get_str_val(1, lang); #undef get_str_val n = create_presence_note(¬e, &lang); if (n) add_tuple_note_no_wb(t, n); } pa_dbf.free_result(db, res); return r; } /* in memory operations */ void add_tuple_note_no_wb(presence_tuple_t *t, presence_note_t *n) { DOUBLE_LINKED_LIST_ADD(t->data.first_note, t->data.last_note, n); } void free_tuple_notes(presence_tuple_t *t) { presence_note_t *n, *nn; /* remove all notes for this tuple */ n = t->data.first_note; while (n) { nn = n->next; free_presence_note(n); n = nn; } t->data.first_note = NULL; t->data.last_note = NULL; } kamailio-4.0.4/obsolete/pa/async_auth.h0000644000000000000000000000036612223032460016525 0ustar rootroot#ifndef __ASYNC_AUTH_H #define __ASYNC_AUTH_H #include "presentity.h" #include int ask_auth_rules(presentity_t *p); int async_auth_timer_init(); extern int max_auth_requests_per_tick; extern int async_auth_queries; #endif kamailio-4.0.4/obsolete/pa/mimetypes.c0000644000000000000000000000630112223032460016371 0ustar rootroot#include "mimetypes.h" #include "../../dprint.h" #include "../../parser/parse_event.h" static event_mimetypes_t event_package_mimetypes[] = { { EVENT_PRESENCE, { MIMETYPE(APPLICATION,PIDFXML), /* MIMETYPE(APPLICATION,XML_MSRTC_PIDF), */ /* MIMETYPE(TEXT,XML_MSRTC_PIDF), */ MIMETYPE(APPLICATION,CPIM_PIDFXML), MIMETYPE(APPLICATION,XPIDFXML), MIMETYPE(APPLICATION,LPIDFXML), 0 } }, { EVENT_PRESENCE_WINFO, { MIMETYPE(APPLICATION,WATCHERINFOXML), 0 } }, /* { EVENT_SIP_PROFILE, { MIMETYPE(MESSAGE,EXTERNAL_BODY), 0 } }, */ /* { EVENT_XCAP_CHANGE, { MIMETYPE(APPLICATION,WINFO+XML), 0 } }, */ { -1, { 0 }}, }; /* returns -1 if m is NOT found in mimes * index to the found element otherwise (non-negative value) */ static int find_mime(int *mimes, int m) { int i; for (i = 0; mimes[i]; i++) { if (mimes[i] == m) return i; } return -1; } event_mimetypes_t *find_event_mimetypes(int et) { int i; event_mimetypes_t *em; i = 0; while (et != event_package_mimetypes[i].event_type) { if (event_package_mimetypes[i].event_type == -1) break; i++; } em = &event_package_mimetypes[i]; /* if not found is it the "separator" (-1, 0)*/ return em; } int check_mime_types(int *accepts_mimes, event_mimetypes_t *em) { /* LOG(L_ERR, "check_message -2- accepts_mimes=%p\n", accepts_mimes); */ if (accepts_mimes) { int j = 0, k; int mimetype; while ((mimetype = em->mimes[j]) != 0) { k = find_mime(accepts_mimes, mimetype); if (k >= 0) { int am0 = accepts_mimes[0]; /* we have a match */ /*LOG(L_ERR, "check_message -4b- eventtype=%#x accepts_mime=%#x\n", eventtype, mimetype); */ /* move it to front for later */ accepts_mimes[0] = mimetype; accepts_mimes[k] = am0; return 0; /* ! this may be useful, but it modifies the parsed content !!! */ } j++; } return -1; } return 0; } /* returns index of mimetype, the lowest index = highest priority */ static int get_accepted_mime_type_idx(int *accepts_mimes, event_mimetypes_t *em) { int i, mt; if (accepts_mimes) { /* try find "preferred" mime type */ i = 0; while ((mt = em->mimes[i]) != 0) { /* TRACE_LOG("searching for %x\n", mt); */ if (find_mime(accepts_mimes, mt) >= 0) return i; i++; } } return -1; } int get_preferred_event_mimetype(struct sip_msg *_m, int et) { int idx, tmp, acc = 0; int *accepts_mimes; struct hdr_field *accept; event_mimetypes_t *em = find_event_mimetypes(et); if (!em) return 0; /* never happens, but ... */ accept = _m->accept; idx = -1; while (accept) { /* go through all Accept headers */ if (accept->type == HDR_ACCEPT_T) { /* it MUST be parsed from parse_hdr !!! */ accepts_mimes = (int *)accept->parsed; tmp = get_accepted_mime_type_idx(accepts_mimes, em); if (idx == -1) idx = tmp; else if ((tmp != -1) && (tmp < idx)) idx = tmp; /* TRACE_LOG("%s: found mimetype %x (idx %d), %p\n", __FUNCTION__, (idx >= 0) ? em->mimes[idx]: -1, idx, accepts_mimes); */ if (idx == 0) break; /* the lowest value */ } accept = accept->next; } if (idx != -1) return em->mimes[idx]; /* found value with highest priority */ acc = em->mimes[0]; DBG("defaulting to mimetype %x for event_type=%d\n", acc, et); return acc; } kamailio-4.0.4/obsolete/pa/message.c0000644000000000000000000000433312223032460016004 0ustar rootroot#include "pa_mod.h" #include "message.h" #include "../../id.h" #include "../../parser/parse_from.h" #include #include static int xcap_get_msg_rules(str *uid, msg_rules_t **dst, str *filename, struct sip_msg *m) { xcap_query_params_t xcap; int res; /* str u; */ /* get only presentity name, not whole uri * can't use parse_uri because of absence * of protocol specification ! */ /* if (get_user_from_uri(uri, &u) != 0) u = *uri; */ memset(&xcap, 0, sizeof(xcap)); if (fill_xcap_params) fill_xcap_params(m, &xcap); res = get_msg_rules(uid, filename, &xcap, dst); return res; } static int get_sender_uri(struct sip_msg* _m, str* uri) { struct sip_uri puri; int res = 0; if (parse_headers(_m, HDR_FROM_F, 0) < 0) { ERR("Error while parsing headers\n"); return -1; } uri->s = get_from(_m)->uri.s; uri->len = get_from(_m)->uri.len; if (parse_uri(uri->s, uri->len, &puri) < 0) { LOG(L_ERR, "Error while parsing URI\n"); return -1; } uri->s = puri.user.s; if ((!uri->s) || (puri.user.len < 1)) { uri->s = puri.host.s; uri->len = puri.host.len; res = 1; /* it is uri without username ! */ } uri->len = puri.host.s + puri.host.len - uri->s; return res; } int authorize_message(struct sip_msg* _m, char* _filename, char*_st) { /* get and process XCAP authorization document */ /* may modify the message or its body */ str uid = STR_NULL; msg_rules_t *rules = NULL; msg_handling_t mh = msg_handling_allow; str sender_uri = STR_NULL; str tmp = STR_NULL; str *filename = NULL; int len; get_sender_uri(_m, &sender_uri); if (get_to_uid(&uid, _m) < 0) { ERR("get_to_uid failed\n"); /* enabled */ return 1; } if (_filename) { len =strlen(_filename); if (len > 0) { tmp.s = _filename; tmp.len = len; filename = &tmp; } } if (xcap_get_msg_rules(&uid, &rules, filename, _m) < 0) { /* enabled */ DBG("get_msg_rules failed\n"); return 1; } if (get_msg_rules_action(rules, &sender_uri, &mh) != 0) mh = msg_handling_allow; free_msg_rules(rules); switch (mh) { case msg_handling_block: DBG("XCAP AUTH MESSAGE: block\n"); return -1; case msg_handling_allow: DBG("XCAP AUTH MESSAGE: allow\n"); return 1; } return -1; } kamailio-4.0.4/obsolete/pa/winfo_doc.c0000644000000000000000000001234512223032460016331 0ustar rootroot#include "winfo_doc.h" #include #include static int doc_add_watcher(dstring_t *buf, watcher_t *w) { char tmp[64]; dstr_append_zt(buf, "\t\tstatus]); dstr_append_zt(buf, "\" event=\""); dstr_append_str(buf, &watcher_event_names[w->event]); dstr_append_zt(buf, "\" id=\""); if (w->id.len < 1) { sprintf(tmp, "%p", w); dstr_append_zt(buf, tmp); } else dstr_append_str(buf, &w->id); dstr_append_zt(buf, "\">"); dstr_append_str(buf, &w->uri); dstr_append_zt(buf, "\r\n"); return 0; } static int doc_add_internal_watcher(dstring_t *buf, internal_pa_subscription_t *iw) { char tmp[64]; dstr_append_zt(buf, "\t\tstatus]); dstr_append_zt(buf, "\" event=\""); dstr_append_str(buf, &watcher_event_names[0]); dstr_append_zt(buf, "\" id=\""); sprintf(tmp, "%pi", iw); dstr_append_zt(buf, tmp); dstr_append_zt(buf, "\">"); dstr_append_str(buf, get_subscriber_id(iw->subscription)); dstr_append_zt(buf, "\r\n"); return 0; } static int doc_add_watcher_list(dstring_t *buf, struct presentity* p) { watcher_t *watcher = p->first_watcher; internal_pa_subscription_t *subscription = p->first_qsa_subscription; dstr_append_zt(buf, "\tdata.uri); dstr_append_zt(buf, "\" package=\"presence\">\r\n"); while (watcher) { doc_add_watcher(buf, watcher); watcher = watcher->next; } while (subscription) { doc_add_internal_watcher(buf, subscription); subscription = subscription->next; } /* FIXME: for testing - create too big document to be sent * and trace memory where occurs the leak !!!*/ /* if (1) { int i; for (i = 0; i < 1000; i++) { dstr_append_zt(buf, "\r\n"); } } */ dstr_append_zt(buf, "\t\r\n"); return 0; } static int doc_add_winfo(dstring_t *buf, struct presentity* p, struct watcher* w) { char tmp[256]; dstr_append_zt(buf, "document_index); dstr_append_zt(buf, tmp); dstr_append_zt(buf, "\" state=\"full\">\r\n"); doc_add_watcher_list(buf, p); dstr_append_zt(buf, "\r\n"); return 0; } int create_winfo_document(struct presentity* p, struct watcher* w, str *dst, str *dst_content_type) { dstring_t buf; int err; if (!dst) return -1; str_clear(dst); if (dst_content_type) str_clear(dst_content_type); if ((!p) || (!w)) return -1; if (dst_content_type) if (str_dup_zt(dst_content_type, "application/watcherinfo+xml") < 0) { return -1; } /* if (!p->first_tuple) return 0;*/ /* no tuples => nothing to say */ dstr_init(&buf, 2048); dstr_append_zt(&buf, "\r\n"); doc_add_winfo(&buf, p, w); err = dstr_get_str(&buf, dst); dstr_destroy(&buf); if (err != 0) { str_free_content(dst); if (dst_content_type) str_free_content(dst_content_type); } return err; } static int doc_add_watcher_offline(dstring_t *buf, offline_winfo_t *w) { char tmp[64]; char *wevent = "subscribe"; dstr_append_zt(buf, "\t\tstatus); dstr_append_zt(buf, "\" event=\""); dstr_append_zt(buf, wevent); dstr_append_zt(buf, "\" id=\""); sprintf(tmp, "ol%p%x", w, rand()); dstr_append_zt(buf, tmp); dstr_append_zt(buf, "\">"); dstr_append_str(buf, &w->watcher); dstr_append_zt(buf, "\r\n"); return 0; } static int doc_add_watcher_list_offline(dstring_t *buf, struct presentity* p, offline_winfo_t *info) { offline_winfo_t *i = info; dstr_append_zt(buf, "\tdata.uri); dstr_append_zt(buf, "\" package=\"presence\">\r\n"); while (i) { doc_add_watcher_offline(buf, i); i = i->next; } dstr_append_zt(buf, "\t\r\n"); return 0; } static int doc_add_winfo_offline(dstring_t *buf, struct presentity* p, struct watcher* w, offline_winfo_t *info) { char tmp[256]; dstr_append_zt(buf, "document_index); dstr_append_zt(buf, tmp); dstr_append_zt(buf, "\" state=\"partial\">\r\n"); /* !!! only partial notification !!! */ doc_add_watcher_list_offline(buf, p, info); dstr_append_zt(buf, "\r\n"); return 0; } /* create the NOTIFY from given infos */ int create_winfo_document_offline(struct presentity* p, struct watcher* w, offline_winfo_t *infos, str *dst, str *dst_content_type) { dstring_t buf; if (!dst) return -1; str_clear(dst); if (dst_content_type) str_clear(dst_content_type); if ((!p) || (!w)) return -1; if (dst_content_type) str_dup_zt(dst_content_type, "application/watcherinfo+xml"); /* if (!p->first_tuple) return 0;*/ /* no tuples => nothing to say */ dstr_init(&buf, 2048); dstr_append_zt(&buf, "\r\n"); doc_add_winfo_offline(&buf, p, w, infos); dstr_get_str(&buf, dst); dstr_destroy(&buf); return 0; } kamailio-4.0.4/obsolete/pa/ptime.c0000644000000000000000000000220612223032460015473 0ustar rootroot/* * Presence Agent, time handling * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "ptime.h" time_t act_time; void get_act_time(void) { act_time = time(0); } kamailio-4.0.4/obsolete/pa/tuple.c0000644000000000000000000003025312223032460015511 0ustar rootroot#include "presentity.h" #include #include "paerrno.h" #include "pa_mod.h" #include "tuple.h" #include "tuple_notes.h" #include "tuple_extensions.h" void add_presence_tuple_no_wb(presentity_t *_p, presence_tuple_t *_t); /* * Create a new presence_tuple */ int new_presence_tuple(str* _contact, time_t expires, presence_tuple_t ** _t, int is_published, str *id, str *published_id, str *etag) { presence_tuple_t* tuple; int size = 0; int len; dbid_t tmp; if (!_t) { paerrno = PA_INTERNAL_ERROR; ERR("Invalid parameter value\n"); return -1; } if (!id) { /* always needed (for PIDF documents, ...) */ generate_dbid(tmp); len = dbid_strlen(tmp); } else len = id->len; size = sizeof(presence_tuple_t); if (etag) size += etag->len; if (published_id) size += published_id->len; if (!is_published) { if (_contact) size += _contact->len; /* Non-published tuples have contact allocated * together with other data! */ } size += len; tuple = (presence_tuple_t*)mem_alloc(size); if (!tuple) { paerrno = PA_NO_MEMORY; ERR("No memory left: size=%d\n", size); return -1; } memset(tuple, 0, sizeof(presence_tuple_t)); tuple->data.status.basic = presence_tuple_undefined_status; tuple->data.id.s = ((char*)tuple) + sizeof(presence_tuple_t); if (id) str_cpy(&tuple->data.id, id); else dbid_strcpy(&tuple->data.id, tmp, len); tuple->etag.s = after_str_ptr(&tuple->data.id); if (etag) str_cpy(&tuple->etag, etag); else tuple->etag.len = 0; tuple->published_id.s = after_str_ptr(&tuple->etag); if (published_id) str_cpy(&tuple->published_id, published_id); else tuple->published_id.len = 0; if (is_published) { str_dup(&tuple->data.contact, _contact); /* published contacts can change */ } else { /* non-published contacts can NOT change !!! */ tuple->data.contact.s = after_str_ptr(&tuple->published_id); if (_contact) str_cpy(&tuple->data.contact, _contact); else tuple->data.contact.len = 0; } tuple->expires = expires; tuple->data.priority = default_priority; tuple->is_published = is_published; *_t = tuple; return 0; } int db_read_tuples(presentity_t *_p, db_con_t* db) { db_key_t keys[] = { col_pres_id }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = _p->pres_id } } }; int i; int r = 0; db_res_t *res = NULL; db_key_t result_cols[] = { col_basic, col_expires, col_priority, col_contact, col_tupleid, col_etag, col_published_id } ; if (!use_db) return 0; if (pa_dbf.use_table(db, presentity_contact_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.query (db, keys, ops, k_vals, result_cols, 1, sizeof(result_cols) / sizeof(db_key_t), 0, &res) < 0) { ERR("Error while querying DB\n"); return -1; } if (!res) return 0; /* ? */ for (i = 0; i < res->n; i++) { presence_tuple_t *tuple = NULL; db_row_t *row = &res->rows[i]; db_val_t *row_vals = ROW_VALUES(row); str contact = STR_NULL; basic_tuple_status_t basic = presence_tuple_undefined_status; str id = STR_NULL; str etag = STR_NULL; str published_id = STR_NULL; time_t expires = 0; double priority = row_vals[2].val.double_val; #define get_str_val(i,dst) do{if(!row_vals[i].nul){dst.s=(char*)row_vals[i].val.string_val;dst.len=strlen(dst.s);}}while(0) #define get_int_val(i,dst) do{if(!row_vals[i].nul){dst=row_vals[i].val.int_val;}}while(0) #define get_time_val(i,dst) do{if(!row_vals[i].nul){dst=row_vals[i].val.time_val;}}while(0) get_int_val(0, basic); get_time_val(1, expires); get_str_val(3, contact); get_str_val(4, id); get_str_val(5, etag); get_str_val(6, published_id); #undef get_str_val #undef get_time_val r = new_presence_tuple(&contact, expires, &tuple, 1, &id, &published_id, &etag) | r; if (tuple) { tuple->data.status.basic = basic; LOG(L_DBG, "read tuple %.*s\n", id.len, id.s); tuple->data.priority = priority; db_read_tuple_notes(_p, tuple, db); db_read_tuple_extensions(_p, tuple, db); add_presence_tuple_no_wb(_p, tuple); } } pa_dbf.free_result(db, res); return r; } static int set_tuple_db_data(presentity_t *_p, presence_tuple_t *tuple, db_key_t *cols, db_val_t *vals, int *col_cnt) { int n_updates = 0; cols[n_updates] = col_tupleid; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = tuple->data.id; n_updates++; cols[n_updates] = col_pres_id; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = _p->pres_id; n_updates++; cols[n_updates] = col_basic; vals[n_updates].type = DB_INT; vals[n_updates].nul = 0; vals[n_updates].val.int_val = tuple->data.status.basic; n_updates++; cols[n_updates] = col_contact; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = tuple->data.contact; n_updates++; cols[n_updates] = col_etag; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = tuple->etag; n_updates++; cols[n_updates] = col_published_id; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = tuple->published_id; n_updates++; if (tuple->data.priority != 0.0) { cols[n_updates] = col_priority; vals[n_updates].type = DB_DOUBLE; vals[n_updates].nul = 0; vals[n_updates].val.double_val = tuple->data.priority; n_updates++; } if (tuple->expires != 0) { cols[n_updates] = col_expires; vals[n_updates].type = DB_DATETIME; vals[n_updates].nul = 0; vals[n_updates].val.time_val = tuple->expires; n_updates++; } *col_cnt = n_updates; return 0; } static int db_add_presence_tuple(presentity_t *_p, presence_tuple_t *t) { db_key_t query_cols[20]; db_val_t query_vals[20]; int n_query_cols = 0; int res; if (!use_db) return 0; if (!t->is_published) return 0; /* store only published tuples */ if (set_tuple_db_data(_p, t, query_cols, query_vals, &n_query_cols) != 0) { return -1; } if (pa_dbf.use_table(pa_db, presentity_contact_table) < 0) { LOG(L_ERR, "db_add_presence_tuple: Error in use_table\n"); return -1; } if (pa_dbf.insert(pa_db, query_cols, query_vals, n_query_cols) < 0) { LOG(L_ERR, "db_add_presence_tuple: Can't insert record\n"); return -1; } res = 0; if (db_add_tuple_notes(_p, t) < 0) { res = -2; ERR("can't add tuple notes into DB\n"); } if (db_add_tuple_extensions(_p, t) < 0) { res = -3; ERR("can't add tuple extensions into DB\n"); } return res; } static int db_remove_presence_tuple(presentity_t *_p, presence_tuple_t *t) { db_key_t keys[] = { col_pres_id, col_tupleid }; db_op_t ops[] = { OP_EQ, OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = _p->pres_id } }, { DB_STR, 0, { .str_val = t->data.id } } }; if (!use_db) return 0; if (!t->is_published) return 0; /* store only published tuples */ db_remove_tuple_notes(_p, t); db_remove_tuple_extensions(_p, t); if (pa_dbf.use_table(pa_db, presentity_contact_table) < 0) { LOG(L_ERR, "db_remove_presence_tuple: Error in use_table\n"); return -1; } if (pa_dbf.delete(pa_db, keys, ops, k_vals, 2) < 0) { LOG(L_ERR, "db_remove_presence_tuple: Can't delete record\n"); return -1; } return 0; } int db_update_presence_tuple(presentity_t *_p, presence_tuple_t *t, int update_notes_and_ext) { db_key_t keys[] = { col_pres_id, col_tupleid }; db_op_t ops[] = { OP_EQ, OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = _p->pres_id } }, { DB_STR, 0, { .str_val = t->data.id } } }; db_key_t query_cols[20]; db_val_t query_vals[20]; int n_query_cols = 0; if (!use_db) return 0; if (!t->is_published) return 0; /* store only published tuples */ if (set_tuple_db_data(_p, t, query_cols, query_vals, &n_query_cols) != 0) { return -1; } if (pa_dbf.use_table(pa_db, presentity_contact_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.update(pa_db, keys, ops, k_vals, query_cols, query_vals, 2, n_query_cols) < 0) { ERR("Can't update record\n"); return -1; } if (update_notes_and_ext) { db_update_tuple_notes(_p, t); db_update_tuple_extensions(_p, t); } return 0; } void add_presence_tuple_no_wb(presentity_t *_p, presence_tuple_t *_t) { DOUBLE_LINKED_LIST_ADD(_p->data.first_tuple, _p->data.last_tuple, (presence_tuple_info_t*)_t); } void add_presence_tuple(presentity_t *_p, presence_tuple_t *_t) { add_presence_tuple_no_wb(_p, _t); if (use_db) db_add_presence_tuple(_p, _t); } void remove_presence_tuple(presentity_t *_p, presence_tuple_t *_t) { DOUBLE_LINKED_LIST_REMOVE(_p->data.first_tuple, _p->data.last_tuple, (presence_tuple_info_t*)_t); if (use_db) db_remove_presence_tuple(_p, _t); } /* * Free all memory associated with a presence_tuple */ void free_presence_tuple(presence_tuple_t * _t) { if (_t) { free_tuple_notes(_t); free_tuple_extensions(_t); if (_t->is_published) { /* Warning: not-published tuples have contact allocated * together with other data => contact can't change! */ str_free_content(&_t->data.contact); } mem_free(_t); } } /* * Find a presence_tuple for contact _contact on presentity _p */ int find_registered_presence_tuple(str* _contact, presentity_t *_p, presence_tuple_t ** _t) { presence_tuple_t *tuple; if (!_contact || !_contact->len || !_p || !_t) { paerrno = PA_INTERNAL_ERROR; LOG(L_ERR, "find_presence_tuple(): Invalid parameter value\n"); return -1; } tuple = get_first_tuple(_p); while (tuple) { /* only contacts from usrloc should have unique contact - published * may be more times !!! */ if (!tuple->is_published) { if (str_nocase_equals(&tuple->data.contact, _contact) == 0) { *_t = tuple; return 0; } } tuple = (presence_tuple_t*)tuple->data.next; } return 1; } /* * Find a presence_tuple on presentity _p */ int find_presence_tuple_id(str* id, presentity_t *_p, presence_tuple_t ** _t) { presence_tuple_t *tuple; if (!id || !id->len || !_p || !_t) { paerrno = PA_INTERNAL_ERROR; LOG(L_ERR, "find_presence_tuple_id(): Invalid parameter value\n"); return -1; } tuple = get_first_tuple(_p); while (tuple) { if (str_case_equals(&tuple->data.id, id) == 0) { *_t = tuple; return 0; } tuple = (presence_tuple_t*)tuple->data.next; } return 1; } presence_tuple_t *find_published_tuple(presentity_t *presentity, str *etag, str *id) { presence_tuple_t *tuple = get_first_tuple(presentity); while (tuple) { if (str_case_equals(&tuple->etag, etag) == 0) { if (str_case_equals(&tuple->published_id, id) == 0) return tuple; } tuple = get_next_tuple(tuple); } return NULL; } static inline void dup_tuple_notes(presence_tuple_t *dst, presence_tuple_info_t *src) { presence_note_t *n, *nn; n = src->first_note; while (n) { nn = create_presence_note(&n->value, &n->lang); if (nn) add_tuple_note_no_wb(dst, nn); n = n->next; } } static inline void dup_tuple_extensions(presence_tuple_t *dst, presence_tuple_info_t *src) { extension_element_t *e, *ne; e = src->first_unknown_element; while (e) { ne = create_extension_element(&e->element); if (ne) add_tuple_extension_no_wb(dst, ne, 0); e = e->next; } /* add new extensions for tuple status */ e = src->status.first_unknown_element; while (e) { ne = create_extension_element(&e->element); if (ne) add_tuple_extension_no_wb(dst, ne, 1); e = e->next; } } presence_tuple_t *presence_tuple_info2pa(presence_tuple_info_t *i, str *etag, time_t expires) { presence_tuple_t *t = NULL; int res; /* ID for the tuple is newly generated ! */ res = new_presence_tuple(&i->contact, expires, &t, 1, NULL, &i->id, etag); if (res != 0) return NULL; t->data.priority = i->priority; t->data.status.basic = i->status.basic; /* add notes for tuple */ dup_tuple_notes(t, i); /* add all extension elements */ dup_tuple_extensions(t, i); return t; } void update_tuple(presentity_t *p, presence_tuple_t *t, presence_tuple_info_t *i, time_t expires) { t->expires = expires; t->data.priority = i->priority; t->data.status.basic = i->status.basic; str_free_content(&t->data.contact); str_dup(&t->data.contact, &i->contact); /* remove all old notes and extension elements for this tuple */ free_tuple_notes(t); free_tuple_extensions(t); /* add new notes and new extension elemens for tuple */ dup_tuple_notes(t, i); dup_tuple_extensions(t, i); if (use_db) db_update_presence_tuple(p, t, 1); } kamailio-4.0.4/obsolete/pa/ptime.h0000644000000000000000000000230612223032460015501 0ustar rootroot/* * Presence Agent, time handling * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PTIME_H #define PTIME_H #include extern time_t act_time; /* * Get actual time */ void get_act_time(void); #endif /* PTIME_H */ kamailio-4.0.4/obsolete/pa/pdomain.h0000644000000000000000000000573712223032460016025 0ustar rootroot/* * Presence Agent, domain support * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2003-03-11 converted to the new locking scheme: locking.h (andrei) */ #ifndef PDOMAIN_H #define PDOMAIN_H #include "hslot.h" #include "presentity.h" #include "../../locking.h" #include "../../str.h" typedef int (*register_watcher_t)(str* _f, str* _t, void* _cb, void* _data); typedef int (*unregister_watcher_t)(str* _f, str* _t, void* _cb, void* _data); typedef struct pdomain { str* name; int size; /* Hash table size */ struct presentity* first; /* First presentity in the domain */ struct presentity* last; /* Last presentity in the domain */ struct hslot* table; /* Hash table for fast lookup */ gen_lock_t lock; /* Lock for the domain */ int users; /* Number of registered presentities */ int expired; /* Number of expired presentities */ register_watcher_t reg; /* Register watcher function */ unregister_watcher_t unreg; /* Unregister watcher function */ } pdomain_t; /* * Create a new domain structure * _n is pointer to str representing * name of the domain, the string is * not copied, it should point to str * structure stored in domain list * _s is hash table size */ int new_pdomain(str* _n, int _s, pdomain_t** _d, register_watcher_t _reg, unregister_watcher_t _unreg); /* * Free all memory allocated for * the domain */ void free_pdomain(pdomain_t* _d); /* * Timer handler for given domain */ int timer_pdomain(pdomain_t* _d); /* * Get lock */ void lock_pdomain(pdomain_t* _d); /* * Release lock */ void unlock_pdomain(pdomain_t* _d); /* * Find a presentity in domain acording to UID (!) */ int find_presentity_uid(pdomain_t* _d, str* uid, struct presentity** _p); /* * Add a presentity to domain */ void add_presentity(pdomain_t* _d, struct presentity* _p); /* * Remove a presentity from domain */ void remove_presentity(pdomain_t* _d, struct presentity* _p); #endif /* PDOMAIN_H */ kamailio-4.0.4/obsolete/pa/doc/0000755000000000000000000000000012223032460014756 5ustar rootrootkamailio-4.0.4/obsolete/pa/doc/pa.xml0000644000000000000000000000265412223032460016107 0ustar rootroot ]>
PA module Authors Vaclav Kubart Iptel Current maintainer. Nowadays module maintainer. vaclav.kubart@iptel.org Jamey Hicks Hewlett-Packard Company jamey.hicks@hp.com Jan Janak FhG FOKUS jan@iptel.org &base;
kamailio-4.0.4/obsolete/pa/doc/functions.xml0000644000000000000000000001203312223032460017507 0ustar rootroot
Functions handle_subscription(domain) This function processes SUBSCRIBE requests to "presence" and "presence.winfo" (see watcherinfo_notify parameter) events. Meaning of the parameters is as follows: domain - This can be either "registrar" or "jabber". <function>handle_subscription</function> usage ... handle_subscription("registrar"); ... handle_publish(domain) Processes PUBLISH request and generates response to it. target_online(domain) This function returns 1 if the target (using get_to_uid on current message) is online, -1 otherwise. check_subscription_status(status) This function returns 1 if last subscription is in status given by parameter, -1 otherwise. The status can have values: pending Subscription was created but no status information will be sent to the watcher. active Subscription was established and all presence information will be sent to the watcher. terminated Subscription was terminated. store_winfo(domain) This function stores data about currently created subscription into database for later dumping in the form of watcherinfo notification. It should be called after handle_subscription during processing SUBSCRIBE request. dump_stored_winfo(domain, event_package) This function tries to send stored watcherinfo data in the context of existing watcherinfo subscription. It uses get_to_uid as target for notification. If there is no watcherinfo subscription to such presentity, no information is sent. If the client responds with 2xx on generated NOTIFY request, the stored information is removed from database. <function>target_online</function>, <function>store_winfo</function>, <function>dump_stored_winfo</function> usage ... if (method=="SUBSCRIBE") { if (!t_newtran()) { sl_reply_error(); break; }; if (handle_subscription("registrar")) {; # uses uid from AVP (get_to_uid) if (@msg.event=~"presence\.winfo") { log(1, "subscription to watcherinfo\n"); dump_stored_winfo("registrar", "presence"); log(1, "OFFLINE AUTH: watcherinfo dumped\n"); } else { if ((@msg.event=~"presence") && (check_subscription_status("pending"))) { log(1, "pending subscription to presence\n"); # if offline user and new "pending" subscription if (!target_online("registrar") && (@to.tag=="")) { log(1, "OFFLINE AUTH: storing for offline user\n"); store_winfo("registrar"); } } } } break; }; ... authorize_message(filename) This function reads and processes IM authorization data and returns 1 if message can be accepted by destination user. If there is no IM authorization document on given XCAP server, it returns 1 too (MESSAGE is enabled by default). If the sender is blocked by recipient rules, this function returns -1. For more details about authorization documents see . Filename parameter specifies the name of file on XCAP server which contains message authorization rules. It is im-rules.xml by default. <function>authorize_message</function> usage ... if (method=="MESSAGE") { if (authorize_message("im-rules.xml")) { if (!lookup("location")) { sl_reply("404", "Not Found"); break; }; if (!t_relay()) { sl_reply_error(); }; } else { sl_reply("403", "Forbidden"); } break; } ...
kamailio-4.0.4/obsolete/pa/doc/pa_db_src.xml0000644000000000000000000000142612223032460017417 0ustar rootroot PA kamailio-4.0.4/obsolete/pa/doc/xcap.xml0000644000000000000000000001503712223032460016441 0ustar rootroot
XCAP authorization
Presence authorization documents Presence authorization documents are described in and they are not fully supported now. We ignore sphere, transformations and date and time conditions. [Are there any clients supporting this?]. presence authorization document This document has two rules: one named blacklist disabling subscriptions from user smith and one named whitelist enabling subscriptions from all other users from domain test-domain.com. (It doesn't depend on names of rules but on actions for them.) sip:smith@test-domain.com block allow ]]>
Presence authorization document URI Authorization documents are read for presentities according to their presentity URIs (presentity URI is URI in To header of SUBSCRIBE request). Resulting XCAP URI with authorization document is: <xcap-root>/pres-rules/users/<username>/presence-rules.xml, where <xcap-root> is set in configuration of XCAP module and <username> is UUID (unique user identification used in SER everywhere) discovered from presentity URI. If you need to use other filename than default presence-rules.xml, you can redefine it by parameter pres_rules_file. Problem can be, that pres_rules_file is specified as module parameter and thus common for all users. presence authorization document uri For example for presentity sip:joe@someorg.org with UUID joe and xcap-root http://localhost/xcap-root will be the URI for the authorization document http://localhost/xcap-root/pres-rules/users/joe/presence-rules.xml
Message authorization documents Message authorization documents are described in . We ignore sphere, transformations and date and time conditions like in the case of presence authorization. message authorization document This document has one rule named blacklist disabling messages sent from users jan@test-domain.com and jana@test-domain.com. sip:jana@test-domain.com sip:jan@test-domain.com block ]]>
Message authorization document URI Authorization documents are read for recipients according to To URIs. Resulting URI for XCAP query will be: <xcap-root>/im-rules/users/<username>/im-rules.xml where <xcap-root> is set in configuration of XCAP module, im-rules.xml is default filename for message authorization rules (may be redefined in authorize_message call parameter) and <username> is UUID discovered from To URI. message authorization document uri For example for recipient sip:smith@someorg.org with UUID smith and xcap-root http://localhost/xcap-root will be the URI for the authorization document http://localhost/xcap-root/im-rules/users/smith/im-rules.xml
Disadvantages One of the worst disadvantages of XCAP authorization is slowness of XCAP queries compared to - for example - data stored in local database. This is the reason for caching XCAP queries and responses, but there is a problem - how to detect changes in data stored on XCAP server. One of possible solutions is to implement client for XCAP change notifications described in and (planned in future versions).
Standard incompliances XCAP authorization support is not finished yet, there are some standard incompliances now: ignored sphere ignored date and time conditions ignored transformations Presentity URI is taken from To header field instead of AOR because AOR in consequent SUBSCRIBE requests is set to "contact to server URI" instead of user's URI (RFCs contradict themselves). We use UUID instead of whole presentity URI as <username> due to possibility of using the same authorization document for more user's aliases (TODO: control this by module parameter?). According to current presence authorization draft should presence server look for ALL authorization documents within user's directory on XCAP server, but listing documents on XCAP server is NOT defined. This will be partialy solved in future by possibility of having list of authorization documents as user's attributes.
kamailio-4.0.4/obsolete/pa/doc/pa_incl.xml0000644000000000000000000000041712223032460017107 0ustar rootroot ]>
PA module &base;
kamailio-4.0.4/obsolete/pa/doc/pa_base.xml0000644000000000000000000002377012223032460017103 0ustar rootroot
Overview This module implements a presence server, i.e. entity that receives SUBSCRIBE requests and sends NOTIFY when presence status of a user changes and allows user to use PUBLISH request for publishing presence status information.
Features handle SUBSCRIBE requests handle PUBLISH requests XCAP authorization of subscriptions failover - all subscription's data including SIP dialogs can be stored into database and reloaded on startup offline watcher info - watchers can be stored into database when presentity is offline and sent in watcher info notification when presentity subscribes to its watcherinfo again
Presence status User's presence status is hold internaly (in memory); it can be taken from: registrar - online/offline information with contact published information by user (see ) to be done: from reg events subscriptions to be done: from subscriptions to users TODO: cache mode needed by large setups, when the presence status will be in memory only cached, not fully stored (limited size of cache, possibility to switch it off). [status: design in progress, coding will start after Ottendorf will be branched]
Supported document formats Supported document formats in PUBLISH: PIDF - see CPIM-PIDF (last version which differs from PIDF only in namespaces and MIME type name) PIDF extensions (like RPID ) Supported document formats in NOTIFY: PIDF - see CPIM-PIDF (last version which differs from PIDF only in namespaces and MIME type name) LPIDF XPIDF (MS variant used by Windows Messenger 4.7) PIDF extensions (like RPID )
Dependencies Modules tm dialog usrloc optionaly database module (mysql, ...) optionaly xcap module for XCAP authorization Libraries libxml2 - external library for parsing XML documents libcds (internal) libxcap (internal) - XCAP queries (authorization) libpresence (internal) - used for internal subscriptions from RLS
Behaviour Short notes to be remembered... There are two possibilities how to get authorization rules from XCAP when creating new presentity (controled by module parameter async_auth_queries): asynchronously - XCAP is not queried when processing SUBSCRIBE request which created presentity what results into 202 response and delayed authorization of such first watcher. First NOTIFY in this case will carry no information and "pending" status; later (as soon as will be authorization rules got from XCAP) will be generated other NOTIFY with correct authorization status. synchronously - XCAP is queried for presence rules immediately, but this slows down processing of such SUBSCRIBE request and may bring problems under heavy load so it is not recommended. In this case first NOTIFY contains correct subscription status and data. Other SUBSCRIBE requests than the first one which creates the presentity already have authorization document which is stored within presentity's data and don't need to wait for XCAP. But this can consume lots of memory [TODO: this will be solved by intelligent document caching within XCAP module only]. Re-authorization is done by timer - authorization document queries for each presentity are sent time after time and all presentity's watchers are reauthorized. This is due to missing implementation of notification mechanism for XCAP server data changes (might be implemented in the future, but XCAP server able to do it is needed!). One of reasons which lead to timer-based reauthorization was that previous method, when reauthorization was done only when watcher is resubscribing, was not acceptable for users which want to propagate changes in authorization document immediately. When a non-2xx final response to a NOTIFY comes, the subscription is destroyed (really needed under high load). It is possible to say that 408 responses are ignored (see parameter ignore_408_on_notify) but this should be used for testing only.
State aggregation PA modules does state aggregation from multiple sources: registrar Information from registrar is taken by callbacks to usrloc module. Each registered contact is taken as a standalone tuple. Status may be open or closed, contact for the tuple is taken from contact registration. Priority of such tuples is taken from parameter default_priority_percentage. It is recommended to have this value lower than priority of tuples published by PUBLISH. You can ommit this source by setting use_callbacks to 0. published state State published by clients using PUBLISH request according to RFC 3903. There can be more published tuples, each of them is identified by its id in PIDF document. This id is used for tuple identification in re-publications, but it is NOT used as id in NOTIFYs sent from PA. Instead of it is used newly generated tuple id because this id must be unique (across all presentity's UACs). It is NOT possible to replace existing tuple with publishing information for tuple with the same id - this would be against RFC 3903! When publishing status it is only possible to have influence on tuples published with the same entity tag (see Sip-If-Match and SIP-ETag header fields). PA understoods only basic PIDF, but it can handle PIDF extensions like RPID too. PIDF extensions are hold as whole XML elements without knowing about their definition and thus publishing client is responsible for correct content of them. PA ignores "mustUnderstand" attribute (see ). [Are there any problems with it?] You can control this source by PUBLISH request handling in config script (function handle_publish).
kamailio-4.0.4/obsolete/pa/doc/auth.xml0000644000000000000000000000174412223032460016447 0ustar rootroot
Authorization implementation Authorization is one of key elements within presence server. PA module supports only XCAP authorization now. Internal subscriptions may use default settings only (XCAP root, XCAP parameters) which is insufficient! Can we use separate route in config to set this? Or manipulate directly with XCAP module data members/functions for setting them? We will use delayed authorization from now. When is the presentity created, authorization rules are empty and they are asynchronously requested from XCAP. As soon as they arrive all watchers for presentity are reauthorized. Authorization rules are checked after some amount of time according to settings (this eliminates need of notification changes from XCAP for now).
kamailio-4.0.4/obsolete/pa/doc/Makefile0000644000000000000000000000012312223032460016412 0ustar rootrootdocs = pa.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/pa/doc/params.xml0000644000000000000000000002013012223032460016757 0ustar rootroot
Parameters default_expires (integer) Default expiration value to be used when the client doesn't supply one (in seconds). It is used for both subscriptions and publications. Default value is 3600. max_subscription_expiration (integer) Maximal subscription expiration value in seconds. Default value is 3600. max_publish_expiration (integer) Maximal expiration of presence status information published via PUBLISH in seconds. Default value is 3600. use_db (integer) If set to 1, PA module stores all subscription data into database and reloads them on startup. Requires db_url to be set. Default value is 1. db_url (integer) Database connection URL. It has to be specified if use_db or use_offline_winfo is set. Default value is empty. <varname>db_url</varname> settings ... modparam("pa", "use_db", 1) modparam("pa", "db_url", "mysql://ser:heslo@127.0.0.1:3306/ser") ... use_callbacks (integer) If set to 1 callbacks to registrar/jabber will be used, if set to 0 callbacks will not be used, thus it will work only with published information. Default value is 1. accept_internal_subscriptions (integer) If set to 1 PA module will accept internal subscriptions via Querry Status API otherwise not. Set this to 1 if you want RLS module to be using internal subscriptions to PA. You can't use presence B2B UA in this case! Default value is 0. Internal subscriptions are not handled in configuration script thus they use default values of parameters (like XCAP root settings for authorization, ...) watcherinfo_notify (integer) Set this to 1 if you want to enable subscriptions to "presence.winfo" events. If set to 0, watcherinfo subscriptions are denied. Default value is 1. use_offline_winfo (integer) Set this to 1 if you want to use offline watcher info notifications. In such case can be watcher information stored in database and later dumped to presentity in watcher info notification, requires db_url to be set. See functions for example. Default value is 0. offline_winfo_expiration (integer) Expiration time of stored "offline watcher information" in seconds. After this time is the information removed from database. Default value is 259200. offline_winfo_timer (integer) Number of seconds after which is triggered automatic cleanup of expired offline watcherinfo. Default value is 3600. auth (string) This variable specifies authorization type for presence watchers (event package is presence). Value can be one of: none All watchers are always authorized. This is not recommended because it ignores user's wish. implicit All watchers are always pending. This is not recommended because it means, that no presence data is sent to all watchers. xcap This type of authorization means, that authorization rules are read from XCAP server and processed . In this case the xcap module must be loaded. Default value is empty. In this case implict authorization is used with an error message. winfo_auth (string) This variable specifies authorization type for watcher info watchers (event package is presence.winfo). Value can be one of: none All watcher info watchers are always authorized. This is not recommended. implicit This value means, that presentity can subscribe to its own watcher info data and nobody else. This is done via comparing watcher's and presentity's uri. If these values equals, the subscription is allowed. In other cases is the subscription rejected. Default value is implict pres_rules_file Name of the file with presence rules on XCAP server. The filename is by default presence-rules.xml. It is common for all subscriptions/users which seems to be insufficient. It will be replaced by holding this information for each user separately in user attributes (TODO). ignore_408_on_notify If set to 1 the 408 response to NOTIFY is ignored, otherwise all 4xx responses including 408 destroys the subscription. Reason for this parameter is that some clients are not able to process two NOTIFY requests received shortly in sequence and unresponded NOTIFY resulting in generated 408 response destroyed subscription created by them. Default value is 0. timer_interval Interval in seconds when the timer runs and clears expired watchers/tuples and send NOTIFYs for changed presentities. default_priority_percentage Priority value used for tuples created by registration in percents. Default value is 0. auth_rules_refresh_time Time interval specifying amount of time between re-reading of authorization documents stored on XCAP server. In other words it means the time interval of subscription reauthorization. Default value is 300. async_auth_queries Set to 1 if you want to use asynchronous XCAP queries (recommended), 0 otherwise. Default value is 0. max_auth_requests_per_tick Maximum number of asynchronous XCAP requests done at once (within one second). Default value is 600.
kamailio-4.0.4/obsolete/pa/extension_elements.c0000644000000000000000000001523012223032460020266 0ustar rootroot#include "presentity.h" #include "extension_elements.h" #include "pa_mod.h" #include #include /* DB manipulation */ static int db_add_extension_element(presentity_t *p, pa_extension_element_t *n) { db_key_t cols[20]; db_val_t vals[20]; int n_updates = 0; if (!use_db) return 0; /* set data */ cols[n_updates] = col_dbid; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = n->dbid; n_updates++; cols[n_updates] = col_pres_id; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = p->pres_id; n_updates++; cols[n_updates] = col_etag; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = n->etag; n_updates++; cols[n_updates] = col_element; vals[n_updates].type = DB_BLOB; vals[n_updates].nul = 0; vals[n_updates].val.blob_val = n->data.element; n_updates++; cols[n_updates] = col_expires; vals[n_updates].type = DB_DATETIME; vals[n_updates].nul = 0; vals[n_updates].val.time_val = n->expires; n_updates++; /* run update */ if (pa_dbf.use_table(pa_db, extension_elements_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.insert(pa_db, cols, vals, n_updates) < 0) { ERR("Can't insert record\n"); return -1; } return 0; } static int db_remove_extension_element(presentity_t *p, pa_extension_element_t *n) { db_key_t keys[] = { col_pres_id, col_etag, col_dbid }; db_op_t ops[] = { OP_EQ, OP_EQ, OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = p->pres_id } }, { DB_STR, 0, { .str_val = n->etag } }, { DB_STR, 0, { .str_val = n->dbid } } }; if (!use_db) return 0; if (pa_dbf.use_table(pa_db, extension_elements_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.delete(pa_db, keys, ops, k_vals, 3) < 0) { ERR("Can't delete record\n"); return -1; } return 0; } int db_update_extension_element(presentity_t *p, pa_extension_element_t *n) { db_key_t cols[20]; db_val_t vals[20]; int n_updates = 0; db_key_t keys[] = { col_pres_id, col_etag, col_dbid }; db_op_t ops[] = { OP_EQ, OP_EQ, OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = p->pres_id } }, { DB_STR, 0, { .str_val = n->etag } }, { DB_STR, 0, { .str_val = n->dbid } } }; if (!use_db) return 0; cols[n_updates] = col_element; vals[n_updates].type = DB_BLOB; vals[n_updates].nul = 0; vals[n_updates].val.blob_val = n->data.element; n_updates++; cols[n_updates] = col_expires; vals[n_updates].type = DB_DATETIME; vals[n_updates].nul = 0; vals[n_updates].val.time_val = n->expires; n_updates++; if (pa_dbf.use_table(pa_db, extension_elements_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.update(pa_db, keys, ops, k_vals, cols, vals, 3, n_updates) < 0) { ERR("Can't update record\n"); return -1; } return 0; } int db_read_extension_elements(presentity_t *p, db_con_t* db) { db_key_t keys[] = { col_pres_id }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = p->pres_id } } }; int i; int r = 0; db_res_t *res = NULL; db_key_t result_cols[] = { col_dbid, col_etag, col_element, col_dbid, col_expires }; if (!use_db) return 0; if (pa_dbf.use_table(db, extension_elements_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.query (db, keys, ops, k_vals, result_cols, 1, sizeof(result_cols) / sizeof(db_key_t), 0, &res) < 0) { ERR("Error while querying presence extension_elements\n"); return -1; } if (!res) return 0; /* ? */ for (i = 0; i < res->n; i++) { pa_extension_element_t *n = NULL; db_row_t *row = &res->rows[i]; db_val_t *row_vals = ROW_VALUES(row); str dbid = STR_NULL; str etag = STR_NULL; str extension_element = STR_NULL; str id = STR_NULL; time_t expires = 0; #define get_str_val(i,dst) do{if(!row_vals[i].nul){dst.s=(char*)row_vals[i].val.string_val;dst.len=strlen(dst.s);}}while(0) #define get_blob_val(i,dst) do{if(!row_vals[i].nul){dst=row_vals[i].val.blob_val;}}while(0) #define get_time_val(i,dst) do{if(!row_vals[i].nul){dst=row_vals[i].val.time_val;}}while(0) get_str_val(0, dbid); get_str_val(1, etag); get_blob_val(2, extension_element); get_str_val(3, id); get_time_val(4, expires); #undef get_str_val #undef get_time_val n = create_pa_extension_element(&etag, &extension_element, expires, &dbid); if (n) DOUBLE_LINKED_LIST_ADD(p->data.first_unknown_element, p->data.last_unknown_element, (extension_element_t*)n); } pa_dbf.free_result(db, res); return r; } /* in memory presence extension_elements manipulation */ void add_extension_element(presentity_t *_p, pa_extension_element_t *n) { DOUBLE_LINKED_LIST_ADD(_p->data.first_unknown_element, _p->data.last_unknown_element, (extension_element_t*)n); if (use_db) db_add_extension_element(_p, n); } void remove_extension_element(presentity_t *_p, pa_extension_element_t *n) { DOUBLE_LINKED_LIST_REMOVE(_p->data.first_unknown_element, _p->data.last_unknown_element, (extension_element_t*)n); if (use_db) db_remove_extension_element(_p, n); free_pa_extension_element(n); } void free_pa_extension_element(pa_extension_element_t *n) { if (n) mem_free(n); } int remove_extension_elements(presentity_t *p, str *etag) { pa_extension_element_t *n, *nn; int found = 0; n = (pa_extension_element_t*)p->data.first_unknown_element; while (n) { nn = (pa_extension_element_t*)n->data.next; if (str_case_equals(&n->etag, etag) == 0) { /* remove this */ found++; remove_extension_element(p, n); } n = nn; } return found; } pa_extension_element_t *create_pa_extension_element(str *etag, str *extension_element, time_t expires, str *dbid) { pa_extension_element_t *pan; int size; if (!dbid) { ERR("invalid parameters\n"); return NULL; } size = sizeof(pa_extension_element_t); if (dbid) size += dbid->len; if (etag) size += etag->len; if (extension_element) size += extension_element->len; pan = (pa_extension_element_t*)mem_alloc(size); if (!pan) { ERR("can't allocate memory: %d\n", size); return pan; } memset(pan, 0, sizeof(*pan)); pan->expires = expires; pan->dbid.s = (char*)pan + sizeof(pa_extension_element_t); if (dbid) str_cpy(&pan->dbid, dbid); else pan->dbid.len = 0; pan->etag.s = after_str_ptr(&pan->dbid); str_cpy(&pan->etag, etag); pan->data.element.s = after_str_ptr(&pan->etag); str_cpy(&pan->data.element, extension_element); return pan; } pa_extension_element_t *extension_element2pa(extension_element_t *n, str *etag, time_t expires) { dbid_t id; str s; generate_dbid(id); s.len = dbid_strlen(id); s.s = dbid_strptr(id); return create_pa_extension_element(etag, &n->element, expires, &s); } kamailio-4.0.4/obsolete/pa/hslot.h0000644000000000000000000000354012223032460015515 0ustar rootroot/* * Presence Agent, hash table * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef HSLOT_H #define HSLOT_H #include "presentity.h" #include "pdomain.h" struct presentity; struct pdomain; typedef struct hslot { int n; /* Number of elements in the collision slot */ struct presentity* first; /* First element in the list */ struct pdomain* d; /* Domain we belong to */ } hslot_t; /* * Initialize slot structure */ void init_slot(struct pdomain* _d, hslot_t* _s); /* * Deinitialize given slot structure */ void deinit_slot(hslot_t* _s); /* * Add an element to slot linked list */ void slot_add(hslot_t* _s, struct presentity* _p, struct presentity** _f, struct presentity** _l); /* * Remove an element from slot linked list */ void slot_rem(hslot_t* _s, struct presentity* _p, struct presentity** _f, struct presentity** _l); #endif /* HSLOT_H */ kamailio-4.0.4/obsolete/pa/tuple.h0000644000000000000000000000322012223032460015510 0ustar rootroot#ifndef __TUPLE_H #define __TUPLE_H /* PA tuple functions (data structures are defined in presentity.h) */ #include "presentity.h" /* Create a new presence_tuple */ int new_presence_tuple(str* _contact, time_t expires, presence_tuple_t ** _t, int is_published, str *id, str *published_id, str *etag); /* add presence tuple to presentity and to database */ void add_presence_tuple(presentity_t *_p, presence_tuple_t *_t); /* Remove tuple from presentity and from database too */ void remove_presence_tuple(presentity_t *_p, presence_tuple_t *_t); /* Free all memory associated with a presence_tuple */ void free_presence_tuple(presence_tuple_t * _t); /* Find a tuple for contact _contact on presentity _p - only registered contacts ! */ int find_registered_presence_tuple(str* _contact, presentity_t *_p, presence_tuple_t ** _t); /* Find tuple with given id */ int find_presence_tuple_id(str* id, presentity_t *_p, presence_tuple_t ** _t); /* Find published tuple with given ID (ID used for publication, not tuple ID!) */ presence_tuple_t *find_published_tuple(presentity_t *presentity, str *etag, str *id); /** Function reads all tuples from DB for given presentity */ int db_read_tuples(presentity_t *_p, db_con_t* db); /* update tuple status in database */ int db_update_presence_tuple(presentity_t *_p, presence_tuple_t *t, int update_notes_and_ext); /* creates new tuple from given information (needed for publishing */ presence_tuple_t *presence_tuple_info2pa(presence_tuple_info_t *i, str *etag, time_t expires); /* updates published information */ void update_tuple(presentity_t *p, presence_tuple_t *t, presence_tuple_info_t *i, time_t expires); #endif kamailio-4.0.4/obsolete/pa/README0000644000000000000000000001162512223032460015076 0ustar rootroot pa Module Jan Janak FhG FOKUS Edited by Jan Janak Jamey Hicks Copyright © 2003 FhG FOKUS _________________________________________________________ Table of Contents 1. User's Guide 1.1. Overview 1.2. Dependencies 1.2.1. SER Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. default_expires (integer) 1.3.2. default_priority_percentage (integer) 1.3.3. db_url (string) 1.3.4. pa_domain (string) 1.4. Exported Functions 1.4.1. handle_subscription(domain) 1.4.2. handle_publish(domain) 1.4.3. pa_handle_registration(domain) 2. Developer's Guide 3. Frequently Asked Questions List of Examples 1-1. Set default_expires parameter 1-2. handle_subscription usage _________________________________________________________ Chapter 1. User's Guide 1.1. Overview This module implements a presence server, i.e. entity that receives SUBSCRIBE messages and sends NOTIFY when presence status of a user changes. Currently the presence server can be connected to registrar and jabber module so SIP users can see presence of jabber users. This module also acts as a presence aggregator, receiving PUBLISH messages to update extended status and generating NOTIFY messages. This module currently cannot work as a B2BUA, i.e. it cannot subscribe to presence of users using SIP. We are currently working on that feature. _________________________________________________________ 1.2. Dependencies 1.2.1. SER Modules The following modules must be loaded before this module: * No dependencies on other SER modules. _________________________________________________________ 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running SER with this module loaded: * None. _________________________________________________________ 1.3. Exported Parameters 1.3.1. default_expires (integer) Default expires value to be used when the client doesn't supply one (in seconds). Default value is "3600". Example 1-1. Set default_expires parameter ... modparam("pa", "default_expires", 3600) ... 1.3.2. default_priority_percentage (integer) Default priority value to be used when not included in a PUBLISH message, expressed as a percentage. Default value is "50". Example 1-2. Set default_expires parameter ... modparam("pa", "default_expires", 50) ... 1.3.3. db_url (string) URL of the database containing presence-agent data. Example 1-3. Set db_url parameter ... modparam("pa", "db_url", "mysql:ser:heslo@localhost/ser") ... 1.3.4. pa_domain (string) Hostname of xcap server associated with presence agent. Example 1-4. Set default_expires parameter ... modparam("pa", "pa_domain", "ser.example.com") ... _________________________________________________________ 1.4. Exported Functions 1.4.1. handle_subscription(domain) This function processes SUBSCRIBE and generates NOTIFY. Meaning of the parameters is as follows: * domain - This can be either "registrar" or "jabber". Example 1-5. handle_subscription usage ... if (method=="SUBSCRIBE") { if (!t_newtran()) { log(1, "newtran error\n"); sl_reply_error(); }; handle_subscription("registrar"); break; }; ... 1.4.2. handle_publish(domain) This function processes PUBLISH and generates NOTIFY. Meaning of the parameters is as follows: * domain - This can be either "registrar" or "jabber". Example 1-6. handle_publish usage ... if (method=="PUBLISH") { if (!t_newtran()) { log(1, "newtran error\n"); sl_reply_error(); }; handle_publish("registrar"); break; }; ... 1.4.3. pa_handle_registration(domain) This function is no longer used. _________________________________________________________ Chapter 2. Developer's Guide The module does not provide any sort of API to use in other SER modules. _________________________________________________________ Chapter 3. Frequently Asked Questions 3.1. Where can I find more about SER? 3.2. Where can I post a question about this module? 3.3. How can I report a bug? 3.1. Where can I find more about SER? Take a look at http://iptel.org/ser. 3.2. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * http://mail.iptel.org/mailman/listinfo/serusers * http://mail.iptel.org/mailman/listinfo/serdev E-mails regarding any stable version should be sent to and e-mail regarding development versions or CVS snapshots should be send to . 3.3. How can I report a bug? Please follow the guidelines provided at: http://iptel.org/ser/bugs kamailio-4.0.4/obsolete/pa/pres_notes.h0000644000000000000000000000113512223032460016543 0ustar rootroot#ifndef __PA_PRES_NOTES_H #define __PA_PRES_NOTES_H #include "presentity.h" int db_update_pres_note(presentity_t *p, pa_presence_note_t *n); void add_pres_note(presentity_t *_p, pa_presence_note_t *n); void free_pres_note(pa_presence_note_t *n); void remove_pres_note(presentity_t *_p, pa_presence_note_t *n); int remove_pres_notes(presentity_t *p, str *etag); pa_presence_note_t *create_pres_note(str *etag, str *note, str *lang, time_t expires, str *dbid); int db_read_notes(presentity_t *p, db_con_t* db); pa_presence_note_t *presence_note2pa(presence_note_t *n, str *etag, time_t expires); #endif kamailio-4.0.4/obsolete/pa/rpc.h0000644000000000000000000000220212223032460015142 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _PA_RPC_H #define _PA_RPC_H #include "../../rpc.h" extern rpc_export_t pa_rpc_methods[]; #endif /* _PA_RPC_H */ kamailio-4.0.4/obsolete/pa/auth.c0000644000000000000000000000466612223032460015332 0ustar rootroot#include "auth.h" #include "../../parser/parse_event.h" #include "pa_mod.h" #include #include #include /* Authorization */ static watcher_status_t xcap_authorize(presentity_t *p, str *w_uri) { sub_handling_t sh; if (!p->authorization_info) { /* DBG("got empty set of authorization rules for %.*s\n", p->uri.len, ZSW(p->uri.s)); */ return WS_PENDING; } /* process rules for given watcher's uri (w_uri) */ sh = sub_handling_confirm; get_pres_rules_action(p->authorization_info, w_uri, &sh); switch (sh) { case sub_handling_block: DBG("XCAP AUTH: block\n"); return WS_REJECTED; case sub_handling_confirm: DBG("XCAP AUTH: confirm\n"); return WS_PENDING; case sub_handling_polite_block: DBG("XCAP AUTH: polite block\n"); return WS_REJECTED; case sub_handling_allow: DBG("XCAP AUTH: allow\n"); return WS_ACTIVE; } return WS_PENDING; } static watcher_status_t winfo_implicit_auth(presentity_t *p, watcher_t *w) { /* implicit authorization rules for watcher info */ /*str_t p_user, w_user; if (get_user_from_uri(&p->uri, p_user) != 0) return WS_REJECTED; if (get_user_from_uri(&w->uri, w_user) != 0) return WS_REJECTED;*/ if (str_case_equals(&p->data.uri, &w->uri) == 0) { DBG("winfo_implicit_auth(%.*s): enabled for %.*s\n", FMT_STR(p->data.uri), FMT_STR(w->uri)); return WS_ACTIVE; } else { DBG("winfo_implicit_auth(%.*s): disabled for %.*s\n", FMT_STR(p->data.uri), FMT_STR(w->uri)); return WS_REJECTED; } } watcher_status_t authorize_watcher(presentity_t *p, watcher_t *w) { if (w->event_package == EVENT_PRESENCE_WINFO) { switch (winfo_auth_params.type) { case auth_none: return WS_ACTIVE; case auth_implicit: return winfo_implicit_auth(p, w); case auth_xcap: ERROR_LOG("XCAP authorization for winfo is not implemented! " "Using \'implicit\' auth.\n"); return winfo_implicit_auth(p, w); } } else { switch (pa_auth_params.type) { case auth_none: return WS_ACTIVE; case auth_implicit: return WS_PENDING; case auth_xcap: return xcap_authorize(p, &w->uri); } } return WS_PENDING; } watcher_status_t authorize_internal_watcher(presentity_t *p, internal_pa_subscription_t *is) { switch (pa_auth_params.type) { case auth_none: return WS_ACTIVE; case auth_implicit: return WS_PENDING; case auth_xcap: return xcap_authorize(p, get_subscriber_id(is->subscription)); } return WS_PENDING; } kamailio-4.0.4/obsolete/pa/dlist.h0000644000000000000000000000354412223032460015507 0ustar rootroot/* * Presence Agent, domain list * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef DLIST_H #define DLIST_H #include "../../str.h" #include "pdomain.h" /* * List of all domains registered with usrloc */ typedef struct dlist { str name; /* Name of the domain */ pdomain_t* d; /* Payload */ struct dlist* next; /* Next element in the list */ } dlist_t; extern dlist_t* root; /* * Function registers a new domain with presence agent * if the domain exists, pointer to existing structure * will be returned, otherwise a new domain will be * created */ int register_pdomain(const char* _n, pdomain_t** _d); /* * Function finds existing domain with presence agent */ int find_pdomain(const char* _n, pdomain_t** _d); /* * Free all registered domains */ void free_all_pdomains(void); /* * Called from timer */ int timer_all_pdomains(void); #endif /* DLIST_H */ kamailio-4.0.4/obsolete/pa/reply.c0000644000000000000000000001447012223032460015516 0ustar rootroot/* * Presence Agent, reply building * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../../dprint.h" #include "../../data_lump_rpl.h" #include "reply.h" #include "paerrno.h" #include "pa_mod.h" #include #define MSG_200 "OK" #define MSG_400 "Bad Request" #define MSG_500 "Server Internal Error" #define MSG_403 "Forbidden" #define EI_PA_OK "No problem" #define EI_PA_PARSE_ERR "Error while parsing headers" #define EI_PA_CONTACT_MISS "Contact header field missing" #define EI_PA_FROM_MISS "From header field missing" #define EI_PA_EVENT_MISS "Event header field missing" #define EI_PA_EVENT_PARSE "Error while parsing Event header field" #define EI_PA_EXPIRES_PARSE "Error while parsing Expires header field" #define EI_PA_EVENT_UNSUPP "Unsupported event package" #define EI_PA_NO_MEMORY "No memory left on the server" #define EI_PA_TIMER_ERROR "Error while running timer" #define EI_PA_EXTRACT_USER "Cannot extract username from URI" #define EI_PA_CONT_PARSE "Error while parsing Contact" #define EI_PA_CONT_STAR "Start not allowed in Contact" #define EI_PA_FROM_ERROR "Error while parsing From" #define EI_PA_SMALL_BUFFER "Buffer too small on the server" #define EI_PA_UNSUPP_DOC "Unsupported document format" #define EI_PA_INTERNAL_ERROR "Internal Server Error" #define EI_PA_SUBSCRIPTION_REJECTED "Subscription rejected" str error_info[] = { {EI_PA_OK, sizeof(EI_PA_OK) - 1 }, {EI_PA_PARSE_ERR, sizeof(EI_PA_PARSE_ERR) - 1 }, {EI_PA_CONTACT_MISS, sizeof(EI_PA_CONTACT_MISS) - 1 }, {EI_PA_FROM_MISS, sizeof(EI_PA_FROM_MISS) - 1 }, {EI_PA_EVENT_MISS, sizeof(EI_PA_EVENT_MISS) - 1 }, {EI_PA_EVENT_PARSE, sizeof(EI_PA_EVENT_PARSE) - 1 }, {EI_PA_EXPIRES_PARSE, sizeof(EI_PA_EXPIRES_PARSE) - 1 }, {EI_PA_EVENT_UNSUPP, sizeof(EI_PA_EVENT_UNSUPP) - 1 }, {EI_PA_NO_MEMORY, sizeof(EI_PA_NO_MEMORY) - 1 }, {EI_PA_TIMER_ERROR, sizeof(EI_PA_TIMER_ERROR) - 1 }, {EI_PA_EXTRACT_USER, sizeof(EI_PA_EXTRACT_USER) - 1 }, {EI_PA_CONT_PARSE, sizeof(EI_PA_CONT_PARSE) - 1 }, {EI_PA_CONT_STAR, sizeof(EI_PA_CONT_STAR) - 1 }, {EI_PA_FROM_ERROR, sizeof(EI_PA_FROM_ERROR) - 1 }, {EI_PA_SMALL_BUFFER, sizeof(EI_PA_SMALL_BUFFER) - 1 }, {EI_PA_UNSUPP_DOC, sizeof(EI_PA_UNSUPP_DOC) - 1 }, {EI_PA_INTERNAL_ERROR, sizeof(EI_PA_INTERNAL_ERROR) - 1}, {EI_PA_SUBSCRIPTION_REJECTED, sizeof(EI_PA_SUBSCRIPTION_REJECTED) - 1} }; int codes[] = { 200, /* EI_PA_OK */ 400, /* EI_PA_PARSE_ERR */ 400, /* EI_PA_CONTACT_MISS */ 400, /* EI_PA_FROM_MISS */ 400, /* EI_PA_EVENT_MISS */ 400, /* EI_PA_EVENT_PARSE */ 400, /* EI_PA_EXPIRES_PARSE */ 500, /* EI_PA_EVENT_UNSUPP */ 500, /* EI_PA_NO_MEMORY */ 500, /* EI_PA_TIMER_ERROR */ 400, /* EI_PA_EXTRACT_USER */ 400, /* EI_PA_CONT_PARSE */ 400, /* EI_PA_CONT_STAR */ 400, /* EI_PA_FROM_ERROR */ 500, /* EI_PA_SMALL_BUFFER */ 500, /* EI_PA_UNSUPP_DOC */ 500, /* EI_PA_INTERNAL_ERROR */ 403 /* EI_PA_SUBSCRIPTION_REJECTED */ }; /* * Send a reply */ int send_reply(struct sip_msg* _m) { int code = 200; char* msg = MSG_200; /* makes gcc shut up */ /* code = codes[paerrno]; */ switch (paerrno) { case PA_OK: msg = MSG_200; code = 200; break; case PA_PARSE_ERR: msg = MSG_400; code = 400; break; case PA_FROM_MISS: msg = MSG_400; code = 400; break; case PA_EVENT_MISS: msg = "Unsupported event package"; code = 489; break; case PA_EVENT_PARSE: msg = MSG_400; code = 400; break; case PA_EXPIRES_PARSE: msg = MSG_400; code = 400; break; case PA_EVENT_UNSUPP: msg = "Unsupported event package"; code = 489; break; case PA_WRONG_ACCEPTS: msg = "Unsupported document format for given package"; code = 415; break; case PA_NO_MEMORY: msg = MSG_500; code = 500; break; case PA_TIMER_ERROR: msg = MSG_500; code = 500; break; case PA_EXTRACT_USER: msg = MSG_400; code = 400; break; case PA_FROM_ERR: msg = MSG_400; code = 400; break; case PA_TO_ERR: msg = MSG_400; code = 400; break; case PA_SMALL_BUFFER: msg = MSG_500; code = 500; break; case PA_UNSUPP_DOC: msg = "Unsupported document format"; code = 415; break; case PA_ACCEPT_PARSE: msg = MSG_400; code = 400; break; case PA_URI_PARSE: msg = MSG_400; code = 400; break; case PA_DIALOG_ERR: msg = MSG_500; code = 500; break; case PA_INTERNAL_ERROR: msg = MSG_500; code = 500; break; case PA_SUBSCRIPTION_REJECTED: msg = MSG_403; code = 403; break; case PA_NO_MATCHING_TUPLE: msg = "Conditional Request Failed"; code = 412; break; case PA_OK_WAITING_FOR_AUTH: msg = "Accepted"; code = 202; break; /* OK but waiting for auth -> should return 202 */ case PA_SUBSCRIPTION_NOT_EXISTS: msg = "Subscription does not exist"; code = 481; break; /* OK but waiting for auth -> should return 202 */ } if ((code >= 200) && (code < 300)) { /* add Contact header field into response */ str s; if (extract_server_contact(_m, &s, 0) == 0) { if (s.len > 0) { if (!add_lump_rpl(_m, s.s, s.len, LUMP_RPL_HDR)) { ERR("Can't add Contact header into the response\n"); if (s.s) mem_free(s.s); return -1; } } if (s.s) mem_free(s.s); } } if (tmb.t_reply(_m, code, msg) < 0) { ERR("Error while sending %d %s\n", code, msg); return -1; } else return 0; } kamailio-4.0.4/obsolete/pa/trace.h0000644000000000000000000000046012223032460015460 0ustar rootroot#ifndef __TRACE_H #define __TRACE_H #define DO_TRACE #ifdef DO_TRACE #include #include #include #define mem_alloc cds_malloc #define mem_free cds_free #define TRACE(...) TRACE_LOG("PA: " __VA_ARGS__) #else #define TRACE(args...) #endif #endif kamailio-4.0.4/obsolete/pa/presentity.h0000644000000000000000000001567012223032460016601 0ustar rootroot/* * Presence Agent, presentity structure and related functions * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2004 Jamey Hicks * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PRESENTITY_H #define PRESENTITY_H #include "../../str.h" #include "../../modules/tm/dlg.h" #include "watcher.h" #include "hslot.h" #include "trace.h" #include "pdomain.h" #include #include #include #include #include #include #include typedef struct presence_tuple { presence_tuple_info_t data; /* Contact is constant for non-published tuples and it is allocated * together with whole structure. For published tuples is contact * allocated separately in shared memory and can change. */ int is_published; /* 1 for published tuples - these are stored into DB */ str etag; /* etag for published tuples -> constant for tuple life */ time_t expires; /* tuple expires on ... */ str published_id; /* tuple id used for publish -> constant for tuple life */ } presence_tuple_t; typedef struct { presence_note_t data; str etag; /* published via this etag -> constant for note life */ time_t expires; /* note expires on ... */ str dbid; /* id for database ops - needed for removing expired notes -> constant for note life */ } pa_presence_note_t; typedef struct _pa_extension_element_t { extension_element_t data; str etag; /* published via this etag -> constant for structure life */ time_t expires; /* expires on ... */ str dbid; /* id for database ops - needed for removing expired -> constant for structure life */ } pa_extension_element_t; typedef struct { str user; str contact; basic_tuple_status_t state; } tuple_change_info_t; struct pdomain; typedef enum pflag { PFLAG_PRESENCE_CHANGED=1, PFLAG_WATCHERINFO_CHANGED=2 } pflag_t; typedef struct _internal_pa_subscription_t { struct _internal_pa_subscription_t *prev, *next; watcher_status_t status; qsa_subscription_t *subscription; /* msg_queue_t *dst; * str_t package; * str_t watcher_uri; */ } internal_pa_subscription_t; typedef struct presentity { /* URI of presentity - doesn't change for the presentity's life */ presentity_info_t data; str uuid; /* use after usrloc uuid-zation - callbacks are registered to this, - doesn't change for the presentity's life */ str pres_id; /* id of the record in the presentity table (generated!) */ int id_cntr; /* variable for generating watcher/tuple/... ids */ /* provisional data members */ int ref_cnt; /* reference counter - don't remove if > 1 */ pflag_t flags; struct pdomain *pdomain; struct presentity* next; /* Next presentity */ struct presentity* prev; /* Previous presentity in list */ struct hslot* slot; /* Hash table collision slot we belong to */ /* watchers/winfo watchers/internal watchers */ watcher_t *first_watcher, *last_watcher; /* List of watchers */ watcher_t *first_winfo_watcher, *last_winfo_watcher; /* Watchers subscribed to winfo */ internal_pa_subscription_t *first_qsa_subscription, *last_qsa_subscription; /* authorization data */ presence_rules_t *authorization_info; xcap_query_params_t xcap_params; /* doesn't change for the presentity's life (FIXME: rewrite) */ time_t auth_rules_refresh_time; msg_queue_t mq; /* message queue supplying direct usrloc callback processing */ /* data for internal subscriptions to presence * (reduces memory allocation count) */ qsa_subscription_data_t presence_subscription_data; qsa_subscription_t *presence_subscription; } presentity_t; /* shortcuts for PA structures walking (PA uses derived structures * instead of that defined in presence library because it needs * to store more information) */ #define get_first_tuple(p) ((presence_tuple_t*)(p->data.first_tuple)) #define get_next_tuple(t) ((presence_tuple_t*)(t->data.next)) #define get_first_note(p) ((pa_presence_note_t*)(p->data.first_note)) #define get_next_note(n) ((pa_presence_note_t*)(n->data.next)) #define get_first_extension(p) ((pa_extension_element_t*)(p->data.first_unknown_element)) #define get_next_extension(pe) ((pa_extension_element_t*)(pe->data.next)) /** Create a new presentity. */ int new_presentity(struct pdomain *pdomain, str* _uri, str *uid, xcap_query_params_t *xcap_params, presentity_t** _p); /** Free all memory associated with a presentity - use only in special * cases like freeing memory on module cleanup. Otherwise use * release_presentity instead. */ void free_presentity(presentity_t* _p); /* Free all memory associated with a presentity and remove it from DB */ void release_presentity(presentity_t* _p); /** Removes all data for presentity (tuples, watchers, tuple notes, ...) * from given database table. * It is possible due to that pres_id is unique identifier * common for all tables */ int db_remove_presentity_data(presentity_t* presentity, const char *table); /* set authorization rules for presentity * ! call from locked region only ! */ int set_auth_rules(presentity_t *p, presence_rules_t *new_auth_rules); /* Run a timer handler on the presentity - cleanup of expired data, sending * notifications when presentity modified, ... */ int timer_presentity(presentity_t* _p); /********** UTILITY functions **********/ /* Gets UID from message (using get_to_uid) * (it never allocates memory !!!) */ int get_presentity_uid(str *uid_dst, struct sip_msg *m); /* * converts uri to uid (uid is allocated in shm) * used by internal subscriptions and fifo commands * FIXME: remove (internal subscriptions will be through UID too) */ int pres_uri2uid(str_t *uid_dst, const str_t *uri); /* FIXME: change to local function within pdomain.c as soon as * will be message queue data types solved */ void free_tuple_change_info_content(tuple_change_info_t *i); /************ Parameters **********/ /* how often refresh authorization rules (xcap change events are * not implemented yet!) */ extern int auth_rules_refresh_time; int pdomain_load_presentities(struct pdomain *pdomain); #endif /* PRESENTITY_H */ kamailio-4.0.4/obsolete/pa/rpc.c0000644000000000000000000002117612223032460015150 0ustar rootroot#include "../../error.h" #include "../../parser/parse_event.h" #include "pdomain.h" #include "dlist.h" #include #include #include "qsa_interface.h" #include "pa_mod.h" #include #include #include #include "publish.h" extern dlist_t* root; /* FIXME ugly !!!!! */ /* #define rpc_lf(rpc, c) rpc->add(c, "s","") */ #define rpc_lf(rpc, c) do { } while (0) static void trace_tuple(presence_tuple_t *t, rpc_t* rpc, void* c) { presence_note_t *n; extension_element_t *ps; rpc->printf(c, " %.*s contact=\'%.*s\' exp=%u " "status=%d published=%d (id=%.*s)", FMT_STR(t->data.id), FMT_STR(t->data.contact), t->expires - time(NULL), (int)t->data.status.basic, t->is_published, FMT_STR(t->published_id)); rpc_lf(rpc, c); rpc->printf(c, " notes:"); n = t->data.first_note; while (n) { rpc->printf(c, " \'%.*s\'", FMT_STR(n->value)); n = n->next; } rpc_lf(rpc, c); rpc->printf(c, " extension elements:"); rpc_lf(rpc, c); ps = t->data.first_unknown_element; while (ps) { rpc_lf(rpc, c); rpc->printf(c, " %.*s", FMT_STR(ps->element)); rpc_lf(rpc, c); ps = ps->next; } rpc_lf(rpc, c); rpc->printf(c, " status extension elements:"); rpc_lf(rpc, c); ps = t->data.status.first_unknown_element; while (ps) { rpc_lf(rpc, c); rpc->printf(c, " %.*s", FMT_STR(ps->element)); rpc_lf(rpc, c); ps = ps->next; } rpc_lf(rpc, c); rpc->printf(c, ""); } static void trace_presentity(presentity_t *p, rpc_t* rpc, void* c) { watcher_t *w; presence_tuple_t *t; internal_pa_subscription_t *iw; pa_presence_note_t *n; pa_extension_element_t *ps; rpc->printf(c, "* %.*s (uid=%.*s)", FMT_STR(p->data.uri), FMT_STR(p->uuid)); rpc_lf(rpc, c); rpc->printf(c, " - tuples:"); rpc_lf(rpc, c); t = get_first_tuple(p); while (t) { trace_tuple(t, rpc, c); t = get_next_tuple(t); } rpc->printf(c, " - watchers:"); rpc_lf(rpc, c); w = p->first_watcher; while (w) { rpc->printf(c, " %.*s status=%d exp=%u", FMT_STR(w->uri), (int)w->status, w->expires - time(NULL)); rpc_lf(rpc, c); w = w->next; } rpc->printf(c, " - winfo watchers:"); rpc_lf(rpc, c); w = p->first_winfo_watcher; while (w) { rpc->printf(c, " %.*s status=%d exp=%u", FMT_STR(w->uri), (int)w->status, w->expires - time(NULL)); rpc_lf(rpc, c); w = w->next; } rpc->printf(c, " - internal watchers:"); rpc_lf(rpc, c); iw = p->first_qsa_subscription; while (iw) { rpc->printf(c, " %.*s %d", FMT_STR(*get_subscriber_id(iw->subscription)), (int)iw->status); rpc_lf(rpc, c); iw = iw->next; } rpc->printf(c, " - notes:"); rpc_lf(rpc, c); n = get_first_note(p); while (n) { rpc->printf(c, " %.*s (%.*s) exp=%s", FMT_STR(n->data.value), FMT_STR(n->data.lang), ctime(&n->expires)); n = get_next_note(n); } rpc_lf(rpc, c); rpc->printf(c, " - extension elements:"); rpc_lf(rpc, c); ps = get_first_extension(p); while (ps) { rpc->printf(c, " exp=%d", (int)(ps->expires - time(NULL))); rpc_lf(rpc, c); rpc->printf(c, " %.*s", FMT_STR(ps->data.element)); rpc_lf(rpc, c); ps = get_next_extension(ps); } rpc_lf(rpc, c); } static void trace_dlist(dlist_t *dl, rpc_t* rpc, void* c, int detailed) { presentity_t *p; int cnt = 0; if (!dl) return; if (!dl->d) return; lock_pdomain(dl->d); rpc->add(c, "S", dl->d->name); p = dl->d->first; while (p) { if (detailed) trace_presentity(p, rpc, c); cnt++; p = p->next; } rpc_lf(rpc, c); rpc->printf(c, "presentity count: %d", cnt); unlock_pdomain(dl->d); } static const char* rpc_trace_doc[] = { "Display internal data structure.", /* Documentation string */ 0 /* Method signature(s) */ }; static void rpc_trace(rpc_t* rpc, void* c) { dlist_t *dl; int detailed = 0; if (rpc->scan(c, "d", &detailed) <= 0) { detailed = 0; rpc->fault(c, 400, "Invalid argument - number needed"); return; } dl = root; while (dl) { trace_dlist(dl, rpc, c, detailed); dl = dl->next; } rpc->send(c); } static int grant_watcher(presentity_t *p, watcher_t *w) { int changed = 0; switch (w->status) { case WS_PENDING: case WS_REJECTED: w->status = WS_ACTIVE; changed = 1; break; case WS_PENDING_TERMINATED: w->status = WS_TERMINATED; changed = 1; break; default: break; } if (changed) { w->flags |= WFLAG_SUBSCRIPTION_CHANGED; if (w->event_package != EVENT_PRESENCE_WINFO) p->flags |= PFLAG_WATCHERINFO_CHANGED; } return 0; } static int grant_internal_watcher(presentity_t *p, internal_pa_subscription_t *w) { int changed = 0; switch (w->status) { case WS_PENDING: case WS_REJECTED: w->status = WS_ACTIVE; changed = 1; break; case WS_PENDING_TERMINATED: w->status = WS_TERMINATED; changed = 1; break; default: break; } if (changed) { /* w->flags |= WFLAG_SUBSCRIPTION_CHANGED; */ notify_internal_watcher(p, w); p->flags |= PFLAG_WATCHERINFO_CHANGED; } return 0; } static int grant_watchers(presentity_t *p, str *wuri) { watcher_t *w; internal_pa_subscription_t *iw; w = p->first_watcher; while (w) { if (str_case_equals(&w->uri, wuri) == 0) grant_watcher(p, w); w = w->next; } iw = p->first_qsa_subscription; while (iw) { if (str_case_equals(get_subscriber_id(iw->subscription), wuri) == 0) grant_internal_watcher(p, iw); iw = iw->next; } return 0; } static const char* rpc_authorize_doc[] = { "Authorize watcher.", /* Documentation string */ 0 /* Method signature(s) */ }; static void rpc_authorize(rpc_t* rpc, void* c) { pdomain_t *d; presentity_t *p; str pstr, wstr, uid; char* domain; if (rpc->scan(c, "sSS", &domain, &pstr, &wstr) < 3) { rpc->fault(c, 400, "Invalid parameter value"); return; } if (find_pdomain(domain, &d) != 0) { rpc->fault(c, 400, "Unknown domain '%s'\n", domain); return; } if (pres_uri2uid(&uid, &pstr) != 0) { rpc->fault(c, 400, "Unable to convert '%.*s' to UID\n", pstr.len, pstr.s); return; } lock_pdomain(d); if (find_presentity_uid(d, &pstr, &p) != 0) { rpc->fault(c, 400, "Presentity '%.*s' not found\n", pstr.len, pstr.s); unlock_pdomain(d); str_free_content(&uid); return; } grant_watchers(p, &wstr); unlock_pdomain(d); str_free_content(&uid); } static void rpc_pa_publish(rpc_t* rpc, void* c) { pdomain_t *d; presentity_t *p; presentity_info_t *pi = NULL; str pstr, doc; char* domain; str etag = STR_NULL; time_t expires = time(NULL); int exp_sec = 0; int res; void *st; dbid_t generated_etag; xcap_query_params_t xcap_params; int has_etag = 1; res = rpc->scan(c, "sSSd", &domain, &pstr, &doc, &exp_sec); /* TODO: args = domain, uri, presence doc, expires, etag (for republishing) */ if (res < 4) { rpc->fault(c, 400, "Invalid parameter value (%d)", res); return; } if (rpc->scan(c, "S", &etag) < 1) { /* ETag was not set, generate a new one */ generate_dbid(generated_etag); etag.len = dbid_strlen(generated_etag); etag.s = dbid_strptr(generated_etag); has_etag = 0; } expires += exp_sec; if (find_pdomain(domain, &d) != 0) { rpc->fault(c, 400, "Unknown domain '%s'\n", domain); return; } /* if (pres_uri2uid(&uid, &pstr) != 0) { rpc->fault(c, 400, "Unable to convert '%.*s' to UID\n", pstr.len, pstr.s); return; } */ lock_pdomain(d); res = find_presentity_uid(d, &pstr, &p); if (res > 0) { memset(&xcap_params, 0, sizeof(xcap_params)); if (fill_xcap_params) fill_xcap_params(NULL, &xcap_params); res = new_presentity(d, &pstr /* BUG !!! */, &pstr, &xcap_params, &p); } if (res < 0) { rpc->fault(c, 400, "Can't create/find presentity '%.*s'\n", pstr.len, pstr.s); unlock_pdomain(d); return; } if (doc.len > 0) { if (parse_pidf_document(&pi, doc.s, doc.len) != 0) { rpc->fault(c, 400, "Can't parse presence document (not PIDF?)\n"); unlock_pdomain(d); return; } } else pi = NULL; /* if (pi) { */ if (process_published_presentity_info(p, pi, &etag, expires, has_etag) != 0) { if (pi) free_presentity_info(pi); rpc->fault(c, 400, "Can't publish\n"); unlock_pdomain(d); return; } /* } */ if (pi) free_presentity_info(pi); unlock_pdomain(d); if (rpc->add(c, "{", &st) < 0) return; rpc->struct_add(st, "S", "etag", &etag); rpc->send(c); } static const char* rpc_pa_publish_doc[] = { "Publish presence document.", /* Documentation string */ 0 /* Method signature(s) */ }; /* * RPC Methods exported by this module */ rpc_export_t pa_rpc_methods[] = { {"pa.authorize", rpc_authorize, rpc_authorize_doc, 0}, {"pa.trace", rpc_trace, rpc_trace_doc, 0}, {"pa.publish", rpc_pa_publish, rpc_pa_publish_doc, 0}, {0, 0, 0, 0} }; kamailio-4.0.4/obsolete/pa/pa_mod.c0000644000000000000000000004711412223032460015623 0ustar rootroot/* * Presence Agent, module interface * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-16 flags export parameter added (janakj) * 2004-06-08 updated to the new DB api (andrei) */ #include #include "../../lib/srdb2/db.h" #include "../../sr_module.h" #include "../../error.h" #include "subscribe.h" #include "publish.h" #include "dlist.h" #include "pa_mod.h" #include "watcher.h" #include "rpc.h" #include "qsa_interface.h" #include "async_auth.h" #include #include #include #include "status_query.h" #include "offline_winfo.h" #include "message.h" MODULE_VERSION static int pa_mod_init(void); /* Module initialization function */ static int pa_child_init(int _rank); /* Module child init function */ static void pa_destroy(void); /* Module destroy function */ static int subscribe_fixup(void** param, int param_no); /* domain name -> domain pointer */ static void timer(unsigned int ticks, void* param); /* Delete timer for all domains */ int default_expires = 3600; /* Default expires value if not present in the message (for SUBSCRIBE and PUBLISH) */ int max_subscription_expiration = 3600; /* max expires value for subscribe */ int max_publish_expiration = 3600; /* max expires value for subscribe */ int timer_interval = 10; /* Expiration timer interval in seconds */ double default_priority = 0.0; /* Default priority of presence tuple */ static int default_priority_percentage = 0; /* expressed as percentage because config file grammar does not support floats */ int watcherinfo_notify = 1; /* send watcherinfo notifications */ /** TM bind */ struct tm_binds tmb; dlg_func_t dlg_func; fill_xcap_params_func fill_xcap_params = NULL; /** database */ db_con_t* pa_db = NULL; /* Database connection handle */ db_func_t pa_dbf; int use_db = 1; str db_url = STR_NULL; int use_place_table = 0; #ifdef HAVE_LOCATION_PACKAGE str pa_domain = STR_NULL; #endif /* HAVE_LOCATION_PACKAGE */ /* DB tables */ char *presentity_table = "presentity"; char *presentity_contact_table = "presentity_contact"; char *presentity_notes_table = "presentity_notes"; char *extension_elements_table = "presentity_extensions"; char *tuple_extensions_table = "tuple_extensions"; char *tuple_notes_table = "tuple_notes"; char *watcherinfo_table = "watcherinfo"; char *place_table = "place"; char *offline_winfo_table = "offline_winfo"; /* columns in DB tables */ char *col_uri = "uri"; char *col_pdomain = "pdomain"; char *col_uid = "uid"; char *col_pres_id = "pres_id"; /* common for more tables */ char *col_xcap_params = "xcap_params"; char *col_tupleid = "tupleid"; /* common for more tables */ char *col_basic = "basic"; char *col_contact = "contact"; char *col_etag = "etag"; /* common for more tables */ char *col_published_id = "published_id"; char *col_priority = "priority"; char *col_expires = "expires"; /* common for more tables */ char *col_dbid = "dbid"; /* common for more tables */ char *col_note = "note"; char *col_lang = "lang"; char *col_element = "element"; /* common for more tables */ char *col_status_extension = "status_extension"; char *col_s_id = "s_id"; char *col_w_uri = "w_uri"; char *col_package = "package"; char *col_status = "status"; char *col_display_name = "display_name"; char *col_accepts = "accepts"; char *col_event = "event"; char *col_dialog = "dialog"; char *col_server_contact = "server_contact"; char *col_doc_index = "doc_index"; char *col_watcher = "watcher"; char *col_events = "events"; char *col_domain = "domain"; char *col_created_on = "created_on"; char *col_expires_on = "expires_on"; /* authorization parameters */ char *auth_type_str = NULL; /* type of authorization */ char *winfo_auth_type_str = "implicit"; /* type of authorization */ auth_params_t pa_auth_params; /* structure filled according to parameters */ auth_params_t winfo_auth_params; /* structure for watcherinfo filled according to parameters */ str pres_rules_file = STR_NULL; /* filename for XCAP queries */ int use_bsearch = 0; int use_location_package = 0; /* use callbacks to usrloc/??? - if 0 only pusblished information is used */ int use_callbacks = 1; int use_offline_winfo = 0; int offline_winfo_timer_interval = 3600; int subscribe_to_users = 0; str pa_subscription_uri = STR_NULL; /* ignore 408 response on NOTIFY messages (don't destroy the subscription in the case of it if set */ int ignore_408_on_notify = 0; /* locally generated NOTIFY is terget refresh transaction */ int notify_is_refresh = 1; /* * Exported functions */ static cmd_export_t cmds[]={ {"handle_subscription", handle_subscription, 1, subscribe_fixup, REQUEST_ROUTE | FAILURE_ROUTE}, {"handle_publish", handle_publish, 1, subscribe_fixup, REQUEST_ROUTE | FAILURE_ROUTE}, {"target_online", target_online, 1, subscribe_fixup, REQUEST_ROUTE | FAILURE_ROUTE}, {"check_subscription_status", check_subscription_status, 1, check_subscription_status_fix, REQUEST_ROUTE | FAILURE_ROUTE}, {"store_winfo", store_offline_winfo, 1, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"dump_stored_winfo", dump_offline_winfo, 2, subscribe_fixup, REQUEST_ROUTE | FAILURE_ROUTE}, /* TODO: move into XCAP module? */ {"authorize_message", authorize_message, 1, 0, REQUEST_ROUTE | FAILURE_ROUTE}, /* FIXME: are these functions used to something by somebody */ /* * {"pua_exists", pua_exists, 1, subscribe_fixup, REQUEST_ROUTE }, {"pa_handle_registration", pa_handle_registration, 1, subscribe_fixup, REQUEST_ROUTE }, {"existing_subscription", existing_subscription, 1, subscribe_fixup, REQUEST_ROUTE }, {"mangle_pidf", mangle_pidf, 0, NULL, REQUEST_ROUTE | FAILURE_ROUTE}, {"mangle_message_cpim", mangle_message_cpim, 0, NULL, REQUEST_ROUTE | FAILURE_ROUTE},*/ {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[]={ {"default_expires", PARAM_INT, &default_expires }, {"max_subscription_expiration", PARAM_INT, &max_subscription_expiration }, {"max_publish_expiration", PARAM_INT, &max_publish_expiration }, {"auth", PARAM_STRING, &auth_type_str }, /* type of authorization: none, implicit, xcap, ... */ {"winfo_auth", PARAM_STRING, &winfo_auth_type_str }, /* type of authorization: none, implicit, xcap, ... */ {"use_db", PARAM_INT, &use_db }, {"use_callbacks", PARAM_INT, &use_callbacks }, /* use callbacks to usrloc/jabber ? */ {"accept_internal_subscriptions", PARAM_INT, &accept_internal_subscriptions }, {"watcherinfo_notify", PARAM_INT, &watcherinfo_notify }, /* accept winfo subscriptions ? */ {"use_offline_winfo", PARAM_INT, &use_offline_winfo }, /* use DB for offline winfo */ {"offline_winfo_expiration", PARAM_INT, &offline_winfo_expiration }, /* how long hold information in DB */ {"offline_winfo_timer", PARAM_INT, &offline_winfo_timer_interval }, /* basic ticks of "offline winfo" timer */ {"db_url", PARAM_STR, &db_url }, {"pres_rules_file", PARAM_STR, &pres_rules_file }, {"ignore_408_on_notify", PARAM_INT, &ignore_408_on_notify }, /* ignore 408 responses on NOTIFY */ {"timer_interval", PARAM_INT, &timer_interval }, {"default_priority_percentage", PARAM_INT, &default_priority_percentage }, {"auth_rules_refresh_time", PARAM_INT, &auth_rules_refresh_time }, {"async_auth_queries", PARAM_INT, &async_auth_queries }, {"max_auth_requests_per_tick", PARAM_INT, &max_auth_requests_per_tick }, /* experimental, undocumented */ {"subscribe_to_users", PARAM_INT, &subscribe_to_users }, {"pa_subscription_uri", PARAM_STR, &pa_subscription_uri }, /* undocumented still (TODO) */ {"presentity_table", PARAM_STRING, &presentity_table }, {"presentity_contact_table", PARAM_STRING, &presentity_contact_table }, {"watcherinfo_table", PARAM_STRING, &watcherinfo_table }, {"offline_winfo_table", PARAM_STRING, &offline_winfo_table }, /* table with offline winfo */ /* deprecated (undocumented, remove) */ {"place_table", PARAM_STRING, &place_table }, {"use_place_table", PARAM_INT, &use_place_table }, {"use_bsearch", PARAM_INT, &use_bsearch }, {"use_location_package", PARAM_INT, &use_location_package }, /* db columns, undocumented */ /* DB: columns for table "offline_winfo" */ {"uid_column", PARAM_STRING, &col_uid }, {"watcher_column", PARAM_STRING, &col_watcher }, {"events_column", PARAM_STRING, &col_events }, {"domain_column", PARAM_STRING, &col_domain }, {"status_column", PARAM_STRING, &col_status }, {"created_on_column", PARAM_STRING, &col_created_on }, {"expires_on_column", PARAM_STRING, &col_expires_on }, {"dbid_column", PARAM_STRING, &col_dbid }, /* DB: columns for table "presentity_contact" */ {"pres_id_column", PARAM_STRING, &col_pres_id }, {"basic_column", PARAM_STRING, &col_basic }, {"expires_column", PARAM_STRING, &col_expires }, {"priority_column", PARAM_STRING, &col_priority }, {"contact_column", PARAM_STRING, &col_contact }, {"tupleid_column", PARAM_STRING, &col_tupleid }, {"etag_column", PARAM_STRING, &col_etag }, {"published_id_column", PARAM_STRING, &col_published_id }, /* DB: columns for table "presentity_extensions" */ /*{"dbid_column", PARAM_STRING, &col_dbid },*/ /*{"pres_id_column", PARAM_STRING, &col_pres_id },*/ /*{"etag_column", PARAM_STRING, &col_etag },*/ {"element_column", PARAM_STRING, &col_element }, /*{"expires_column", PARAM_STRING, &col_expires },*/ /* DB: columns for table "presentity_notes" */ /*{"dbid_column", PARAM_STRING, &col_dbid },*/ /*{"pres_id_column", PARAM_STRING, &col_pres_id },*/ /*{"etag_column", PARAM_STRING, &col_etag },*/ {"note_column", PARAM_STRING, &col_note }, {"lang_column", PARAM_STRING, &col_lang }, /*{"expires_column", PARAM_STRING, &col_expires },*/ /* DB: columns for table "presentity" */ /*{"pres_id_column", PARAM_STRING, &col_pres_id },*/ {"uri_column", PARAM_STRING, &col_uri }, /*{"uid_column", PARAM_STRING, &col_uid },*/ {"pdomain_column", PARAM_STRING, &col_pdomain }, {"xcap_params_column", PARAM_STRING, &col_xcap_params }, /* DB: columns for table "tuple_extensions" */ /*{"pres_id_column", PARAM_STRING, &col_pres_id },*/ /*{"tupleid_column", PARAM_STRING, &col_tupleid },*/ /*{"element_column", PARAM_STRING, &col_element },*/ {"status_extension_column", PARAM_STRING, &col_status_extension }, /* DB: columns for table "tuple_notes" */ /*{"pres_id_column", PARAM_STRING, &col_pres_id },*/ /*{"tupleid_column", PARAM_STRING, &col_tupleid },*/ /*{"note_column", PARAM_STRING, &col_note },*/ /*{"lang_column", PARAM_STRING, &col_lang },*/ /* DB: columns for table "watcherinfo" */ {"w_uri_column", PARAM_STRING, &col_w_uri }, {"display_name_column", PARAM_STRING, &col_display_name }, {"s_id_column", PARAM_STRING, &col_s_id }, {"package_column", PARAM_STRING, &col_package }, /*{"status_column", PARAM_STRING, &col_status },*/ {"event_column", PARAM_STRING, &col_event }, /*{"expires_column", PARAM_STRING, &col_expires },*/ {"accepts_column", PARAM_STRING, &col_accepts }, /*{"pres_id_column", PARAM_STRING, &col_pres_id },*/ {"server_contact_column", PARAM_STRING, &col_server_contact }, {"dialog_column", PARAM_STRING, &col_dialog }, {"doc_index_column", PARAM_STRING, &col_doc_index }, {"notify_is_refresh", PARAM_INT, ¬ify_is_refresh }, {0, 0, 0} }; struct module_exports exports = { "pa", cmds, /* Exported functions */ pa_rpc_methods, /* RPC methods */ params, /* Exported parameters */ pa_mod_init, /* module initialization function */ 0, /* response function*/ pa_destroy, /* destroy function */ 0, /* oncancel function */ pa_child_init /* per-child init function */ }; char* decode_mime_type(char *start, char *end, unsigned int *mime_type); static void test_mimetype_parser(void) { static struct mimetype_test { char *string; int parsed; } mimetype_tests[] = { { "application/cpim", MIMETYPE(APPLICATION,CPIM) }, { "text/plain", MIMETYPE(TEXT,PLAIN) }, { "message/cpim", MIMETYPE(MESSAGE,CPIM) }, { "application/sdp", MIMETYPE(APPLICATION,SDP) }, { "application/cpl+xml", MIMETYPE(APPLICATION,CPLXML) }, { "application/pidf+xml", MIMETYPE(APPLICATION,PIDFXML) }, { "application/rlmi+xml", MIMETYPE(APPLICATION,RLMIXML) }, { "multipart/related", MIMETYPE(MULTIPART,RELATED) }, { "application/lpidf+xml", MIMETYPE(APPLICATION,LPIDFXML) }, { "application/xpidf+xml", MIMETYPE(APPLICATION,XPIDFXML) }, { "application/watcherinfo+xml", MIMETYPE(APPLICATION,WATCHERINFOXML) }, { "application/external-body", MIMETYPE(APPLICATION,EXTERNAL_BODY) }, { "application/*", MIMETYPE(APPLICATION,ALL) }, { "text/xml+msrtc.pidf", MIMETYPE(TEXT,XML_MSRTC_PIDF) }, { "application/cpim-pidf+xml", MIMETYPE(APPLICATION,CPIM_PIDFXML) }, { NULL, 0 } }; struct mimetype_test *mt = &mimetype_tests[0]; LOG(L_DBG, "Presence Agent - testing mimetype parser\n"); while (mt->string) { unsigned int pmt; LOG(L_DBG, "Presence Agent - parsing mimetype %s\n", mt->string); decode_mime_type(mt->string, mt->string+strlen(mt->string), &pmt); if (pmt != mt->parsed) { LOG(L_ERR, "Parsed mimetype %s got %x expected %x\n", mt->string, pmt, mt->parsed); } mt++; } } static int set_auth_params(auth_params_t *dst, const char *auth_type_str, const char *log_str) { if (!auth_type_str) { LOG(L_ERR, "no subscription authorization type for %s given, using \'implicit\'!\n", log_str); dst->type = auth_none; return 0; } if (strcmp(auth_type_str, "xcap") == 0) { dst->type = auth_xcap; return 0; } if (strcmp(auth_type_str, "none") == 0) { dst->type = auth_none; LOG(L_WARN, "using \'none\' subscription authorization for %s!\n", log_str); return 0; } if (strcmp(auth_type_str, "implicit") == 0) { dst->type = auth_implicit; return 0; } LOG(L_ERR, "Can't resolve subscription authorization for %s type: \'%s\'." " Use one of: none, implicit, xcap.\n", log_str, auth_type_str); return -1; } static int pa_mod_init(void) { load_tm_f load_tm; bind_dlg_mod_f bind_dlg; /* SER_PROFILE_INIT - init in child (each process needs it) */ test_mimetype_parser(); DBG("Presence Agent - initializing\n"); DBG(" ... common libraries\n"); qsa_initialize(); if (subscribe_to_users) { if (is_str_empty(&pa_subscription_uri)) { ERR("pa_subscription_uri must be set if set subscribe_to_users\n"); return -1; } if (accept_internal_subscriptions) { ERR("impossible to have set accept_internal_subscriptions together with subscribe_to_users\n"); return -1; } } /* set authorization type according to requested "auth type name" * and other (type specific) parameters */ if (set_auth_params(&pa_auth_params, auth_type_str, "presence") != 0) return -1; /* set authorization type for watcherinfo * according to requested "auth type name" * and other (type specific) parameters */ if (set_auth_params(&winfo_auth_params, winfo_auth_type_str, "watcher info") != 0) return -1; /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) { LOG(L_ERR, "Can't import tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) { return -1; } bind_dlg = (bind_dlg_mod_f)find_export("bind_dlg_mod", -1, 0); if (!bind_dlg) { LOG(L_ERR, "Can't import dlg\n"); return -1; } if (bind_dlg(&dlg_func) != 0) { return -1; } /* Register cache timer */ register_timer(timer, 0, timer_interval); /* register offline winfo timer */ if (use_offline_winfo) register_timer(offline_winfo_timer, 0, offline_winfo_timer_interval); #ifdef HAVE_LOCATION_PACKAGE if (pa_domain.len == 0) { LOG(L_ERR, "pa_mod_init(): pa_domain must be specified\n"); return -1; } LOG(L_DBG, "pa_mod: pa_mod=%s\n", ZSW(pa_domain.s)); #endif /* HAVE_LOCATION_PACKAGE */ LOG(L_DBG, "pa_mod: use_db=%d us_offline_winfo=%d db_url.s=%s\n", use_db, use_offline_winfo, ZSW(db_url.s)); if (use_db || use_offline_winfo) { if (!db_url.len) { LOG(L_ERR, "pa_mod_init(): no db_url specified but DB has to be used " "(use_db=%d us_offline_winfo=%d)\n", use_db, use_offline_winfo); return -1; } if (bind_dbmod(db_url.s, &pa_dbf) < 0) { /* Find database module */ LOG(L_ERR, "pa_mod_init(): Can't bind database module via url %s\n", db_url.s); return -1; } if (!DB_CAPABILITY(pa_dbf, DB_CAP_ALL)) { LOG(L_ERR, "pa_mod_init(): Database module does not implement all functions needed by the module\n"); return -1; } } default_priority = ((double)default_priority_percentage) / 100.0; if (pa_qsa_interface_init() != 0) { LOG(L_CRIT, "pa_mod_init(): QSA interface initialization failed!\n"); return -1; } fill_xcap_params = (fill_xcap_params_func)find_export("fill_xcap_params", 0, -1); if (async_auth_timer_init() < 0) { ERR("can't init async timer\n"); return -1; } LOG(L_DBG, "pa_mod_init done\n"); return 0; } /* can be called after pa_mod_init and creates new PA DB connection */ db_con_t* create_pa_db_connection() { if (!(use_db || use_offline_winfo)) return NULL; if (!pa_dbf.init) return NULL; return pa_dbf.init(db_url.s); } void close_pa_db_connection(db_con_t* db) { if (db && pa_dbf.close) pa_dbf.close(db); } static int pa_child_init(int _rank) { /* Shall we use database ? */ if (use_db || use_offline_winfo) { /* Yes */ if (_rank==PROC_INIT || _rank==PROC_MAIN || _rank==PROC_TCP_MAIN) return 0; /* do nothing for the main or tcp_main processes */ pa_db = create_pa_db_connection(); if (!pa_db) { LOG(L_ERR, "ERROR: pa_child_init(%d): " "Error while connecting database\n", _rank); return -1; } } #ifdef DO_TRACE SER_PROFILE_INIT #endif return 0; } static void pa_destroy(void) { DBG("PA module cleanup\n"); DBG("destroying PA module\n"); DBG(" ... qsa interface\n"); pa_qsa_interface_destroy(); DBG(" ... pdomains\n"); free_all_pdomains(); if ((use_db || use_offline_winfo) && pa_db) { DBG(" ... closing db connection\n"); close_pa_db_connection(pa_db); } pa_db = NULL; DBG(" ... cleaning common libs\n"); qsa_cleanup(); } /* * Convert char* parameter to pdomain_t* pointer */ static int subscribe_fixup(void** param, int param_no) { pdomain_t* d; if (param_no == 1) { LOG(L_DBG, "subscribe_fixup: pdomain name is %s\n", (char*)*param); if (register_pdomain((char*)*param, &d) < 0) { LOG(L_ERR, "subscribe_fixup(): Error while registering domain\n"); return E_UNSPEC; } *param = (void*)d; } return 0; } /* * Timer handler */ static void timer(unsigned int ticks, void* param) { if (timer_all_pdomains() != 0) { LOG(L_ERR, "timer(): Error while synchronizing domains\n"); } } kamailio-4.0.4/obsolete/pa/winfo_doc.h0000644000000000000000000000055312223032460016334 0ustar rootroot#ifndef __WINFO_DOC_H #define __WINFO_DOC_H #include "presentity.h" #include "watcher.h" #include "offline_winfo.h" int create_winfo_document(struct presentity* p, struct watcher* w, str *dst, str *dst_content_type); int create_winfo_document_offline(struct presentity* p, struct watcher* w, offline_winfo_t *infos, str *dst, str *dst_content_type); #endif kamailio-4.0.4/obsolete/pa/publish.c0000644000000000000000000003065712223032460016036 0ustar rootroot/* * Presence Agent, publish handling * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2003-2004 Hewlett-Packard Company * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include "../../str.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_event.h" #include "dlist.h" #include "presentity.h" #include "watcher.h" #include "notify.h" #include "paerrno.h" #include "pdomain.h" #include "pa_mod.h" #include "ptime.h" #include "reply.h" #include "subscribe.h" #include "publish.h" #include "tuple.h" #include "pres_notes.h" #include "extension_elements.h" #include "../../data_lump_rpl.h" #include "../../parser/parse_sipifmatch.h" #include #include #include #include #include /* ------------ Helper functions ------------ */ /* * Parse all header fields that will be needed * to handle a PUBLISH request */ static int parse_publish_hfs(struct sip_msg* _m) { int rc = 0; if ((rc = parse_headers(_m, HDR_FROM_F | HDR_EVENT_F | HDR_EXPIRES_F | HDR_SIPIFMATCH_F | HDR_CONTENTTYPE_F | HDR_CONTENTLENGTH_F, 0)) == -1) { paerrno = PA_PARSE_ERR; LOG(L_ERR, "parse_publish_hfs(): Error while parsing headers\n"); return -1; } if (parse_from_header(_m) < 0) { paerrno = PA_FROM_ERR; LOG(L_ERR, "parse_publish_hfs(): From malformed or missing\n"); return -6; } if (_m->event) { if (parse_event(_m->event) < 0) { paerrno = PA_EVENT_PARSE; LOG(L_ERR, "parse_publish_hfs(): Error while parsing Event header field\n"); return -8; } } else { paerrno = PA_EVENT_PARSE; LOG(L_ERR, "parse_publish_hfs(): Missing Event header field\n"); return -7; } if (_m->expires) { if (parse_expires(_m->expires) < 0) { paerrno = PA_EXPIRES_PARSE; LOG(L_ERR, "parse_publish_hfs(): Error while parsing Expires header field\n"); return -9; } } /* patch from PIC-SER */ if (_m->sipifmatch) { if (parse_sipifmatch(_m->sipifmatch) < 0) { paerrno = PA_PARSE_ERR; LOG(L_ERR, "parse_hfs(): Error while parsing SIP-If-Match header field\n"); return -10; } } if (_m->content_type) { if (parse_content_type_hdr(_m) < 0) { LOG(L_ERR, "parse_hfs(): Can't parse Content-Type\n"); return -12; } } return 0; } static inline void generate_etag(dbid_t dst, presentity_t *p) { generate_dbid_ptr(dst, p); } static void add_expires_to_rpl(struct sip_msg *_m, int expires) { char tmp[64]; if (expires < 0) expires = 0; sprintf(tmp, "Expires: %d\r\n", expires); if (!add_lump_rpl(_m, tmp, strlen(tmp), LUMP_RPL_HDR)) { LOG(L_ERR, "Can't add expires header to the response\n"); } } static void add_etag_to_rpl(struct sip_msg *_m, str *etag) { char *tmp; tmp = (char*)pkg_malloc(32 + etag->len); if (!tmp) { LOG(L_ERR, "Can't allocate package memory for SIP-ETag header to the response\n"); return; } sprintf(tmp, "SIP-ETag: %.*s\r\n", etag->len, etag->s); if (!add_lump_rpl(_m, tmp, strlen(tmp), LUMP_RPL_HDR)) { LOG(L_ERR, "Can't add SIP-ETag header to the response\n"); /* return -1; */ } pkg_free(tmp); } /* ------------ publishing functions ------------ */ static void add_presentity_notes(presentity_t *presentity, presentity_info_t *p, str *etag, time_t expires) { presence_note_t *n; pa_presence_note_t *pan; if (!p) return; n = p->first_note; while (n) { pan = presence_note2pa(n, etag, expires); if (pan) add_pres_note(presentity, pan); n = n->next; } } static void add_extension_elements(presentity_t *presentity, presentity_info_t *p, str *etag, time_t expires) { extension_element_t *n; pa_extension_element_t *pan; if (!p) return; n = p->first_unknown_element; while (n) { pan = extension_element2pa(n, etag, expires); if (pan) add_extension_element(presentity, pan); n = n->next; } } static void add_published_tuples(presentity_t *presentity, presentity_info_t *p, str *etag, time_t expires) { presence_tuple_info_t *i; presence_tuple_t *t; if (!p) return; i = p->first_tuple; while (i) { t = presence_tuple_info2pa(i, etag, expires); if (t) add_presence_tuple(presentity, t); i = i->next; } } static int update_published_tuples(presentity_t *presentity, presentity_info_t *p, str *etag, time_t expires) { presence_tuple_info_t *i; presence_tuple_t *t, *tt; int found = 0; double mark = -149.386; if (!p) return 0; /* mark tuples as unprocessed */ t = get_first_tuple(presentity); while (t) { if (str_case_equals(&t->etag, etag) == 0) { t->data.priority = mark; found++; } t = get_next_tuple(t); } /* add previously not published tuples and update previously published */ i = p->first_tuple; while (i) { t = find_published_tuple(presentity, etag, &i->id); if (t) { /* the tuple was published this way */ found++; update_tuple(presentity, t, i, expires); } else { /* this tuple was not published => add it */ t = presence_tuple_info2pa(i, etag, expires); if (t) add_presence_tuple(presentity, t); } i = i->next; } /* remove previously published tuples which were not processed (not present now) */ t = get_first_tuple(presentity); while (t) { tt = get_next_tuple(t); if (t->data.priority == mark) { remove_presence_tuple(presentity, t); free_presence_tuple(t); } t = tt; } return found; } static int update_all_published_tuples(presentity_t *p, str *etag, time_t expires) { int found = 0; presence_tuple_t *tuple = get_first_tuple(p); while (tuple) { if (str_case_equals(&tuple->etag, etag) == 0) { tuple->expires = expires; found++; db_update_presence_tuple(p, tuple, 0); } tuple = get_next_tuple(tuple); } return found; } static int update_pres_notes(presentity_t *p, str *etag, time_t expires) { int found = 0; pa_presence_note_t *note = get_first_note(p); while (note) { if (str_case_equals(¬e->etag, etag) == 0) { note->expires = expires; found++; db_update_pres_note(p, note); } note = get_next_note(note); } return found; } static int update_extension_elements(presentity_t *p, str *etag, time_t expires) { int found = 0; pa_extension_element_t *e = (pa_extension_element_t *)p->data.first_unknown_element; while (e) { if (str_case_equals(&e->etag, etag) == 0) { e->expires = expires; db_update_extension_element(p, e); found++; } e = (pa_extension_element_t *)e->data.next; } return found; } int process_published_presentity_info(presentity_t *presentity, presentity_info_t *p, str *etag, time_t expires, int has_etag) { if (!has_etag) { if (!p) return -1; /* must be published something */ /* add all notes for presentity */ add_presentity_notes(presentity, p, etag, expires); /* add all tuples */ add_published_tuples(presentity, p, etag, expires); /* add all extension elements (RPID) */ add_extension_elements(presentity, p, etag, expires); } else { if (p) { /* remove all notes for this etag */ remove_pres_notes(presentity, etag); /* remove all extension elements (RPID) */ remove_extension_elements(presentity, etag); /* add all notes for presentity */ add_presentity_notes(presentity, p, etag, expires); update_published_tuples(presentity, p, etag, expires); add_extension_elements(presentity, p, etag, expires); } else { /* all expirations must be refreshed, nothing cleared */ update_all_published_tuples(presentity, etag, expires); update_extension_elements(presentity, etag, expires); update_pres_notes(presentity, etag, expires); } } presentity->flags |= PFLAG_PRESENCE_CHANGED; return 0; } /* ------------ PUBLISH handling functions ------------ */ static int publish_presence(struct sip_msg* _m, struct presentity* presentity) { char *body = get_body(_m); int body_len = 0; int msg_expires = default_expires; time_t expires = 0; str etag; dbid_t generated_etag; int has_etag; presentity_info_t *p = NULL; int content_type = -1; if (_m->content_type) content_type = get_content_type(_m); if (_m->content_length) body_len = get_content_length(_m); if (_m->expires) { if (_m->expires->parsed) { msg_expires = ((exp_body_t*)_m->expires->parsed)->val; } } if (msg_expires > max_publish_expiration) msg_expires = max_publish_expiration; if (msg_expires != 0) expires = msg_expires + act_time; if (_m->sipifmatch) { if (_m->sipifmatch->parsed) etag = *(str*)_m->sipifmatch->parsed; else str_clear(&etag); has_etag = 1; } else { /* ETag was not set, generate a new one */ generate_dbid(generated_etag); etag.len = dbid_strlen(generated_etag); etag.s = dbid_strptr(generated_etag); has_etag = 0; } if (body_len > 0) { switch (content_type) { case MIMETYPE(APPLICATION,PIDFXML): if (parse_pidf_document(&p, body, body_len) != 0) { LOG(L_ERR, "can't parse PIDF document\n"); paerrno = PA_UNSUPP_DOC; /* ? PA_PARSE_ERR */ } break; case MIMETYPE(APPLICATION,CPIM_PIDFXML): if (parse_cpim_pidf_document(&p, body, body_len) != 0) { LOG(L_ERR, "can't parse CPIM-PIDF document\n"); paerrno = PA_UNSUPP_DOC; } break; default: LOG(L_ERR, "unsupported Content-Type 0x%x for PUBLISH handling\n", content_type); paerrno = PA_UNSUPP_DOC; } if (paerrno != PA_OK) return -1; } if (process_published_presentity_info(presentity, p, &etag, expires, has_etag) == 0) { /* add header fields into response */ add_expires_to_rpl(_m, msg_expires); add_etag_to_rpl(_m, &etag); } if (p) free_presentity_info(p); return 0; } static int publish_presentity(struct sip_msg* _m, struct pdomain* _d, struct presentity* presentity) { event_t *parsed_event = NULL; int event_package = EVENT_OTHER; str callid = STR_STATIC_INIT("???"); int res; if (_m->event) parsed_event = (event_t *)_m->event->parsed; if (parsed_event) event_package = parsed_event->parsed; LOG(L_DBG, "publish_presentity: event_package=%d -1-\n", event_package); switch (event_package) { case EVENT_PRESENCE: res = publish_presence(_m, presentity); break; default: if (_m->callid) callid = _m->callid->body; LOG(L_WARN, "publish_presentity: no handler for event_package=%d" " callid=%.*s\n", event_package, callid.len, ZSW(callid.s)); paerrno = PA_EVENT_UNSUPP; res = -1; } return res; } /* * Handle a publish Request */ int handle_publish(struct sip_msg* _m, char* _domain, char* _s2) { struct pdomain* d; struct presentity *p; str p_uri = STR_NULL; str uid = STR_NULL; xcap_query_params_t xcap_params; get_act_time(); paerrno = PA_OK; if (parse_publish_hfs(_m) < 0) { LOG(L_ERR, "handle_publish(): Error while parsing message header\n"); goto error; } d = (struct pdomain*)_domain; if (get_pres_uri(_m, &p_uri) < 0 || p_uri.s == NULL || p_uri.len == 0) { LOG(L_ERR, "handle_publish(): Error while extracting presentity URI\n"); goto error; } if (get_presentity_uid(&uid, _m) < 0) { ERR("Error while extracting presentity UID\n"); goto error; } lock_pdomain(d); if (find_presentity_uid(d, &uid, &p) > 0) { memset(&xcap_params, 0, sizeof(xcap_params)); if (fill_xcap_params) fill_xcap_params(_m, &xcap_params); if (new_presentity(d, &p_uri, &uid, &xcap_params, &p) < 0) { LOG(L_ERR, "handle_publish can't create presentity\n"); goto error2; } } /* update presentity event state */ if (p) publish_presentity(_m, d, p); unlock_pdomain(d); if (send_reply(_m) < 0) return -1; return 1; error2: unlock_pdomain(d); error: send_reply(_m); return 0; } kamailio-4.0.4/obsolete/pa/tuple_extensions.h0000644000000000000000000000134412223032460017774 0ustar rootroot#ifndef __TUPLE_EXTENSIONS_H #define __TUPLE_EXTENSIONS_H #include "presentity.h" int db_read_tuple_extensions(presentity_t *p, presence_tuple_t *t, db_con_t* db); int db_add_tuple_extensions(presentity_t *p, presence_tuple_t *t); /* add all exts for tuple into DB */ int db_remove_tuple_extensions(presentity_t *p, presence_tuple_t *t); /* remove all exts for tuple */ int db_update_tuple_extensions(presentity_t *p, presence_tuple_t *t); /* adds extension element to tuple in memory, not in DB (use update)! */ void add_tuple_extension_no_wb(presence_tuple_t *t, extension_element_t *n, int is_status_extension); /* frees all notes for given tuple (in memory only, not DB) */ void free_tuple_extensions(presence_tuple_t *t); #endif kamailio-4.0.4/obsolete/pa/tuple_notes.h0000644000000000000000000000122412223032460016722 0ustar rootroot#ifndef __TUPLE_NOTES_H #define __TUPLE_NOTES_H #include "presentity.h" int db_read_tuple_notes(presentity_t *p, presence_tuple_t *t, db_con_t* db); int db_add_tuple_notes(presentity_t *p, presence_tuple_t *t); /* add all notes for tuple into DB */ int db_remove_tuple_notes(presentity_t *p, presence_tuple_t *t); /* remove all notes for tuple */ int db_update_tuple_notes(presentity_t *p, presence_tuple_t *t); /* adds note to tuple in memory, not in DB (use update)! */ void add_tuple_note_no_wb(presence_tuple_t *t, presence_note_t *n); /* frees all notes for given tuple (in memory only, not DB) */ void free_tuple_notes(presence_tuple_t *t); #endif kamailio-4.0.4/obsolete/pa/async_auth.c0000644000000000000000000000631312223032460016516 0ustar rootroot#include "pa_mod.h" #include "async_auth.h" #include "../../timer.h" int async_timer_interval = 1; int max_auth_requests_per_tick = 50; int async_auth_queries = 0; static msg_queue_t *async_mq = NULL; typedef struct { str uid; struct pdomain *d; char buf[1]; } async_auth_query_t; int xcap_get_pres_rules(str *uid, cp_ruleset_t **dst, xcap_query_params_t *xcap) { int res; str *filename = NULL; /* str u; */ if (!is_str_empty(&pres_rules_file)) filename = &pres_rules_file; res = get_pres_rules(uid, filename, xcap, dst); return res; } static inline void query_auth_rules(async_auth_query_t *params) { presence_rules_t *rules; presentity_t *p; lock_pdomain(params->d); if (find_presentity_uid(params->d, ¶ms->uid, &p) == 0) { p->ref_cnt++; } else p = NULL; unlock_pdomain(params->d); if (p) { rules = NULL; /* we can use p->xcap_params because it doesn't change * till the presentity dies */ xcap_get_pres_rules(¶ms->uid, &rules, &p->xcap_params); lock_pdomain(params->d); /*if (rules)*/ set_auth_rules(p, rules); p->ref_cnt--; unlock_pdomain(params->d); } } static void async_timer_cb(unsigned int ticks, void *param) { mq_message_t *msg; async_auth_query_t *params; int cnt = 0; /* TODO: dynamicaly set max_xcap_requests according to * load, free mem, ... */ /* process queries in message queue */ /* the number of processed queries may be limited for one step */ msg = pop_message(async_mq); while (msg) { /* INFO("processing authorization rules query\n"); */ params = (async_auth_query_t*)get_message_data(msg); if (params) query_auth_rules(params); free_message(msg); if (++cnt > max_auth_requests_per_tick) break; msg = pop_message(async_mq); } } int async_auth_timer_init() { if (register_timer(async_timer_cb, NULL, async_timer_interval) < 0) { LOG(L_ERR, "vs_init(): can't register timer\n"); return -1; } async_mq = shm_malloc(sizeof(*async_mq)); if (!async_mq) { ERR("can't allocate memory\n"); return -1; } msg_queue_init(async_mq); return 0; } int ask_auth_rules(presentity_t *p) { int len, res; mq_message_t *msg; async_auth_query_t *params; presence_rules_t *rules = NULL; /* ! call from critical section locked by pdomain mutex ! */ if (pa_auth_params.type != auth_xcap) { return 0; /* don't load anything */ } if (!async_auth_queries) { /* do synchronous query - it is called from locked section, * thus it is not good for performance! */ res = xcap_get_pres_rules(&p->uuid, &rules, &p->xcap_params); if (res == RES_OK) set_auth_rules(p, rules); return res; } /* Ask for authorization rules (only XCAP now). This should be done * asynchronously, at least for time consuming operations like XCAP * queries */ len = sizeof(async_auth_query_t) + p->uuid.len; msg = create_message_ex(len); if (!msg) { ERR("can't allocate memory (%d bytes)\n", len); return -1; } params = (async_auth_query_t*)get_message_data(msg); params->uid.s = params->buf; params->d = p->pdomain; if (!is_str_empty(&p->uuid)) { params->uid.len = p->uuid.len; memcpy(params->uid.s, p->uuid.s, p->uuid.len); } else params->uid.len = 0; push_message(async_mq, msg); /* INFO("asking authorization rules\n"); */ return 0; } kamailio-4.0.4/obsolete/pa/subscribe.h0000644000000000000000000000321312223032460016342 0ustar rootroot/* * Presence Agent, subscribe handling * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef SUBSCRIBE_H #define SUBSCRIBE_H #include "../../parser/msg_parser.h" /* * Handle a subscribe Request */ int handle_subscription(struct sip_msg* _m, char* _domain, char* _s2); /* * Return 1 if the subscription exists and 0 if not */ int existing_subscription(struct sip_msg* _m, char* _domain, char* _s2); /* * Handle a REGISTER Request: ensures AOR is in presentity table. */ int pa_handle_registration(struct sip_msg* _m, char* _domain, char* _s2); /* * Get presentity URI, which is stored in R-URI */ int get_pres_uri(struct sip_msg* _m, str* _puri); #endif /* SUBSCRIBE_H */ kamailio-4.0.4/obsolete/pa/paerrno.h0000644000000000000000000000455712223032460016043 0ustar rootroot/* * Presence Agent, error reporting * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PAERRNO_H #define PAERRNO_H typedef enum paerr { PA_OK, /* Everything went OK */ PA_PARSE_ERR, /* Error while parsing headers */ PA_FROM_MISS, /* From header field missing */ PA_EVENT_MISS, /* Event header field missing */ PA_EVENT_PARSE, /* Error while parsing Event header field */ PA_EXPIRES_PARSE, /* Error while parsing Expires header field */ PA_EVENT_UNSUPP, /* Unsupported event package */ PA_WRONG_ACCEPTS, /* Accepts does not match event package */ PA_NO_MEMORY, /* No memory left */ PA_TIMER_ERROR, /* Error in timer */ PA_EXTRACT_USER, /* Error while extracting username from R-URI */ PA_FROM_ERR, /* From malformed or missing */ PA_TO_ERR, /* To malformed or missing */ PA_SMALL_BUFFER, /* Buffer too small */ PA_UNSUPP_DOC, /* Unsupported presence document format */ PA_ACCEPT_PARSE, /* Error while parsing Accept header field */ PA_URI_PARSE, /* Error while parsing URI */ PA_DIALOG_ERR, /* Error while creating dialog */ PA_INTERNAL_ERROR, /* Internal server error */ PA_SUBSCRIPTION_REJECTED, PA_NO_MATCHING_TUPLE, /* there is no tuple with published SIP-ETag */ PA_OK_WAITING_FOR_AUTH, /* OK but waiting for auth -> should return 202 */ PA_SUBSCRIPTION_NOT_EXISTS /* -> 481 */ } paerr_t; extern paerr_t paerrno; #endif /* PAERRNO_H */ kamailio-4.0.4/obsolete/pa/watcher.h0000644000000000000000000001154412223032460016024 0ustar rootroot/* * Presence Agent, watcher structure and related functions * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef WATCHER_H #define WATCHER_H #include "../../str.h" #include "../../modules/tm/dlg.h" #include "../../lib/srdb2/db.h" #include "../../parser/parse_content.h" #include "../../parser/parse_event.h" /* EVENT_PRESENCE, EVENT_PRESENCE_WINFO, ... */ #include #include #define DOC_XPIDF MIMETYPE(APPLICATION,XPIDFXML) #define DOC_LPIDF MIMETYPE(APPLICATION,LPIDFXML) #define DOC_PIDF MIMETYPE(APPLICATION,PIDFXML) #define DOC_CPIM_PIDF MIMETYPE(APPLICATION,CPIM_PIDFXML) #define DOC_MSRTC_PIDF MIMETYPE(APPLICATION,XML_MSRTC_PIDF) #define DOC_WINFO MIMETYPE(APPLICATION,WATCHERINFOXML) /* DOC_XCAP_CHANGE = (1 << 4), DOC_LOCATION = (1 << 5),*/ #define DOC_MULTIPART_RELATED MIMETYPE(MULTIPART,RELATED), #define DOC_RLMI_XML MIMETYPE(APPLICATION,RLMIXML) typedef enum watcher_status { WS_PENDING = 0, WS_ACTIVE = 1, WS_REJECTED = 2, WS_TERMINATED = 3, WS_PENDING_TERMINATED = 4 } watcher_status_t; extern str watcher_status_names[]; extern str watcher_event_names[]; typedef enum watcher_event { WE_SUBSCRIBE = 0, WE_APPROVED = 1, WE_DEACTIVATED = 2, WE_PROBATION = 3, WE_REJECTED = 4, WE_TIMEOUT = 5, WE_GIVEUP = 6, WE_NORESOURCE = 7 } watcher_event_t; typedef enum wflags { WFLAG_SUBSCRIPTION_CHANGED=1 } wflags_t; /* * Structure representing a watcher */ typedef struct watcher { str display_name; /* Display Name of watcher */ str uri; /* Uri of the watcher */ time_t expires; /* Absolute of the expiration */ int event_package; /* event package being watched */ int preferred_mimetype; /* Type of document accepted by the watcher */ int document_index; /* many documents (winfo, ...) requires sequential numbering */ dlg_t* dialog; /* Dialog handle */ str id; /* id of this watcher (used for DB and winfo docs) */ str server_contact; /* used for contact header in NOTIFY messages */ wflags_t flags; watcher_event_t event; watcher_status_t status; /* status of subscription */ struct watcher *prev, *next; /* linking members */ } watcher_t; struct presentity; /* Convert watcher status name to enum */ watcher_status_t watcher_status_from_string(str *wsname); /* Create a new watcher structure */ int new_watcher_no_wb(str* _uri, time_t _e, int event_package, int doc_type, dlg_t* _dlg, str *display_name, str *server_contact, str *id, /* database ID or NULL if not loading from DB */ watcher_t** _w); /* Release a watcher structure */ void free_watcher(watcher_t* _w); /** Appends watcher/winfo watcher to presentity. It updates presentity's * flags and adds the watcher into DB if requested and use_db set. */ int append_watcher(struct presentity *_p, watcher_t *_w, int add_to_db); /* Remove a watcher/winfo watcher from the watcher list and from database. */ void remove_watcher(struct presentity* _p, watcher_t* _w); /* Find a watcher/winfo watcher in the list/winfo list (according to * _et parameter) via dialog identifier */ int find_watcher_dlg(struct presentity* _p, dlg_id_t *dlg_id, int _et, watcher_t** _w); /* update watcher in db */ int db_update_watcher(struct presentity *p, watcher_t* _w); /* Update expires value of a watcher */ int update_watcher(struct presentity *p, watcher_t* _w, time_t _e, struct sip_msg *m); /* Read watcherinfo table from database for presentity _p */ int db_read_watcherinfo(struct presentity *_p, db_con_t* db); /** Returns 1 if given watcher is in one of terminated statuses * and should be deleted */ int is_watcher_terminated(watcher_t *w); /** Returns 1 if given watcher can receive status documents */ int is_watcher_authorized(watcher_t *w); /** Sets status to correct terminated status for this watcher. */ void set_watcher_terminated_status(watcher_t *w); const char *event_package2str(int et); #endif /* WATCHER_H */ kamailio-4.0.4/obsolete/pa/offline_winfo.c0000644000000000000000000002760512223032460017213 0ustar rootroot/* offline presence authorization "requests" e.g. storing * watcher information for offline presentities */ #include #include "../../str.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "dlist.h" #include "presentity.h" #include "watcher.h" #include "pdomain.h" #include "pa_mod.h" #include "../../parser/parse_from.h" #include #include #include #include #include #include "offline_winfo.h" #include "winfo_doc.h" #include "message.h" /* ----- Helper and internal functions ----- */ #define add_str_len(len,p) if (p) if (p->s) len += p->len #define set_member_str(buf,len,dst,src) if (src) if (src->s) { \ memcpy(buf + len, src->s, src->len); \ dst.s = buf + len; \ dst.len = src->len; \ len += src->len; \ } #define string_val(v,s) (v).type = DB_STR; \ (v).val.str_val=s; \ (v).nul=(s.len == 0); #define time_val(v,t) (v).type = DB_DATETIME; \ (v).val.time_val=t;\ (v).nul=0; #define get_str_val(rvi,dst) do{if(!rvi.nul){dst.s=(char*)rvi.val.string_val;dst.len=strlen(dst.s);}}while(0) #define get_blob_val(rvi,dst) do{if(!rvi.nul){dst=rvi.val.blob_val;}else dst.len=0;}while(0) #define get_time_val(rvi,dst) do{if(!rvi.nul){dst=rvi.val.time_val;}}while(0) #define get_int_val(rvi,dst) do{if(!rvi.nul){dst=rvi.val.int_val;}else dst=0;}while(0) /* expiration time in secs */ int offline_winfo_expiration = 259200; /* status of last subscription */ static watcher_status_t last_subscription_status = WS_PENDING; void set_last_subscription_status(watcher_status_t status) { last_subscription_status = status; } watcher_status_t get_last_subscription_status() { return last_subscription_status; } static offline_winfo_t *create_winfo(str *uid, str *wuri, str *events, str *domain, str *status) { int len = 0; offline_winfo_t *info; add_str_len(len, uid); add_str_len(len, wuri); add_str_len(len, events); add_str_len(len, domain); add_str_len(len, status); len += sizeof(offline_winfo_t); info = (offline_winfo_t*)mem_alloc(len); if (info) { memset(info, 0, len); len = 0; set_member_str(info->buffer, len, info->uid, uid); set_member_str(info->buffer, len, info->watcher, wuri); set_member_str(info->buffer, len, info->events, events); set_member_str(info->buffer, len, info->domain, domain); set_member_str(info->buffer, len, info->status, status); info->created = time(NULL); info->expires = info->created + offline_winfo_expiration; info->index = -1; } return info; } static inline void free_winfo(offline_winfo_t *info) { if (info) mem_free(info); } static inline void free_winfos(offline_winfo_t *info) { offline_winfo_t *n; while (info) { n = info->next; free_winfo(info); info = n; } } static int db_store_winfo(offline_winfo_t *info) { /* ignore duplicit records (should be stored only once!) */ db_key_t cols[20]; db_val_t vals[20]; int n = -1; if (!pa_db) { ERR("database not initialized: set parameter \'use_offline_winfo\' to 1\n"); return -1; } if (pa_dbf.use_table(pa_db, offline_winfo_table) < 0) { LOG(L_ERR, "db_add_watcher: Error in use_table\n"); return -1; } cols[++n] = col_uid; string_val(vals[n], info->uid); cols[++n] = col_watcher; string_val(vals[n], info->watcher); cols[++n] = col_events; string_val(vals[n], info->events); cols[++n] = col_domain; string_val(vals[n], info->domain); cols[++n] = col_status; string_val(vals[n], info->status); cols[++n] = col_created_on; time_val(vals[n], info->created); cols[++n] = col_expires_on; time_val(vals[n], info->expires); /* index ("dbid") is created automaticaly ! */ /* insert new record into database */ if (pa_dbf.insert(pa_db, cols, vals, n + 1) < 0) { return -1; } return 0; } static int get_watcher_uri(struct sip_msg* _m, str* uri) { struct sip_uri puri; int res = 0; uri->s = get_from(_m)->uri.s; uri->len = get_from(_m)->uri.len; if (parse_uri(uri->s, uri->len, &puri) < 0) { LOG(L_ERR, "Error while parsing URI\n"); return -1; } #if 0 uri->s = puri.user.s; if ((!uri->s) || (puri.user.len < 1)) { uri->s = puri.host.s; uri->len = puri.host.len; res = 1; /* it is uri without username ! */ } #endif uri->len = puri.host.s + puri.host.len - uri->s; return res; } static int get_events(struct sip_msg* _m, str* events) { char *c; str_clear(events); if (parse_headers(_m, HDR_EVENT_F, 0) == -1) { ERR("Error while parsing headers\n"); return -1; } if (_m->event) { /* parse_event(_m->event); */ *events = _m->event->body; c = str_strchr(events, ';'); if (c) events->len = c - events->s; } return 0; } int remove_expired_winfos() { db_key_t keys[] = { col_expires_on }; db_val_t vals[1] = { { DB_DATETIME, 0, { .time_val = time(NULL) } } }; db_op_t ops[] = { OP_LEQ }; int res = 0; if (!pa_db) { ERR("database not initialized: set parameter \'use_offline_winfo\' to 1\n"); return -1; } if (pa_dbf.use_table(pa_db, offline_winfo_table) < 0) { LOG(L_ERR, "db_add_watcher: Error in use_table\n"); return -1; } res = pa_dbf.delete(pa_db, keys, ops, vals, 1); if (res < 0 ) DBG("ERROR cleaning expired offline winfo\n"); return res; } int db_remove_winfos(offline_winfo_t *info) { db_key_t keys[] = { col_dbid }; db_val_t vals[1]; db_op_t ops[] = { OP_EQ }; int res = 0; if (!pa_db) { ERR("database not initialized: set parameter \'use_offline_winfo\' to 1\n"); return -1; } if (pa_dbf.use_table(pa_db, offline_winfo_table) < 0) { LOG(L_ERR, "Error in use_table\n"); return -1; } while (info) { vals[0].type = DB_INT; vals[0].nul = 0; vals[0].val.int_val = info->index; res = pa_dbf.delete(pa_db, keys, ops, vals, 1); if (res < 0 ) DBG("ERROR cleaning expired offline winfo\n"); info = info->next; } return res; } static void send_winfo_cb(struct cell* t, int type, struct tmcb_params* params) { offline_winfo_t *info = NULL; if (!params) { ERR("BUG: empty arg\n"); return; } if (params->param) info = (offline_winfo_t *)*(params->param); if (!info) { ERR("BUG: empty arg\n"); return; } if ((params->code >= 200) && (params->code < 300)) { /* delete infos from DB */ db_remove_winfos(info); } else ERR("%d response on winfo NOTIFY\n", params->code); /* delete infos from memory */ free_winfos(info); } static int send_winfo(presentity_t *p, offline_winfo_t *info) { watcher_t *w; if (!p) { ERR("BUG: trying to send offline winfo to empty presentity\n"); return -1; } w = p->first_winfo_watcher; while (w) { if (w->status == WS_ACTIVE) { if (send_winfo_notify_offline(p, w, info, send_winfo_cb, info) == 0) return 0; } w = w->next; } return -1; /* impossible to send it */ } int db_load_winfo(str *uid, str *events, str *domain, offline_winfo_t **infos) { int i, r = 0; db_res_t *res = NULL; db_key_t result_cols[] = { col_watcher, col_created_on, col_expires_on, col_dbid, col_status }; db_key_t keys[] = { col_uid, col_events }; db_op_t ops[] = { OP_EQ, OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = *uid } } }; offline_winfo_t *info = NULL; offline_winfo_t *last = NULL; *infos = NULL; if (pa_dbf.use_table(pa_db, offline_winfo_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.query (pa_db, keys, ops, k_vals, result_cols, 1, sizeof(result_cols) / sizeof(db_key_t), 0, &res) < 0) { ERR("Error while querying stored winfos\n"); r = -1; res = NULL; } if (res) { for (i = 0; i < res->n; i++) { db_row_t *row = &res->rows[i]; db_val_t *row_vals = ROW_VALUES(row); str watcher = STR_NULL; str status = STR_NULL; time_t created_on = 0; time_t expires_on = 0; int index = 0; get_str_val(row_vals[0], watcher); get_time_val(row_vals[1], created_on); get_time_val(row_vals[2], expires_on); get_int_val(row_vals[3], index); get_str_val(row_vals[4], status); info = create_winfo(uid, &watcher, events, domain, &status); if (!info) { r = -1; break; } info->created = created_on; info->expires = expires_on; info->index = index; if (last) last->next = info; else *infos = info; last = info; } pa_dbf.free_result(pa_db, res); } if ((*infos) && (r != 0)) { free_winfos(*infos); *infos = NULL; } return r; } /* ----- Handler functions ----- */ #if 0 /* not used due to problems with lost AVP after sending NOTIFY */ static int get_status(str *dst) { avp_t *avp; int_str name, val; struct search_state s; str avp_subscription_status = STR_STATIC_INIT("subscription_status"); /* if (!dst) return -1; */ name.s = avp_subscription_status; avp = search_first_avp(AVP_CLASS_USER | AVP_TRACK_FROM | AVP_NAME_STR | AVP_VAL_STR, name, &val, 0); if (avp) { /* don't use default - use value from AVP */ TRACE("subscription status = %.*s\n", FMT_STR(val.s)); *dst = val.s; return 0; } else { /* leave default value!! */ /* TRACE("left default subscription status\n"); */ TRACE("subscription status AVP not found\n"); } return 1; } #endif static void get_status_str(str *dst) { str s; switch(get_last_subscription_status()) { case WS_ACTIVE: s = watcher_status_names[WS_ACTIVE]; break; case WS_REJECTED: case WS_PENDING_TERMINATED: case WS_TERMINATED: s = watcher_status_names[WS_TERMINATED]; break; case WS_PENDING: s = watcher_status_names[WS_PENDING]; break; default: str_clear(&s); /* throw out gcc complains */ } if (dst) *dst = s; } int store_offline_winfo(struct sip_msg* _m, char* _domain, char* _table) { str uid = STR_NULL; str wuri = STR_NULL; str events = STR_NULL; str domain = STR_NULL; str status = STR_NULL; int res = -1; offline_winfo_t *info; if (get_presentity_uid(&uid, _m) < 0) { ERR("Error while extracting presentity UID\n"); return 0; /* ??? impossible to return -1 or 1 */ } get_watcher_uri(_m, &wuri); get_events(_m, &events); if (_domain) { domain.s = _domain; domain.len = strlen(_domain); } /* get stored subscription status value */ get_status_str(&status); /* TRACE("subscription status is: %.*s\n", FMT_STR(status)); */ info = create_winfo(&uid, &wuri, &events, &domain, &status); /* store it into database or use internal data structures too? */ /* better to use only database because of lower memory usage - this * information could be stored for very long time ! */ db_store_winfo(info); free_winfo(info); /* don't hold this information in memory ! */ return res; } /* send offline winfo as regular watcher info NOTIFY if possible * (for presentity got from get_to) */ int dump_offline_winfo(struct sip_msg* _m, char* _domain, char* _events) { struct pdomain* d; struct presentity *p; str uid = STR_NULL; int res = -1; str events; offline_winfo_t *info; d = (struct pdomain*)_domain; if (get_presentity_uid(&uid, _m) < 0) { ERR("Error while extracting presentity UID\n"); return -1; } if (_events) { events.s = _events; events.len = strlen(_events); } if (db_load_winfo(&uid, &events, d->name, &info) != 0) { return -1; } if (!info) { return 1; /* nothing to do */ } lock_pdomain(d); if (find_presentity_uid(d, &uid, &p) == 0) { /* presentity found */ if (send_winfo(p, info) == 0) res = 1; else res = -1; } unlock_pdomain(d); return res; } void offline_winfo_timer(unsigned int ticks, void* param) { remove_expired_winfos(); } int check_subscription_status(struct sip_msg* _m, char* _status, char* _x) { watcher_status_t status = (watcher_status_t)_status; if (status == get_last_subscription_status()) return 1; else return -1; } /* convert char* parameter to watcher_status_t */ int check_subscription_status_fix(void **param, int param_no) { watcher_status_t status = WS_PENDING; char *s; str ss; if (param_no == 1) { s = (char*)*param; if (!s) { ERR("status not given!\n"); return -1; } /* TRACE("status name is %s\n", (char*)*param); */ ss.s = s; ss.len = strlen(s); status = watcher_status_from_string(&ss); *param = (void*)status; } return 0; } kamailio-4.0.4/obsolete/pa/offline_winfo.h0000644000000000000000000000313312223032460017206 0ustar rootroot#ifndef __OFFLINE_WINFO_H #define __OFFLINE_WINFO_H #include "pa_mod.h" typedef struct _offline_winfo_t { time_t created; /* date and time of this info creation */ time_t expires; /* date and time of this info expiration */ int index; /* key generated by database - it is used for removing winfos from DB */ str uid; /* presentity identification */ str watcher; /* watcher's uri */ str events; /* event package */ str domain; /* domain */ str status; /* status of subscription */ struct _offline_winfo_t *next; /* for linking infos together */ char buffer[1]; } offline_winfo_t; int store_offline_winfo(struct sip_msg* _m, char* _domain, char* _str); /* send offline winfo as regular watcher info NOTIFY if possible * (for presentity got from get_to) */ int dump_offline_winfo(struct sip_msg* _m, char* _domain, char* _s2); /* expiration time in seconds */ extern int offline_winfo_expiration; void offline_winfo_timer(unsigned int ticks, void* param); int send_winfo_notify_offline(struct presentity* _p, struct watcher* _w, offline_winfo_t *info, transaction_cb completion_cb, void* cbp); /* For storing status of last subscription is used global variable, * NOT AVP because AVPs are cleared when sending NOTIFY request from * handle_subscripton function and behaviour of AVPs in such cases is * not strictly defined. */ void set_last_subscription_status(watcher_status_t status); watcher_status_t get_last_subscription_status(); int check_subscription_status(struct sip_msg* _m, char* _status, char* _x); int check_subscription_status_fix(void **param, int param_no); #endif kamailio-4.0.4/obsolete/pa/qsa_interface.h0000644000000000000000000000072412223032460017171 0ustar rootroot#ifndef __PA_QSA_INTERFACE_H #define __PA_QSA_INTERFACE_H int pa_qsa_interface_init(); void pa_qsa_interface_destroy(); #include "presentity.h" int notify_qsa_watchers(presentity_t *p); int notify_internal_watcher(presentity_t *p, internal_pa_subscription_t *ss); void free_internal_subscription(internal_pa_subscription_t *is); int subscribe_to_user(presentity_t *_p); int unsubscribe_to_user(presentity_t *_p); extern int accept_internal_subscriptions; #endif kamailio-4.0.4/obsolete/pa/pres_timer.c0000644000000000000000000001631512223032460016534 0ustar rootroot#include "presentity.h" #include "pa_mod.h" #include "ptime.h" #include "notify.h" #include "async_auth.h" #include "tuple.h" #include "pres_notes.h" #include "extension_elements.h" static void process_watchers(presentity_t* _p, int *changed) { watcher_t *next, *w; int presentity_changed; int notify; /* !!! "changed" is not initialized here it is only set if change * in presentity occurs */ presentity_changed = _p->flags & PFLAG_PRESENCE_CHANGED; w = _p->first_watcher; while (w) { /* changes status of expired watcher */ if (w->expires <= act_time) { LOG(L_DBG, "Expired watcher %.*s\n", w->uri.len, w->uri.s); w->expires = 0; set_watcher_terminated_status(w); _p->flags |= PFLAG_WATCHERINFO_CHANGED; w->flags |= WFLAG_SUBSCRIPTION_CHANGED; if (changed) *changed = 1; } /* send NOTIFY if needed */ notify = 0; if ((w->flags & WFLAG_SUBSCRIPTION_CHANGED)) { notify = 1; if (changed) *changed = 1; /* ??? */ } if (presentity_changed && is_watcher_authorized(w)) notify = 1; if (notify) send_notify(_p, w); w->flags &= ~WFLAG_SUBSCRIPTION_CHANGED; if (is_watcher_terminated(w)) { next = w->next; remove_watcher(_p, w); free_watcher(w); w = next; if (changed) *changed = 1; } else w = w->next; } } static void process_winfo_watchers(presentity_t* _p, int *changed) { watcher_t *next, *w; int notify; /* !!! "changed" is not initialized here it is only set if change * in presentity occurs */ w = _p->first_winfo_watcher; while (w) { /* changes status of expired watcher */ if (w->expires <= act_time) { LOG(L_DBG, "Expired watcher %.*s\n", w->uri.len, w->uri.s); w->expires = 0; set_watcher_terminated_status(w); w->flags |= WFLAG_SUBSCRIPTION_CHANGED; if (changed) *changed = 1; } /* send NOTIFY if needed */ notify = 0; if ((w->flags & WFLAG_SUBSCRIPTION_CHANGED)) { notify = 1; if (changed) *changed = 1; /* ??? */ } if ((_p->flags & PFLAG_WATCHERINFO_CHANGED) && is_watcher_authorized(w)) notify = 1; if (notify) send_notify(_p, w); w->flags &= ~WFLAG_SUBSCRIPTION_CHANGED; if (is_watcher_terminated(w)) { next = w->next; remove_watcher(_p, w); free_watcher(w); w = next; if (changed) *changed = 1; } else w = w->next; } } /* static void mark_expired_tuples(presentity_t *_p, int *changed) { presence_tuple_t *t; t = _p->tuples; while (t) { if (t->expires < act_time) { t->state = PS_OFFLINE; if (changed) *changed = 1; _p->flags |= PFLAG_PRESENCE_CHANGED; } t = t->next; } }*/ static void remove_expired_tuples(presentity_t *_p, int *changed) { presence_tuple_t *t, *n; t = (presence_tuple_t*)_p->data.first_tuple; while (t) { n = (presence_tuple_t *)t->data.next; if (t->expires < act_time) { DBG("Expiring tuple %.*s\n", t->data.contact.len, t->data.contact.s); remove_presence_tuple(_p, t); free_presence_tuple(t); if (changed) *changed = 1; _p->flags |= PFLAG_PRESENCE_CHANGED; } t = n; } } static void remove_expired_notes(presentity_t *_p) { pa_presence_note_t *n, *nn; n = (pa_presence_note_t*)_p->data.first_note; while (n) { nn = (pa_presence_note_t *)n->data.next; if (n->expires < act_time) { DBG("Expiring note %.*s\n", FMT_STR(n->data.value)); remove_pres_note(_p, n); _p->flags |= PFLAG_PRESENCE_CHANGED; } n = nn; } } static void remove_expired_extension_elements(presentity_t *_p) { pa_extension_element_t *n, *nn; n = (pa_extension_element_t *)_p->data.first_unknown_element; while (n) { nn = (pa_extension_element_t *)n->data.next; if (n->expires < act_time) { DBG("Expiring person element %.*s\n", FMT_STR(n->dbid)); remove_extension_element(_p, n); _p->flags |= PFLAG_PRESENCE_CHANGED; } n = nn; } } static inline int refresh_auth_rules(presentity_t *p) { /* TODO reload authorization rules if needed */ if ((p->auth_rules_refresh_time > 0) && (p->auth_rules_refresh_time <= act_time)) { /* INFO("refreshing auth rules\n"); */ ask_auth_rules(p); /* it will run next time if fails now */ p->auth_rules_refresh_time = act_time + auth_rules_refresh_time; } return 0; } static void process_tuple_change(presentity_t *p, tuple_change_info_t *info) { presence_tuple_t *tuple = NULL; basic_tuple_status_t orig; time_t e; DBG("processing tuple change message: %.*s, %.*s, %d\n", FMT_STR(info->user), FMT_STR(info->contact), info->state); if (is_str_empty(&info->contact)) { /* error - registered tuples need contact address */ ERR("invalid registered tuple (empty contact)\n"); return; } if (info->state == presence_tuple_closed) { e = act_time + 2 * timer_interval; } else { e = INT_MAX; /* act_time + default_expires; */ /* hack - re-registrations don't call the callback */ } /* Find only registered (not published) tuple - don't overwrite * published information! */ if (find_registered_presence_tuple(&info->contact, p, &tuple) != 0) { /* not found -> create new tuple */ new_presence_tuple(&info->contact, e, &tuple, 0, NULL, NULL, NULL); if (!tuple) return; /* error */ tuple->data.status.basic = info->state; add_presence_tuple(p, tuple); p->flags |= PFLAG_PRESENCE_CHANGED; } else { /* tuple found -> update */ orig = tuple->data.status.basic; tuple->data.status.basic = info->state; tuple->expires = e; db_update_presence_tuple(p, tuple, 0); if (orig != tuple->data.status.basic) p->flags |= PFLAG_PRESENCE_CHANGED; } } static int process_qsa_message(presentity_t *p, client_notify_info_t *info) { TRACE("received QSA notification for presentity %.*s\n", FMT_STR(p->data.uri)); /* TODO: handle it as publish for special tuple (but handle merging * from multiple QSA sources in any way) */ return 0; } static void process_presentity_messages(presentity_t *p) { mq_message_t *msg; tuple_change_info_t *info; client_notify_info_t *qsa_info; while ((msg = pop_message(&p->mq)) != NULL) { /* FIXME: ugly data type detection */ if (msg->destroy_function == (destroy_function_f)free_tuple_change_info_content) { info = (tuple_change_info_t*)get_message_data(msg); if (info) process_tuple_change(p, info); } else { /* QSA message */ qsa_info = (client_notify_info_t *)get_message_data(msg); if (qsa_info) process_qsa_message(p, qsa_info); } free_message(msg); } } int timer_presentity(presentity_t* _p) { int old_flags; int presentity_changed; PROF_START(pa_timer_presentity) old_flags = _p->flags; /* reload authorization rules if needed */ refresh_auth_rules(_p); process_presentity_messages(_p); remove_expired_tuples(_p, NULL); remove_expired_notes(_p); remove_expired_extension_elements(_p); /* notify watchers and remove expired */ process_watchers(_p, NULL); /* notify winfo watchers and remove expired */ process_winfo_watchers(_p, NULL); /* notify internal watchers */ presentity_changed = _p->flags & PFLAG_PRESENCE_CHANGED; if (presentity_changed) { /* DBG("presentity %.*s changed\n", _p->uri.len, _p->uri.s); */ notify_qsa_watchers(_p); } /* clear presentity "change" flags */ _p->flags &= ~(PFLAG_PRESENCE_CHANGED | PFLAG_WATCHERINFO_CHANGED); /* update DB record if something changed - USELESS */ /* if (changed) { db_update_presentity(_p); } */ PROF_STOP(pa_timer_presentity) return 0; } kamailio-4.0.4/obsolete/pa/status_query.c0000644000000000000000000000334412223032460017131 0ustar rootroot#include #include "../../str.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "dlist.h" #include "presentity.h" #include "watcher.h" #include "pdomain.h" #include "pa_mod.h" #include #include #include #include /* Helper functions */ presence_tuple_t *find_online_tuple(presentity_t *p, presence_tuple_t *search_from) { presence_tuple_t *t = NULL; if (!p) return t; if (search_from) t = search_from; else t = get_first_tuple(p); while (t) { switch (t->data.status.basic) { case presence_tuple_open: return t; /* TODO: what about other state values? */ default: break; } t = get_next_tuple(t); } return NULL; } /* Handler functions */ int target_online(struct sip_msg* _m, char* _domain, char* _s2) { struct pdomain* d; struct presentity *p; str uid = STR_NULL; int res = -1; presence_tuple_t *t; d = (struct pdomain*)_domain; if (get_presentity_uid(&uid, _m) < 0) { ERR("Error while extracting presentity UID\n"); return 0; /* ??? impossible to return -1 or 1 */ } /* TRACE_LOG("is \'%.*s\' online ?\n", FMT_STR(uid)); */ lock_pdomain(d); if (find_presentity_uid(d, &uid, &p) == 0) { /* presentity found */ t = find_online_tuple(p, NULL); if (t) res = 1; /* online tuple found */ } unlock_pdomain(d); return res; } /* check watcher status for given value */ int test_watcher_status(struct sip_msg* _m, char* _domain, char* _status) { /* returns watcher's authorization status (only existing watchers - should * be called after processing SUBSCRIBE request) */ /* find presentity, not found => -1*/ /* find watcher, not found => -1 */ /* test if auth == watcher status => -1/1 */ return -1; } kamailio-4.0.4/obsolete/pa/pres_notes.c0000644000000000000000000001474412223032460016550 0ustar rootroot#include "presentity.h" #include "pa_mod.h" #include "pres_notes.h" #include /* DB manipulation */ static int db_add_pres_note(presentity_t *p, pa_presence_note_t *n) { db_key_t cols[20]; db_val_t vals[20]; int n_updates = 0; if (!use_db) return 0; /* set data */ cols[n_updates] = col_dbid; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = n->dbid; n_updates++; cols[n_updates] = col_pres_id; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = p->pres_id; n_updates++; cols[n_updates] = col_etag; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = n->etag; n_updates++; cols[n_updates] = col_note; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = n->data.value; n_updates++; cols[n_updates] = col_lang; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = n->data.lang; n_updates++; cols[n_updates] = col_expires; vals[n_updates].type = DB_DATETIME; vals[n_updates].nul = 0; vals[n_updates].val.time_val = n->expires; n_updates++; /* run update */ if (pa_dbf.use_table(pa_db, presentity_notes_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.insert(pa_db, cols, vals, n_updates) < 0) { ERR("Can't insert record\n"); return -1; } return 0; } int db_update_pres_note(presentity_t *p, pa_presence_note_t *n) { db_key_t cols[20]; db_val_t vals[20]; int n_updates = 0; db_key_t keys[] = { col_pres_id, col_etag, col_dbid }; db_op_t ops[] = { OP_EQ, OP_EQ, OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = p->pres_id } }, { DB_STR, 0, { .str_val = n->etag } }, { DB_STR, 0, { .str_val = n->dbid } } }; if (!use_db) return 0; cols[n_updates] = col_note; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = n->data.value; n_updates++; cols[n_updates] = col_lang; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = n->data.lang; n_updates++; cols[n_updates] = col_expires; vals[n_updates].type = DB_DATETIME; vals[n_updates].nul = 0; vals[n_updates].val.time_val = n->expires; n_updates++; if (pa_dbf.use_table(pa_db, presentity_notes_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.update(pa_db, keys, ops, k_vals, cols, vals, 3, n_updates) < 0) { ERR("Can't update record\n"); return -1; } return 0; } static int db_remove_pres_note(presentity_t *p, pa_presence_note_t *n) { db_key_t keys[] = { col_pres_id, col_etag, col_dbid }; db_op_t ops[] = { OP_EQ, OP_EQ, OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = p->pres_id } }, { DB_STR, 0, { .str_val = n->etag } }, { DB_STR, 0, { .str_val = n->dbid } } }; if (!use_db) return 0; if (pa_dbf.use_table(pa_db, presentity_notes_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.delete(pa_db, keys, ops, k_vals, 3) < 0) { ERR("Can't delete record\n"); return -1; } return 0; } int db_read_notes(presentity_t *p, db_con_t* db) { db_key_t keys[] = { col_pres_id }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = p->pres_id } } }; int i; int r = 0; db_res_t *res = NULL; db_key_t result_cols[] = { col_dbid, col_etag, col_note, col_lang, col_expires }; if (!use_db) return 0; if (pa_dbf.use_table(db, presentity_notes_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.query (db, keys, ops, k_vals, result_cols, 1, sizeof(result_cols) / sizeof(db_key_t), 0, &res) < 0) { ERR("Error while querying presence notes\n"); return -1; } if (!res) return 0; /* ? */ for (i = 0; i < res->n; i++) { pa_presence_note_t *n = NULL; db_row_t *row = &res->rows[i]; db_val_t *row_vals = ROW_VALUES(row); str dbid = STR_NULL; str etag = STR_NULL; str note = STR_NULL; str lang = STR_NULL; time_t expires = 0; #define get_str_val(i,dst) do{if(!row_vals[i].nul){dst.s=(char*)row_vals[i].val.string_val;dst.len=strlen(dst.s);}}while(0) #define get_time_val(i,dst) do{if(!row_vals[i].nul){dst=row_vals[i].val.time_val;}}while(0) get_str_val(0, dbid); get_str_val(1, etag); get_str_val(2, note); get_str_val(3, lang); get_time_val(4, expires); #undef get_str_val #undef get_time_val n = create_pres_note(&etag, ¬e, &lang, expires, &dbid); if (n) DOUBLE_LINKED_LIST_ADD(p->data.first_note, p->data.last_note, (presence_note_t*)n); } pa_dbf.free_result(db, res); return r; } /* in memory presence notes manipulation */ void add_pres_note(presentity_t *_p, pa_presence_note_t *n) { DOUBLE_LINKED_LIST_ADD(_p->data.first_note, _p->data.last_note, (presence_note_t*)n); if (use_db) db_add_pres_note(_p, n); } void remove_pres_note(presentity_t *_p, pa_presence_note_t *n) { DOUBLE_LINKED_LIST_REMOVE(_p->data.first_note, _p->data.last_note, (presence_note_t*)n); if (use_db) db_remove_pres_note(_p, n); free_pres_note(n); } void free_pres_note(pa_presence_note_t *n) { if (n) { str_free_content(&n->data.value); str_free_content(&n->data.lang); mem_free(n); } } int remove_pres_notes(presentity_t *p, str *etag) { pa_presence_note_t *n, *nn, *prev; int found = 0; prev = NULL; n = (pa_presence_note_t *)p->data.first_note; while (n) { nn = (pa_presence_note_t *)n->data.next; if (str_case_equals(&n->etag, etag) == 0) { /* remove this */ found++; remove_pres_note(p, n); } else prev = n; n = nn; } return found; } pa_presence_note_t *create_pres_note(str *etag, str *note, str *lang, time_t expires, str *dbid) { int size; pa_presence_note_t *pan; if (!dbid) { ERR("invalid parameters\n"); return NULL; } size = sizeof(pa_presence_note_t); if (dbid) size += dbid->len; if (etag) size += etag->len; pan = (pa_presence_note_t*)mem_alloc(size); if (!pan) { ERR("can't allocate memory (%d)\n", size); return pan; } pan->data.next = NULL; pan->data.prev = NULL; pan->expires = expires; str_dup(&pan->data.value, note); str_dup(&pan->data.lang, lang); pan->dbid.s = (char*)pan + sizeof(*pan); if (dbid) str_cpy(&pan->dbid, dbid); else pan->dbid.len = 0; pan->etag.s = after_str_ptr(&pan->dbid); str_cpy(&pan->etag, etag); return pan; } pa_presence_note_t *presence_note2pa(presence_note_t *n, str *etag, time_t expires) { dbid_t id; str s; generate_dbid(id); s.len = dbid_strlen(id); s.s = dbid_strptr(id); return create_pres_note(etag, &n->value, &n->lang, expires, &s); } kamailio-4.0.4/obsolete/pa/notify.c0000644000000000000000000003526312223032460015676 0ustar rootroot/* * Presence Agent, notifications * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2003-02-28 protocolization of t_uac_dlg completed (jiri) */ #include "../../str.h" #include "../../dprint.h" #include "../../trim.h" #include "../../parser/parse_event.h" #include "pa_mod.h" #include "presentity.h" #include "paerrno.h" #include "notify.h" #include "watcher.h" #include "qsa_interface.h" #include #include #include #include "winfo_doc.h" #include "dlist.h" static str notify = STR_STATIC_INIT("NOTIFY"); /* TM callback processing */ typedef struct { dlg_id_t id; str uid; struct pdomain *domain; /* replace with domain name for safe stop */ char buf[1]; } pa_notify_cb_param_t; static pa_notify_cb_param_t *create_notify_cb_param(presentity_t *p, watcher_t *w) { pa_notify_cb_param_t *cbd; int size; if ((!p) || (!w)) return NULL; if (!w->dialog) return NULL; size = sizeof(*cbd) + p->uuid.len + w->dialog->id.call_id.len + w->dialog->id.rem_tag.len + w->dialog->id.loc_tag.len; cbd = (pa_notify_cb_param_t*)mem_alloc(size); if (!cbd) { ERR("can't allocate memory (%d bytes)\n", size); return NULL; } cbd->domain = p->pdomain; cbd->uid.s = cbd->buf; cbd->uid.len = p->uuid.len; cbd->id.call_id.s = cbd->uid.s + cbd->uid.len; cbd->id.call_id.len = w->dialog->id.call_id.len; cbd->id.rem_tag.s = cbd->id.call_id.s + cbd->id.call_id.len; cbd->id.rem_tag.len = w->dialog->id.rem_tag.len; cbd->id.loc_tag.s = cbd->id.rem_tag.s + cbd->id.rem_tag.len; cbd->id.loc_tag.len = w->dialog->id.loc_tag.len; /* copy data */ if (p->uuid.s) memcpy(cbd->uid.s, p->uuid.s, p->uuid.len); if (w->dialog->id.call_id.s) memcpy(cbd->id.call_id.s, w->dialog->id.call_id.s, w->dialog->id.call_id.len); if (w->dialog->id.rem_tag.s) memcpy(cbd->id.rem_tag.s, w->dialog->id.rem_tag.s, w->dialog->id.rem_tag.len); if (w->dialog->id.loc_tag.s) memcpy(cbd->id.loc_tag.s, w->dialog->id.loc_tag.s, w->dialog->id.loc_tag.len); return cbd; } static int get_watcher(pa_notify_cb_param_t *cbd, watcher_t **w, presentity_t **p) { int et = EVENT_PRESENCE; if (find_presentity_uid(cbd->domain, &cbd->uid, p) != 0) { return -1; } if (find_watcher_dlg(*p, &cbd->id, et, w) != 0) { /* presence watcher NOT found */ et = EVENT_PRESENCE_WINFO; if (find_watcher_dlg(*p, &cbd->id, et, w) != 0) { /* presence.winfo watcher NOT found */ return -1; } } return 0; } static void destroy_subscription(pa_notify_cb_param_t *cbd) { presentity_t *p = NULL; watcher_t *w = NULL; /* if (find_pdomain(cbd->domain, &domain) != 0) { ERR("can't find PA domain\n"); return; } */ lock_pdomain(cbd->domain); if (get_watcher(cbd, &w, &p) != 0) { unlock_pdomain(cbd->domain); return; } remove_watcher(p, w); free_watcher(w); unlock_pdomain(cbd->domain); } static void refresh_dialog(pa_notify_cb_param_t *cbd, struct sip_msg *m) { watcher_t *w; presentity_t *p; lock_pdomain(cbd->domain); if (get_watcher(cbd, &w, &p) >= 0) tmb.dlg_response_uac(w->dialog, m, notify_is_refresh != 0 ? IS_TARGET_REFRESH : IS_NOT_TARGET_REFRESH); unlock_pdomain(cbd->domain); } static void pa_notify_cb(struct cell* t, int type, struct tmcb_params* params) { pa_notify_cb_param_t *cbd = NULL; if (!params) return; /* Possible problems - see subscribe_cb in presence_b2b/euac_funcs.c */ if (params->param) cbd = (pa_notify_cb_param_t *)*(params->param); if (!cbd) { ERR("BUG empty cbd parameter given to callback function\n"); return; } if ((params->code >= 200) && (params->code < 300)) { if (params->rpl && (params->rpl != FAKED_REPLY)) refresh_dialog(cbd, params->rpl); } if ((params->code >= 300)) { int ignore = 0; switch (params->code) { case 408: if (ignore_408_on_notify) ignore = 1; /* due to eyeBeam's problems with processing more NOTIFY * requests sent consequently without big delay */ break; } if (!ignore) { WARN("destroying subscription from callback due to %d response on NOTIFY\n", params->code); destroy_subscription(cbd); TRACE("subscription destroyed!!!\n"); } } shm_free(cbd); } /* helper functions */ static inline int add_event_hf(dstring_t *buf, int event_package) { dstr_append_zt(buf, "Event: "); dstr_append_zt(buf, event_package2str(event_package)); dstr_append_zt(buf, "\r\n"); return 0; } static inline int add_cont_type_hf(dstring_t *buf, str *content_type) { /* content types can have dynamical parameters (multipart/related) * => don't generate them "staticaly"; use values created in the * time of document creation */ if (is_str_empty(content_type)) return 0; /* documents without body doesn't need it */ dstr_append_zt(buf, "Content-Type: "); dstr_append_str(buf, content_type); dstr_append_zt(buf, "\r\n"); return 0; } static inline int add_subs_state_hf(dstring_t *buf, watcher_status_t _s, time_t _e) { char* num; int len; str s = STR_NULL; static str timeout = STR_STATIC_INIT("timeout"); static str rejected = STR_STATIC_INIT("rejected"); switch(_s) { case WS_ACTIVE: ; s = watcher_status_names[WS_ACTIVE]; break; case WS_REJECTED: case WS_PENDING_TERMINATED: case WS_TERMINATED: s = watcher_status_names[WS_TERMINATED]; break; case WS_PENDING: s = watcher_status_names[WS_PENDING]; break; } dstr_append_zt(buf, "Subscription-State: "); dstr_append_str(buf, &s); switch(_s) { case WS_PENDING:; case WS_ACTIVE: dstr_append_zt(buf, ";expires="); num = int2str((unsigned int)_e, &len); dstr_append(buf, num, len); break; case WS_REJECTED: case WS_PENDING_TERMINATED: case WS_TERMINATED: dstr_append_zt(buf, ";reason="); if (_e <= 0) dstr_append_str(buf, &timeout); else dstr_append_str(buf, &rejected); break; } dstr_append_zt(buf, "\r\n"); return 0; } static inline int create_headers(struct watcher* _w, str *dst, str *content_type) { dstring_t buf; time_t t; int err = 0; dstr_init(&buf, 256); str_clear(dst); /* required by RFC 3261 */ dstr_append_zt(&buf, "Max-Forwards: 70\r\n"); /* Event header */ dstr_append_zt(&buf, "Event: "); dstr_append_zt(&buf, event_package2str(_w->event_package)); dstr_append_zt(&buf, "\r\n"); /* Content-Type header */ /* content types can have dynamical parameters (multipart/related) * => don't generate them "staticaly"; use values created in the * time of document creation */ if (!is_str_empty(content_type)) { /* documents without body doesn't need it */ dstr_append_zt(&buf, "Content-Type: "); dstr_append_str(&buf, content_type); dstr_append_zt(&buf, "\r\n"); } /* Contact header */ if (is_str_empty(&_w->server_contact)) { LOG(L_WARN, "add_contact_hf(): Can't add empty contact to NOTIFY.\n"); } else { dstr_append_zt(&buf, "Contact: "); dstr_append_str(&buf, &_w->server_contact); dstr_append_zt(&buf, "\r\n"); } /* Subscription-State header */ if (_w->expires) t = _w->expires - time(0); else t = 0; if (add_subs_state_hf(&buf, _w->status, t) < 0) { LOG(L_ERR, "create_headers(): Error while adding Subscription-State\n"); dstr_destroy(&buf); return -3; } err = dstr_get_str(&buf, dst); dstr_destroy(&buf); return err; } /* NOTIFY creation functions for specific events/state */ static int prepare_presence_notify(struct retr_buf **dst, struct presentity* _p, struct watcher* _w, pa_notify_cb_param_t *cbd) { /* Send a notify, saved Contact will be put in * Request-URI, To will be put in from and new tag * will be generated, callid will be callid, * from will be put in to including tag */ str doc = STR_NULL; str content_type = STR_NULL; str headers = STR_NULL; str body = STR_STATIC_INIT(""); int res = 0; uac_req_t uac_r; switch(_w->preferred_mimetype) { case DOC_XPIDF: res = create_xpidf_document(&_p->data, &doc, &content_type); break; case DOC_LPIDF: res = create_lpidf_document(&_p->data, &doc, &content_type); break; case DOC_CPIM_PIDF: res = create_cpim_pidf_document(&_p->data, &doc, &content_type); break; case DOC_MSRTC_PIDF: case DOC_PIDF: default: res = create_pidf_document(&_p->data, &doc, &content_type); } if (res != 0) { LOG(L_ERR, "can't create presence document (%d)\n", _w->preferred_mimetype); return -2; } if (create_headers(_w, &headers, &content_type) < 0) { LOG(L_ERR, "send_presence_notify(): Error while adding headers\n"); str_free_content(&doc); str_free_content(&content_type); return -7; } if (!is_str_empty(&doc)) body = doc; /* res = tmb.t_request_within(¬ify, &headers, &body, _w->dialog, pa_notify_cb, cbd);*/ set_uac_req(&uac_r, ¬ify, &headers, &body, _w->dialog, TMCB_LOCAL_COMPLETED, pa_notify_cb, cbd ); res = tmb.prepare_request_within(&uac_r, dst); if (res < 0) { ERR("Can't send NOTIFY (%d) in dlg %.*s, %.*s, %.*s\n", res, FMT_STR(_w->dialog->id.call_id), FMT_STR(_w->dialog->id.rem_tag), FMT_STR(_w->dialog->id.loc_tag)); } str_free_content(&doc); str_free_content(&headers); str_free_content(&content_type); return res; } static int prepare_winfo_notify(struct retr_buf **dst, struct presentity* _p, struct watcher* _w, pa_notify_cb_param_t *cbd) { str doc = STR_NULL; str content_type = STR_NULL; str headers = STR_NULL; int res = 0; str body = STR_STATIC_INIT(""); uac_req_t uac_r; switch (_w->preferred_mimetype) { case DOC_WINFO: create_winfo_document(_p, _w, &doc, &content_type); DEBUG("winfo document created\n"); break; /* other formats ? */ default: ERR("unknow doctype\n"); return -1; } if (create_headers(_w, &headers, &content_type) < 0) { ERR("Error while adding headers\n"); str_free_content(&doc); str_free_content(&content_type); return -7; } if (!is_str_empty(&doc)) body = doc; /* res = tmb.t_request_within(¬ify, &headers, &body, _w->dialog, 0, 0); */ set_uac_req(&uac_r, ¬ify, &headers, &body, _w->dialog, TMCB_LOCAL_COMPLETED, pa_notify_cb, cbd ); res = tmb.prepare_request_within(&uac_r, dst); if (res < 0) { ERR("Can't send watcherinfo notification (%d)\n", res); } else { _w->document_index++; /* increment index for next document */ } str_free_content(&doc); str_free_content(&headers); str_free_content(&content_type); return res; } int prepare_unauthorized_notify(struct retr_buf **dst, struct presentity* _p, struct watcher* _w, pa_notify_cb_param_t *cbd) { str headers = STR_NULL; str body = STR_STATIC_INIT(""); int res; unc_req_t uac_r; /* send notifications to unauthorized (pending) watchers */ if (create_headers(_w, &headers, NULL) < 0) { LOG(L_ERR, "notify_unauthorized_watcher(): Error while adding headers\n"); return -7; } set_uac_req(&uac_r, ¬ify, &headers, &body, _w->dialog, TMCB_LOCAL_COMPLETED, pa_notify_cb, cbd ); res = tmb.prepare_request_within(&uac_r, dst); if (res < 0) { ERR("Can't send NOTIFY (%d) in dlg %.*s, %.*s, %.*s\n", res, FMT_STR(_w->dialog->id.call_id), FMT_STR(_w->dialog->id.rem_tag), FMT_STR(_w->dialog->id.loc_tag)); } str_free_content(&headers); return res; } int prepare_notify(struct retr_buf **dst, struct presentity* _p, struct watcher* _w) { int rc = 0; pa_notify_cb_param_t *cbd = NULL; /* alloc data for callback */ cbd = create_notify_cb_param(_p, _w); if (!cbd) { ERR("can't allocate data for callback\n"); /* FIXME: destroy subscription? */ return -1; } LOG(L_DBG, "notifying %.*s _p->flags=%x _w->event_package=%d _w->preferred_mimetype=%d _w->status=%d\n", _w->uri.len, _w->uri.s, _p->flags, _w->event_package, _w->preferred_mimetype, _w->status); if ((_w->status == WS_PENDING) || (_w->status == WS_PENDING_TERMINATED) || (_w->status == WS_REJECTED)) { rc = prepare_unauthorized_notify(dst, _p, _w, cbd); } else { switch (_w->event_package) { case EVENT_PRESENCE: rc = prepare_presence_notify(dst, _p, _w, cbd); break; case EVENT_PRESENCE_WINFO: rc = prepare_winfo_notify(dst, _p, _w, cbd); break; default: LOG(L_ERR, "sending notify for unknow package\n"); rc = -1; } } if ((rc < 0) && cbd) shm_free(cbd); /* ??? or not ??? */ else { /* At least dialog has changed (sometimes more - for example * version counter for winfo)! * Ignore errors there because the watcher need NOT to be in DB * (polling). */ if (use_db) db_update_watcher(_p, _w); /* if (use_db && (!is_watcher_terminated(_w))) db_update_watcher(_p, _w); */ } return rc; } int send_notify(struct presentity* _p, struct watcher* _w) { struct retr_buf *request; int res = prepare_notify(&request, _p, _w); if (res < 0) return res; tmb.send_prepared_request(request); return res; } int send_winfo_notify_offline(struct presentity* _p, struct watcher* _w, offline_winfo_t *info, transaction_cb completion_cb, void* cbp) { str doc = STR_NULL; str content_type = STR_NULL; str headers = STR_NULL; str body = STR_STATIC_INIT(""); uac_req_t uac_r; switch (_w->preferred_mimetype) { case DOC_WINFO: create_winfo_document_offline(_p, _w, info, &doc, &content_type); break; /* other formats ? */ default: ERR("send_winfo_notify: unknow doctype\n"); return -1; } if (create_headers(_w, &headers, &content_type) < 0) { ERR("send_winfo_notify(): Error while adding headers\n"); str_free_content(&doc); str_free_content(&content_type); return -7; } if (!is_str_empty(&doc)) body = doc; set_uac_req(&uac_r, ¬ify, &headers, &body, _w->dialog, TMCB_LOCAL_COMPLETED, completion_cb, cbp ); tmb.t_request_within(&uac_r); str_free_content(&doc); str_free_content(&headers); str_free_content(&content_type); _w->document_index++; /* increment index for next document */ if (use_db) db_update_watcher(_p, _w); /* dialog and index have changed */ return 0; } kamailio-4.0.4/obsolete/pa/pa_mod.h0000644000000000000000000000675712223032460015640 0ustar rootroot/* * Presence Agent, module interface * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PA_MOD_H #define PA_MOD_H #include "../../parser/msg_parser.h" #include "../../modules/tm/tm_load.h" #include "../../lib/srdb2/db.h" #include "../dialog/dlg_mod.h" #include "auth.h" /* we have to use something from this module */ #include "../xcap/xcap_mod.h" extern int default_expires; extern int max_subscription_expiration; /* max expires value for SUBSCRIBE */ extern int max_publish_expiration; /* max expires value for PUBLISH */ extern double default_priority; extern int timer_interval; /* TM bind */ extern struct tm_binds tmb; extern dlg_func_t dlg_func; /* DB module bind */ extern db_func_t pa_dbf; extern db_con_t* pa_db; extern fill_xcap_params_func fill_xcap_params; /* PA database */ extern int use_db; extern int use_place_table; extern str db_url; extern str pa_domain; extern char *presentity_table; extern char *presentity_contact_table; extern char *presentity_notes_table; extern char *extension_elements_table; extern char *watcherinfo_table; extern char *place_table; extern char *tuple_notes_table; extern char *tuple_extensions_table; /* columns in DB tables */ extern char *col_uri; extern char *col_pdomain; extern char *col_uid; extern char *col_pres_id; extern char *col_xcap_params; extern char *col_tupleid; extern char *col_basic; extern char *col_contact; extern char *col_etag; extern char *col_published_id; extern char *col_priority; extern char *col_expires; extern char *col_dbid; extern char *col_note; extern char *col_lang; extern char *col_element; extern char *col_status_extension; extern char *col_s_id; extern char *col_w_uri; extern char *col_package; extern char *col_status; extern char *col_display_name; extern char *col_accepts; extern char *col_event; extern char *col_dialog; extern char *col_server_contact; extern char *col_doc_index; extern char *col_watcher; extern char *col_events; extern char *col_domain; extern char *col_created_on; extern char *col_expires_on; extern int use_bsearch; extern int use_location_package; extern auth_params_t pa_auth_params; extern auth_params_t winfo_auth_params; extern int watcherinfo_notify; extern int use_callbacks; extern int subscribe_to_users; extern str pa_subscription_uri; extern int use_offline_winfo; extern char *offline_winfo_table; extern int ignore_408_on_notify; extern int notify_is_refresh; extern str pres_rules_file; /* filename for XCAP queries */ db_con_t* create_pa_db_connection(); void close_pa_db_connection(db_con_t* db); #endif /* PA_MOD_H */ kamailio-4.0.4/obsolete/pa/notify.h0000644000000000000000000000264212223032460015676 0ustar rootroot/* * Presence Agent, notifications * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NOTIFY_H #define NOTIFY_H #include "presentity.h" #include "watcher.h" #include "../../modules/tm/uac.h" int send_notify(struct presentity* _p, struct watcher* _w); /* prepares notify to be sent */ int prepare_notify(struct retr_buf **dst, struct presentity* _p, struct watcher* _w); int send_prepared_notify(struct retr_buf *request); #endif /* NOTIFY_H */ kamailio-4.0.4/obsolete/pa/subscribe.c0000644000000000000000000003433612223032460016347 0ustar rootroot/* * Presence Agent, subscribe handling * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2003-02-29 scratchpad compatibility abandoned * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) */ #include #include #include "../../str.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_event.h" #include "../../data_lump_rpl.h" #include "presentity.h" #include "watcher.h" #include "notify.h" #include "paerrno.h" #include "pdomain.h" #include "pa_mod.h" #include "ptime.h" #include "reply.h" #include "subscribe.h" #include "auth.h" #include #include #include #include #include #include "offline_winfo.h" #include "mimetypes.h" #include #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../globals.h" #include "../../md5.h" #include "../../crc.h" #include "../../ip_addr.h" #include "../../socket_info.h" #include "../../modules/tm/ut.h" #include "../../modules/tm/h_table.h" #include "../../modules/tm/t_hooks.h" #include "../../modules/tm/t_funcs.h" #include "../../modules/tm/t_msgbuilder.h" #include "../../modules/tm/callid.h" #include "../../modules/tm/uac.h" #define DOCUMENT_TYPE "application/cpim-pidf+xml" #define DOCUMENT_TYPE_L (sizeof(DOCUMENT_TYPE) - 1) /* * Extract plain uri -- return URI without parameters * The uri will be in form username@domain * */ static int extract_plain_uri(str* _uri) { struct sip_uri puri; int res = 0; if (parse_uri(_uri->s, _uri->len, &puri) < 0) { paerrno = PA_URI_PARSE; LOG(L_ERR, "extract_plain_uri(): Error while parsing URI\n"); return -1; } /* _uri->s = puri.user.s; if ((!_uri->s) || (puri.user.len < 1)) { _uri->s = puri.host.s; _uri->len = puri.host.len; return -1; }*/ if (puri.user.len < 1) { res = -1; /* it is uri without username ! */ } _uri->len = puri.host.s + puri.host.len - _uri->s; return res; } /* * Get presentity URI, which is stored in R-URI */ int get_pres_uri(struct sip_msg* _m, str* _puri) { if (_m->new_uri.s) { _puri->s = _m->new_uri.s; _puri->len = _m->new_uri.len; } else { _puri->s = _m->first_line.u.request.uri.s; _puri->len = _m->first_line.u.request.uri.len; } LOG(L_DBG, "get_pres_uri: _puri=%.*s\n", _puri->len, _puri->s); if (extract_plain_uri(_puri) < 0) { _puri->s = get_to(_m)->uri.s; _puri->len = get_to(_m)->uri.len; LOG(L_DBG, "get_pres_uri(2): _puri=%.*s\n", _puri->len, _puri->s); if (extract_plain_uri(_puri) < 0) { LOG(L_ERR, "get_pres_uri(): Error while extracting plain URI\n"); return -1; } } return 0; } static int get_watch_uri(struct sip_msg* _m, str* _wuri, str *_dn) { _wuri->s = get_from(_m)->uri.s; _wuri->len = get_from(_m)->uri.len; _dn->s = get_from(_m)->body.s; _dn->len = get_from(_m)->body.len; if (extract_plain_uri(_wuri) < 0) { LOG(L_ERR, "get_watch_uri(): Error while extracting plain URI\n"); return -1; } return 0; } static int get_dlg_id(struct sip_msg *_m, dlg_id_t *dst) { if (!dst) return -1; memset(dst, 0, sizeof(*dst)); if (_m->to) dst->loc_tag = ((struct to_body*)_m->to->parsed)->tag_value; if (_m->from) dst->rem_tag = ((struct to_body*)_m->from->parsed)->tag_value; if (_m->callid) dst->call_id = _m->callid->body; return 0; } static time_t get_expires(struct sip_msg *_m) { time_t e; if (_m->expires) e = ((exp_body_t*)_m->expires->parsed)->val; else e = default_expires; if (e > max_subscription_expiration) e = max_subscription_expiration; return e; } /* * Parse all header fields that will be needed * to handle a SUBSCRIBE request */ static int parse_hfs(struct sip_msg* _m, int accept_header_required) { int rc = 0; struct hdr_field *acc; /* EOH instead HDR_FROM_F | HDR_EVENT_F | HDR_EXPIRES_F | HDR_ACCEPT_F * because we need all Accept headers */ if ( (rc = parse_headers(_m, HDR_EOH_F, 0)) == -1) { paerrno = PA_PARSE_ERR; LOG(L_ERR, "parse_hfs(): Error while parsing headers: rc=%d\n", rc); return -1; } if (!_m->from) { ERR("From header missing\n"); return -1; } if (parse_from_header(_m) < 0) { paerrno = PA_FROM_ERR; LOG(L_ERR, "parse_hfs(): From malformed or missing\n"); return -6; } if (_m->event) { if (parse_event(_m->event) < 0) { /* can not parse Event header */ paerrno = PA_EVENT_PARSE; LOG(L_ERR, "parse_hfs(): Error while parsing Event header field\n"); return -8; } } else { /* no Event header -> bad message */ paerrno = PA_EVENT_PARSE; LOG(L_ERR, "parse_hfs(): Error while parsing Event header field\n"); return -8; } if (_m->expires) { if (parse_expires(_m->expires) < 0) { paerrno = PA_EXPIRES_PARSE; LOG(L_ERR, "parse_hfs(): Error while parsing Expires header field\n"); return -9; } } /* now look for Accept header */ acc = _m->accept; if (accept_header_required && (!acc)) { LOG(L_ERR, "no accept header\n"); return -11; } while (acc) { /* parse all accept headers */ if (acc->type == HDR_ACCEPT_T) { DBG("parsing accept header: %.*s\n", FMT_STR(acc->body)); if (parse_accept_body(acc) < 0) { paerrno = PA_ACCEPT_PARSE; LOG(L_ERR, "parse_hfs(): Error while parsing Accept header field\n"); return -10; } } acc = acc->next; } return 0; } /* returns 0 if package supported by PA */ static inline int verify_event_package(int et) { switch (et) { case EVENT_PRESENCE: return 0; case EVENT_PRESENCE_WINFO: if (watcherinfo_notify) return 0; else return -1; default: return -1; } } /* get_event MUST be parsed when calling get event -> done by parse_hfs! */ #define get_event(_m) ((event_t*)(_m->event->parsed))->parsed /* * Check if a message received has been constructed properly */ static int check_message(struct sip_msg* _m) { int eventtype = 0; int *accepts_mimes = NULL; event_mimetypes_t *em; struct hdr_field *acc; if ((!_m->event) || (!_m->event->parsed)) { paerrno = PA_EXPIRES_PARSE; ERR("Event header field not found\n"); return -1; /* should be verified in parse_hfs before */ } /* event package verification */ eventtype = get_event(_m); if (verify_event_package(eventtype) != 0) { INFO("Unsupported event package\n"); paerrno = PA_EVENT_UNSUPP; return -1; } em = find_event_mimetypes(eventtype); if (em) if (em->event_type == -1) em = NULL; if (!em) { paerrno = PA_EVENT_UNSUPP; ERR("Unsupported event package\n"); return -1; } acc = _m->accept; if (!acc) return 0; /* default will be used */ while (acc) { /* go through all Accept headers */ if (acc->type == HDR_ACCEPT_T) { /* it MUST be parsed from parse_hdr !!! */ accepts_mimes = acc->parsed; if (check_mime_types(accepts_mimes, em) == 0) return 0; /* else, none of the mimetypes accepted are generated for this event package */ INFO("Accepts %.*s not valid for event package et=%.*s\n", acc->body.len, acc->body.s, _m->event->body.len, _m->event->body.s); } acc = acc->next; } paerrno = PA_WRONG_ACCEPTS; ERR("no satisfactory document type found\n"); return -1; } static int create_watcher(struct sip_msg* _m, struct watcher** _w) { dlg_t* dialog; str server_contact = STR_NULL; int acc = 0; str watch_uri; str watch_dn; int et; time_t expires; et = get_event(_m); expires = get_expires(_m); if (expires) expires += act_time; if (get_watch_uri(_m, &watch_uri, &watch_dn) < 0) { paerrno = PA_URI_PARSE; ERR("Error while extracting watcher URI\n"); return -1; } acc = get_preferred_event_mimetype(_m, et); if (tmb.new_dlg_uas(_m, 200, &dialog) < 0) { paerrno = PA_DIALOG_ERR; ERR("Error while creating dialog state\n"); return -4; } if (extract_server_contact(_m, &server_contact, 1) != 0) { paerrno = PA_DIALOG_ERR; ERR("Error while extracting server contact\n"); return -3; } if (new_watcher_no_wb(&watch_uri, expires, et, acc, dialog, &watch_dn, &server_contact, NULL, _w) < 0) { ERR("Error while creating watcher\n"); tmb.free_dlg(dialog); if (server_contact.s) mem_free(server_contact.s); paerrno = PA_NO_MEMORY; return -5; } if (server_contact.s) mem_free(server_contact.s); (*_w)->flags |= WFLAG_SUBSCRIPTION_CHANGED; return 0; } static inline int add_rpl_expires(struct sip_msg *_m, watcher_t *w) { int i = 0; char tmp[64]; /* add expires header field into response */ if (w) i = w->expires - act_time; if (i < 0) i = 0; sprintf(tmp, "Expires: %d\r\n", i); if (!add_lump_rpl(_m, tmp, strlen(tmp), LUMP_RPL_HDR)) { ERR("Can't add Expires header to the response\n"); return -1; } return 0; } int handle_renewal_subscription(struct sip_msg* _m, struct pdomain *d) { struct presentity *p = NULL; struct watcher* w = NULL; str uid = STR_NULL; dlg_id_t dlg_id; int et, e; if (get_presentity_uid(&uid, _m) < 0) { ERR("Error while extracting presentity UID\n"); paerrno = PA_INTERNAL_ERROR; goto err; } /* needed to find watcher (better to set outside of crit. sect. due to * better performance) */ et = get_event(_m); get_dlg_id(_m, &dlg_id); lock_pdomain(d); if (find_presentity_uid(d, &uid, &p) != 0) { /* presentity not found */ INFO("resubscription to nonexisting presentity %.*s\n", FMT_STR(uid)); paerrno = PA_SUBSCRIPTION_NOT_EXISTS; goto err2; } if (find_watcher_dlg(p, &dlg_id, et, &w) != 0) { /* watcher not found */ INFO("resubscription for nonexisting watcher\n"); paerrno = PA_SUBSCRIPTION_NOT_EXISTS; goto err2; } e = get_expires(_m); if (e) e += act_time; update_watcher(p, w, e, _m); set_last_subscription_status(w->status); add_rpl_expires(_m, w); if (send_reply(_m) >= 0) { /* we have successfully sent the response */ if (send_notify(p, w) >= 0) { w->flags &= ~WFLAG_SUBSCRIPTION_CHANGED; /* notified */ /* remove terminated watcher otherwise he will * receive another NOTIFY generated from timer_pdomain */ if (is_watcher_terminated(w)) { remove_watcher(p, w); free_watcher(w); } } } else { ERR("Error while sending reply\n"); } unlock_pdomain(d); return 1; err2: unlock_pdomain(d); err: set_last_subscription_status(WS_REJECTED); if (send_reply(_m) < 0) ERR("Error while sending reply\n"); return -1; } presentity_t *get_presentity(struct sip_msg *_m, struct pdomain *d, int allow_creation) { str p_uri, uid; presentity_t *p = NULL; xcap_query_params_t xcap_params; /* presence_rules_t *auth_rules = NULL;*/ if (get_presentity_uid(&uid, _m) < 0) { ERR("Error while extracting presentity UID\n"); return NULL; } if (find_presentity_uid(d, &uid, &p) > 0) { if (allow_creation) { if (get_pres_uri(_m, &p_uri) < 0) { ERR("Error while extracting presentity URI\n"); } else { /* presentity not found -> create new presentity */ memset(&xcap_params, 0, sizeof(xcap_params)); if (fill_xcap_params) fill_xcap_params(_m, &xcap_params); if (new_presentity(d, &p_uri, &uid, &xcap_params, &p) < 0) ERR("Error while creating new presentity\n"); } } } return p; } int handle_new_subscription(struct sip_msg* _m, struct pdomain *d) { struct presentity *p; struct watcher* w; struct retr_buf *req; int is_terminated; if (create_watcher(_m, &w) < 0) { ERR("can't create watcher\n"); goto err; } lock_pdomain(d); p = get_presentity(_m, d, 1); if (!p) goto err3; /* authorize watcher */ w->status = authorize_watcher(p, w); switch (w->status) { case WS_REJECTED: unlock_pdomain(d); free_watcher(w); paerrno = PA_SUBSCRIPTION_REJECTED; INFO("watcher rejected\n"); goto err; case WS_PENDING: case WS_PENDING_TERMINATED: paerrno = PA_OK_WAITING_FOR_AUTH; default: break; } if (w->expires <= act_time) { set_watcher_terminated_status(w); is_terminated = 1; } else { is_terminated = 0; if (append_watcher(p, w, 1) < 0) { ERR("can't add watcher\n"); goto err3; } } if (prepare_notify(&req, p, w) < 0) { ERR("can't send notify\n"); goto err4; } set_last_subscription_status(w->status); add_rpl_expires(_m, w); unlock_pdomain(d); send_reply(_m); if (req) { tmb.send_prepared_request(req); w->flags &= ~WFLAG_SUBSCRIPTION_CHANGED; /* notified */ if (is_terminated) { free_watcher(w); w = NULL; } } return 1; err4: remove_watcher(p, w); err3: unlock_pdomain(d); free_watcher(w); paerrno = PA_INTERNAL_ERROR; err: set_last_subscription_status(WS_REJECTED); if (paerrno == PA_OK) paerrno = PA_INTERNAL_ERROR; if (send_reply(_m) < 0) ERR("Error while sending reply\n"); return -1; } /* * Handle a subscribe Request */ int handle_subscription(struct sip_msg* _m, char* _domain, char* _s2) { int res; PROF_START_DECL(pa_response_generation) PROF_START(pa_handle_subscription) get_act_time(); paerrno = PA_OK; if (parse_hfs(_m, 0) < 0) { ERR("Error while parsing message header\n"); goto error; } if (check_message(_m) < 0) { ERR("Error while checking message\n"); goto error; } if (has_to_tag(_m)) res = handle_renewal_subscription(_m, (struct pdomain*)_domain); else res = handle_new_subscription(_m, (struct pdomain*)_domain); PROF_STOP(pa_handle_subscription) return res; error: INFO("handle_subscription about to send_reply and return -2\n"); send_reply(_m); set_last_subscription_status(WS_REJECTED); PROF_STOP(pa_handle_subscription) return -1; } kamailio-4.0.4/obsolete/pa/presentity.c0000644000000000000000000003337412223032460016575 0ustar rootroot/* * Presence Agent, presentity structure and related functions * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2004 Jamey Hicks * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "../../lib/srdb2/db.h" #include "../../dprint.h" #include "../../id.h" #include "../../mem/shm_mem.h" #include "../../ut.h" #include "../../parser/parse_event.h" #include "paerrno.h" #include "dlist.h" #include "notify.h" #include "pdomain.h" #include "presentity.h" #include "ptime.h" #include "pa_mod.h" #include "qsa_interface.h" #include #include #include "async_auth.h" #include "tuple.h" #include "pres_notes.h" #include "extension_elements.h" extern int use_db; int auth_rules_refresh_time = 300; /* ----- helper functions ----- */ int get_presentity_uid(str *uid_dst, struct sip_msg *m) { /* Independently on implementation of get_to_uid this function * gets UID from the message. It never uses dynamic allocation of * data (better to use static buffer instead due to speed)! */ return get_to_uid(uid_dst, m); } void free_tuple_change_info_content(tuple_change_info_t *i) { str_free_content(&i->user); str_free_content(&i->contact); } int pres_uri2uid(str_t *uid_dst, const str_t *uri) { /* FIXME: convert uri to uid - used by internal subscriptions and fifo * commands - throw it out and use UUID only! */ struct sip_uri puri; str_clear(uid_dst); /* if (db_lookup_user(uri, uid_dst) == 0) return 0; */ /* else try the "hack" */ if (parse_uri(uri->s, uri->len, &puri) == -1) { LOG(L_ERR, "get_from_uid: Error while parsing From URI\n"); return -1; } str_dup(uid_dst, &puri.user); strlower(uid_dst); return 0; } /* ----- presentity functions ----- */ /* Create a new presentity but do not update database. * If pres_id not set it generates new one, but only if db_mode set. */ static inline int new_presentity_no_wb(struct pdomain *pdomain, str* _uri, str *uid, xcap_query_params_t *xcap_params, str *pres_id, presentity_t** _p) { presentity_t* presentity; int size = 0; dbid_t id; int id_len = 0; char *xcap_param_buffer; if ((!_uri) || (!_p) || (!uid)) { paerrno = PA_INTERNAL_ERROR; ERR("Invalid parameter value\n"); return -1; } if (pres_id) size += pres_id->len; else { if (use_db) { /* do not generate IDs if not using DB */ generate_dbid(id); id_len = dbid_strlen(id); size += id_len; } else id_len = 0; } if (xcap_params) size += get_inline_xcap_buf_len(xcap_params); size += sizeof(presentity_t) + _uri->len + uid->len; presentity = (presentity_t*)mem_alloc(size); /* TRACE("allocating presentity: %d\n", size); */ if (!presentity) { paerrno = PA_NO_MEMORY; LOG(L_ERR, "No memory left: size=%d\n", size); *_p = NULL; return -1; } /* fill whole structure with zeros */ memset(presentity, 0, sizeof(presentity_t)); msg_queue_init(&presentity->mq); presentity->data.uri.s = ((char*)presentity) + sizeof(presentity_t); str_cpy(&presentity->data.uri, _uri); presentity->uuid.s = presentity->data.uri.s + presentity->data.uri.len; str_cpy(&presentity->uuid, uid); presentity->pres_id.s = presentity->uuid.s + presentity->uuid.len; if (pres_id) str_cpy(&presentity->pres_id, pres_id); else { if (use_db) dbid_strcpy(&presentity->pres_id, id, id_len); else presentity->pres_id.len = 0; } xcap_param_buffer = after_str_ptr(&presentity->pres_id); presentity->pdomain = pdomain; if (pa_auth_params.type == auth_xcap) { /* store XCAP parameters for async XCAP queries and refreshing * (FIXME: rewrite - use table of a few of existing XCAP parameter * sets instead of always duplicating because it will be mostly * the same!) */ if (dup_xcap_params_inline(&presentity->xcap_params, xcap_params, xcap_param_buffer) < 0) { ERR("can't duplicate XCAP parameters\n"); shm_free(presentity); *_p = NULL; return -1; } } if (ask_auth_rules(presentity) < 0) { /* try it from timer again if fails here */ presentity->auth_rules_refresh_time = act_time; } else presentity->auth_rules_refresh_time = act_time + auth_rules_refresh_time; *_p = presentity; /* add presentity into domain */ add_presentity(pdomain, *_p); return 0; } static inline int db_add_presentity(presentity_t* presentity) { db_key_t query_cols[6]; db_val_t query_vals[6]; int n_query_cols = 0; int res; str str_xcap_params; query_cols[0] = col_uri; query_vals[0].type = DB_STR; query_vals[0].nul = 0; query_vals[0].val.str_val = presentity->data.uri; n_query_cols++; query_cols[n_query_cols] = col_pdomain; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = *presentity->pdomain->name; n_query_cols++; query_cols[n_query_cols] = col_uid; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = presentity->uuid; n_query_cols++; query_cols[n_query_cols] = col_pres_id; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = presentity->pres_id; n_query_cols++; /*query_cols[n_query_cols] = "id_cntr"; query_vals[n_query_cols].type = DB_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val = presentity->id_cntr; n_query_cols++;*/ /* store XCAP parameters with presentity */ if (xcap_params2str(&str_xcap_params, &presentity->xcap_params) != 0) { LOG(L_ERR, "Error while serializing xcap params\n"); return -1; } query_cols[n_query_cols] = col_xcap_params; query_vals[n_query_cols].type = DB_BLOB; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.blob_val = str_xcap_params; n_query_cols++; res = 0; if (pa_dbf.use_table(pa_db, presentity_table) < 0) { ERR("Error in use_table\n"); res = -1; } /* insert new record into database */ if (res == 0) { if (pa_dbf.insert(pa_db, query_cols, query_vals, n_query_cols) < 0) { ERR("Error while inserting presentity into DB\n"); res = -1; } } str_free_content(&str_xcap_params); return res; } /* * Create a new presentity */ int new_presentity(struct pdomain *pdomain, str* _uri, str *uid, xcap_query_params_t *params, presentity_t** _p) { int res = 0; res = new_presentity_no_wb(pdomain, _uri, uid, params, NULL, _p); if (res != 0) return res; if (use_db) { if (db_add_presentity(*_p) != 0) { paerrno = PA_INTERNAL_ERROR; free_presentity(*_p); *_p = NULL; return -1; } } return res; } /* Removes all data for presentity (tuples, watchers, tuple notes, ...) * from database. It is possible due to that pres_id is unique identifier * common for all tables */ int db_remove_presentity_data(presentity_t* presentity, const char *table) { db_key_t keys[] = { col_pres_id }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = presentity->pres_id } } }; if (!use_db) return 0; if (pa_dbf.use_table(pa_db, table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.delete(pa_db, keys, ops, k_vals, 1) < 0) { LOG(L_ERR, "Error while querying presentity\n"); return -1; } return 0; } static inline int db_remove_presentity(presentity_t* presentity) { int res = 0; if (!use_db) return 0; res = db_remove_presentity_data(presentity, presentity_contact_table); res = db_remove_presentity_data(presentity, tuple_notes_table) | res; res = db_remove_presentity_data(presentity, watcherinfo_table) | res; res = db_remove_presentity_data(presentity, presentity_notes_table) | res; res = db_remove_presentity_data(presentity, extension_elements_table) | res; res = db_remove_presentity_data(presentity, presentity_table) | res; return res; } void release_presentity(presentity_t *_p) { /* remove presentity from DB and free its memory */ if (_p) { db_remove_presentity(_p); free_presentity(_p); } } void free_presentity(presentity_t* _p) { watcher_t *w, *nw; presence_tuple_t *tuple, *t; internal_pa_subscription_t *iw, *niw; pa_presence_note_t *n, *nn; pa_extension_element_t *e, *ne; /* remove presentity from domain */ remove_presentity(_p->pdomain, _p); /* watchers should be released already */ w = _p->first_watcher; while (w) { nw = w->next; free_watcher(w); w = nw; } w = _p->first_winfo_watcher; while (w) { nw = w->next; free_watcher(w); w = nw; } t = get_first_tuple(_p); while (t) { tuple = t; t = (presence_tuple_t*)t->data.next; free_presence_tuple(tuple); } iw = _p->first_qsa_subscription; while (iw) { niw = iw->next; free_internal_subscription(iw); iw = niw; } /* remove notes */ n = (pa_presence_note_t*)_p->data.first_note; while (n) { nn = (pa_presence_note_t*)n->data.next; free_pres_note(n); n = nn; } /* remove extension_elements */ e = (pa_extension_element_t*)_p->data.first_unknown_element; while (e) { ne = (pa_extension_element_t*)e->data.next; free_pa_extension_element(e); e = ne; } if (_p->authorization_info) { free_pres_rules(_p->authorization_info); } /* XCAP params are allocated "inline" -> no free needed - it will * be freed with whole structure */ /* free_xcap_params_content(&_p->xcap_params); */ msg_queue_destroy(&_p->mq); mem_free(_p); } int pdomain_load_presentities(pdomain_t *pdomain) { if (!use_db) return 0; db_key_t query_cols[1]; db_op_t query_ops[1]; db_val_t query_vals[1]; db_key_t result_cols[8]; db_res_t *res; int n_query_cols = 0; int n_result_cols = 0; int uri_col; int presid_col; int uid_col; int xcap_col; int i; presentity_t *presentity = NULL; db_con_t* db = create_pa_db_connection(); /* must create its own connection (called before child init)! */ if (!db) { ERR("Can't load presentities - no DB connection\n"); return -1; } act_time = time(NULL); /* needed for fetching auth rules, ... */ query_cols[n_query_cols] = col_pdomain; query_ops[n_query_cols] = OP_EQ; query_vals[n_query_cols].type = DB_STR; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.str_val = *pdomain->name; n_query_cols++; result_cols[uri_col = n_result_cols++] = col_uri; result_cols[presid_col = n_result_cols++] = col_pres_id; result_cols[uid_col = n_result_cols++] = col_uid; result_cols[xcap_col = n_result_cols++] = col_xcap_params; if (pa_dbf.use_table(db, presentity_table) < 0) { LOG(L_ERR, "pdomain_load_presentities: Error in use_table\n"); close_pa_db_connection(db); return -1; } if (pa_dbf.query (db, query_cols, query_ops, query_vals, result_cols, n_query_cols, n_result_cols, 0, &res) < 0) { LOG(L_ERR, "pdomain_load_presentities: Error while querying presentity\n"); close_pa_db_connection(db); return -1; } if (res) { for (i = 0; i < res->n; i++) { /* fill in tuple structure from database query result */ db_row_t *row = &res->rows[i]; db_val_t *row_vals = ROW_VALUES(row); str uri = STR_NULL; str pres_id = STR_NULL; str uid = STR_NULL; str serialized_xcap_params = STR_NULL; xcap_query_params_t xcap_params; if (!row_vals[uri_col].nul) { uri.s = (char *)row_vals[uri_col].val.string_val; uri.len = strlen(uri.s); } if (!row_vals[uid_col].nul) { uid.s = (char *)row_vals[uid_col].val.string_val; uid.len = strlen(uid.s); } if (!row_vals[presid_col].nul) { pres_id.s = (char *)row_vals[presid_col].val.string_val; pres_id.len = strlen(pres_id.s); } if (!row_vals[xcap_col].nul) { serialized_xcap_params.s = (char *)row_vals[xcap_col].val.string_val; serialized_xcap_params.len = strlen(serialized_xcap_params.s); } DBG("pdomain_load_presentities: pdomain=%.*s presentity uri=%.*s presid=%.*s\n", pdomain->name->len, pdomain->name->s, uri.len, uri.s, pres_id.len, pres_id.s); str2xcap_params(&xcap_params, &serialized_xcap_params); new_presentity_no_wb(pdomain, &uri, &uid, &xcap_params, &pres_id, &presentity); free_xcap_params_content(&xcap_params); } pa_dbf.free_result(db, res); } for (presentity = pdomain->first; presentity; presentity = presentity->next) { db_read_watcherinfo(presentity, db); db_read_tuples(presentity, db); db_read_notes(presentity, db); db_read_extension_elements(presentity, db); } close_pa_db_connection(db); return 0; } int set_auth_rules(presentity_t *p, presence_rules_t *new_auth_rules) { watcher_t *w; watcher_status_t s; /* ! call from locked region only ! */ /* INFO("setting auth rules\n"); */ if (p->authorization_info) { free_pres_rules(p->authorization_info); /* free old rules */ } p->authorization_info = new_auth_rules; /* reauthorize all watchers (but NOT winfo watchers - not needed * now because we have only "implicit" auth. rules for them) */ w = p->first_watcher; while (w) { s = authorize_watcher(p, w); if (w->status != s) { /* status has changed */ w->status = s; w->flags |= WFLAG_SUBSCRIPTION_CHANGED; p->flags |= PFLAG_WATCHERINFO_CHANGED; /* NOTIFYs, terminating, ... wil be done in timer */ } w = w->next; } return 0; } kamailio-4.0.4/obsolete/pa/dlist.c0000644000000000000000000001207512223032460015501 0ustar rootroot/* * Presence Agent, domain list * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "dlist.h" #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "../../sr_module.h" #include "../../ut.h" #include "paerrno.h" #include #include "ptime.h" #include "presentity.h" /* * List of all registered domains */ dlist_t* root = 0; /* * Find domain with the given name * Returns 0 if the domain was found * and 1 of not */ static inline int find_dlist(str* _n, dlist_t** _d) { dlist_t* ptr; ptr = root; while(ptr) { if ((_n->len == ptr->name.len) && !memcmp(_n->s, ptr->name.s, _n->len)) { *_d = ptr; return 0; } ptr = ptr->next; } return 1; } /* * Create a new domain structure * Returns 0 if everything went OK, otherwise value < 0 * is returned * * The structure is NOT created in shared memory so the * function must be called before ser forks if it should * be available to all processes */ static inline int new_dlist(str* _n, dlist_t** _d) { cmd_function reg; cmd_function unreg; dlist_t* ptr; /* Domains are created before ser forks, * so we can create them using pkg_malloc */ ptr = (dlist_t*)mem_alloc(sizeof(dlist_t)); if (ptr == 0) { paerrno = PA_NO_MEMORY; LOG(L_ERR, "new_dlist(): No memory left\n"); return -1; } memset(ptr, 0, sizeof(dlist_t)); ptr->name.s = (char*)mem_alloc(_n->len); if (ptr->name.s == 0) { paerrno = PA_NO_MEMORY; LOG(L_ERR, "new_dlist(): No memory left 2\n"); mem_free(ptr); return -2; } memcpy(ptr->name.s, _n->s, _n->len); ptr->name.len = _n->len; if ((_n->len == 9) && (!strncasecmp(_n->s, "registrar", 9))) { reg = find_export("ul_register_watcher", 1, 0); if (reg == 0) { LOG(L_ERR, "new_dlist(): ~ul_register_watcher not found\n"); return -3; } unreg = find_export("ul_unregister_watcher", 1, 0); if (unreg == 0) { LOG(L_ERR, "new_dlist(): ~ul_unregister_watcher not found\n"); return -4; } } else if ((_n->len == 6) && (!strncasecmp(_n->s, "jabber", 6))) { reg = find_export("jab_register_watcher", 1, 0); if (reg == 0) { LOG(L_ERR, "new_dlist(): jab_register_watcher not found\n"); return -5; } unreg = find_export("jab_unregister_watcher", 1, 0); if (unreg == 0) { LOG(L_ERR, "new_dlist(): jab_unregister_watcher not found\n"); return -6; } } else { LOG(L_ERR, "new_dlist(): Unknown module to bind: %.*s\n", _n->len, ZSW(_n->s)); return -7; } if (new_pdomain(&(ptr->name), 512, &(ptr->d), (register_watcher_t)reg, (unregister_watcher_t)unreg) < 0) { LOG(L_ERR, "new_dlist(): Error while creating domain structure\n"); mem_free(ptr->name.s); mem_free(ptr); return -8; } *_d = ptr; return 0; } int find_pdomain(const char* _n, pdomain_t** _d) { dlist_t* d; str s; s.s = (char*)_n; s.len = strlen(_n); if (find_dlist(&s, &d) == 0) { *_d = d->d; return 0; } return 1; } /* * Function registers a new domain with presence agent * if the domain exists, pointer to existing structure * will be returned, otherwise a new domain will be * created */ int register_pdomain(const char* _n, pdomain_t** _d) { pdomain_t *pdomain; dlist_t* d; str s; s.s = (char*)_n; s.len = strlen(_n); if (find_dlist(&s, &d) == 0) { *_d = d->d; return 0; } if (new_dlist(&s, &d) < 0) { LOG(L_ERR, "register_pdomain(): Error while creating new domain\n"); return -1; } pdomain = d->d; lock_pdomain(pdomain); /* do not enable timer to delete presentities in it */ d->next = root; root = d; *_d = pdomain; /* Preload domain with data from database if we are gonna * to use database */ pdomain_load_presentities(pdomain); unlock_pdomain(pdomain); return 0; } /* * Free all allocated memory */ void free_all_pdomains(void) { dlist_t* ptr; while(root) { ptr = root; root = root->next; free_pdomain(ptr->d); mem_free(ptr->name.s); mem_free(ptr); } } /* * Run timer handler of all domains */ int timer_all_pdomains(void) { int res = 0; dlist_t* ptr; get_act_time(); /* Get and save actual time */ ptr = root; while(ptr) { res |= timer_pdomain(ptr->d); ptr = ptr->next; } return res; } kamailio-4.0.4/obsolete/pa/qsa_interface.c0000644000000000000000000001737712223032460017200 0ustar rootroot#include "qsa_interface.h" #include "pdomain.h" #include "pa_mod.h" #include "dlist.h" #include "auth.h" #include #include #include #include #include #include static notifier_domain_t *domain = NULL; static notifier_t *notifier = NULL; static qsa_content_type_t *ct_presence_info = NULL; /*static qsa_content_type_t *ct_pidf_xml = NULL;*/ /* static str_t notifier_name = { s: "pa", len: 2 }; */ static int pa_subscribe(notifier_t *n, qsa_subscription_t *subscription); static void pa_unsubscribe(notifier_t *n, qsa_subscription_t *subscription); extern dlist_t* root; /* ugly !!!!! */ int accept_internal_subscriptions = 0; /* QSA interface initialization */ int pa_qsa_interface_init() { static str presence_info = STR_STATIC_INIT(CT_PRESENCE_INFO); static str_t presence_package = { s: "presence", len: 8 }; domain = qsa_get_default_domain(); if (!domain) { ERR("can't register notifier domain\n"); return -1; } /* DBG("QSA (pa) domain: %p\n", domain); */ notifier = register_notifier(domain, &presence_package, pa_subscribe, pa_unsubscribe, NULL); if (!notifier) { ERR("can't register notifier\n"); return -1; } ct_presence_info = register_content_type(domain, &presence_info, (destroy_function_f)free_presentity_info); if (!ct_presence_info) { ERR("can't register QSA content type\n"); return -1; } else TRACE("PA_CONTENT_TYPE: %p\n", ct_presence_info); /* DBG("pa_qsa_interface_init(): created notifier %.*s\n", FMT_STR(notifier_name)); */ return 0; } void pa_qsa_interface_destroy() { if (domain && notifier) unregister_notifier(domain, notifier); notifier = NULL; /* no new qsa subscriptions will be created now - now can be * released all existing ones */ if (domain) qsa_release_domain(domain); /* no QSA operations should be done there (don't send * notification messages, ...) only subscriptions may * be released */ domain = NULL; } /* notifier functions */ static int add_internal_subscription(presentity_t *p, internal_pa_subscription_t *is) { if (is->status != WS_ACTIVE) is->status = authorize_internal_watcher(p, is); if (is->status == WS_REJECTED) return -1; DOUBLE_LINKED_LIST_ADD(p->first_qsa_subscription, p->last_qsa_subscription, is); return 0; } internal_pa_subscription_t *create_internal_subscription(qsa_subscription_t *s) { internal_pa_subscription_t *ss = cds_malloc(sizeof(internal_pa_subscription_t)); if (!ss) return ss; ss->subscription = s; ss->status = WS_PENDING; ss->prev = NULL; ss->next = NULL; return ss; } void free_internal_subscription(internal_pa_subscription_t *is) { if (is) cds_free(is); } static int pa_subscribe(notifier_t *n, qsa_subscription_t *subscription) { dlist_t *dl; presentity_t *p = NULL; internal_pa_subscription_t *ss; str uid = STR_NULL; str *record_id = NULL; xcap_query_params_t xcap_params; if (!accept_internal_subscriptions) return 0; /* do not accept subscriptions */ record_id = get_record_id(subscription); if (!record_id) { ERR("BUG: subscription to empty record\n"); return -1; } DBG("SUBSCRIBE to PA for %.*s [%.*s]\n", FMT_STR(*record_id), FMT_STR(subscription->package->name)); if (pres_uri2uid(&uid, record_id) != 0) { /* can't convert uri to uid */ INFO("can't convert URI to UID for internal PA subscription\n"); return -1; } /* DBG("SUBSCRIBE to uid: %.*s\n", FMT_STR(uid)); */ dl = root; /* FIXME: ugly and possibly unsafe (locking needed?) */ while (dl) { /* create new server subscription */ ss = create_internal_subscription(subscription); if (!ss) { ERROR_LOG("can't allocate memory for internal pa subscription\n"); break; } lock_pdomain(dl->d); if (find_presentity_uid(dl->d, &uid, &p) != 0) p = NULL; if (!p) { memset(&xcap_params, 0, sizeof(xcap_params)); if (fill_xcap_params) fill_xcap_params(NULL, &xcap_params); if (new_presentity(dl->d, record_id, &uid, &xcap_params, &p) < 0) { ERR("can't create presentity\n"); } } if (p) { /* add server subscription to p */ if (add_internal_subscription(p, ss) == 0) { p->flags |= PFLAG_WATCHERINFO_CHANGED; notify_internal_watcher(p, ss); } else { /* error adding subscription to p (auth failed, ...) */ free_internal_subscription(ss); } } unlock_pdomain(dl->d); dl = dl->next; } str_free_content(&uid); DBG("finished SUBSCRIBE to PA for %.*s [%.*s]\n", FMT_STR(*record_id), FMT_STR(subscription->package->name)); return 0; } static void remove_internal_subscription(presentity_t *p, qsa_subscription_t *s) { internal_pa_subscription_t *ss; ss = p->first_qsa_subscription; while (ss) { if (s == ss->subscription) { DOUBLE_LINKED_LIST_REMOVE(p->first_qsa_subscription, p->last_qsa_subscription, ss); free_internal_subscription(ss); break; /* may be only once */ } ss = ss->next; } } static void pa_unsubscribe(notifier_t *n, qsa_subscription_t *subscription) { dlist_t *dl; presentity_t *p = NULL; str uid = STR_NULL; str *record_id = NULL; if (!accept_internal_subscriptions) return; /* do not accept subscriptions */ record_id = get_record_id(subscription); if (!record_id) { ERR("BUG: unsubscription to empty record\n"); return; } if (pres_uri2uid(&uid, record_id) != 0) { /* can't convert uri to uid */ ERR("can't convert URI to UID for internal PA unsubscription\n"); return; } /* DBG("UNBSCRIBE from PA for %.*s [%.*s]\n", FMT_STR(subscription->record_id), FMT_STR(subscription->package->name)); */ dl = root; /* FIXME: ugly and possibly unsafe (locking needed?) */ while (dl) { lock_pdomain(dl->d); if (find_presentity_uid(dl->d, &uid, &p) != 0) p = NULL; if (!p) continue; remove_internal_subscription(p, subscription); p->flags |= PFLAG_WATCHERINFO_CHANGED; unlock_pdomain(dl->d); dl = dl->next; } str_free_content(&uid); } int notify_internal_watcher(presentity_t *p, internal_pa_subscription_t *ss) { presentity_info_t *pinfo; /* notify only accepted watchers */ switch (ss->status) { case WS_PENDING: return notify_subscriber(ss->subscription, notifier, ct_presence_info, NULL, qsa_subscription_pending); case WS_REJECTED: return notify_subscriber(ss->subscription, notifier, ct_presence_info, NULL, qsa_subscription_rejected); case WS_PENDING_TERMINATED: case WS_TERMINATED: return notify_subscriber(ss->subscription, notifier, ct_presence_info, NULL, qsa_subscription_terminated); case WS_ACTIVE: pinfo = dup_presentity_info(&p->data); if (!pinfo) { ERROR_LOG("can't create presentity info from presentity!\n"); return -1; } return notify_subscriber(ss->subscription, notifier, ct_presence_info, pinfo, qsa_subscription_active); } return 0; } int notify_qsa_watchers(presentity_t *p) { internal_pa_subscription_t *ss; int res = 0; /* DBG("notify_qsa_watchers for %.*s\n", FMT_STR(p->uri)); */ ss = p->first_qsa_subscription; while (ss) { if (notify_internal_watcher(p, ss) < 0) res = -1; ss = ss->next; } return res; } int subscribe_to_user(presentity_t *_p) { static str package = STR_STATIC_INIT("presence"); clear_subscription_data(&_p->presence_subscription_data); /* ??? FIXME msg queue */ _p->presence_subscription_data.dst = &_p->mq; _p->presence_subscription_data.record_id = _p->data.uri; _p->presence_subscription_data.subscriber_id = pa_subscription_uri; _p->presence_subscription_data.subscriber_data = _p; _p->presence_subscription = subscribe(domain, &package, &_p->presence_subscription_data); if (_p->presence_subscription) return 0; else return -1; } int unsubscribe_to_user(presentity_t *_p) { unsubscribe(domain, _p->presence_subscription); /* TODO ? clean messages ? they will be freed automaticaly ... */ return 0; } kamailio-4.0.4/obsolete/pa/message.h0000644000000000000000000000021612223032460016005 0ustar rootroot#ifndef __MESSAGE_H #define __MESSAGE_H /* message processing */ int authorize_message(struct sip_msg* _m, char* _filename, char*); #endif kamailio-4.0.4/obsolete/pa/tuple_extensions.c0000644000000000000000000001123312223032460017765 0ustar rootroot#include "presentity.h" #include "pa_mod.h" #include "tuple_extensions.h" #include /* DB manipulation */ static int db_add_tuple_extension(presentity_t *p, presence_tuple_t *t, extension_element_t *n, int is_status_extension) { db_key_t cols[20]; db_val_t vals[20]; int n_updates = 0; if (!use_db) return 0; /* set data */ cols[n_updates] = col_pres_id; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = p->pres_id; n_updates++; cols[n_updates] = col_tupleid; vals[n_updates].type = DB_STR; vals[n_updates].nul = 0; vals[n_updates].val.str_val = t->data.id; n_updates++; cols[n_updates] = col_element; vals[n_updates].type = DB_BLOB; vals[n_updates].nul = 0; vals[n_updates].val.blob_val = n->element; n_updates++; cols[n_updates] = col_status_extension; vals[n_updates].type = DB_INT; vals[n_updates].nul = 0; vals[n_updates].val.int_val = is_status_extension; n_updates++; /* run update */ if (pa_dbf.use_table(pa_db, tuple_extensions_table) < 0) { LOG(L_ERR, "db_add_pres_note: Error in use_table\n"); return -1; } if (pa_dbf.insert(pa_db, cols, vals, n_updates) < 0) { LOG(L_ERR, "db_add_pres_note: Can't insert record\n"); return -1; } return 0; } int db_add_tuple_extensions(presentity_t *p, presence_tuple_t *t) { extension_element_t *n; n = t->data.first_unknown_element; while (n) { db_add_tuple_extension(p, t, n, 0); n = n->next; } n = t->data.status.first_unknown_element; while (n) { db_add_tuple_extension(p, t, n, 1); n = n->next; } return 0; } int db_remove_tuple_extensions(presentity_t *p, presence_tuple_t *t) { db_key_t keys[] = { col_pres_id, col_tupleid }; db_op_t ops[] = { OP_EQ, OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = p->pres_id } }, { DB_STR, 0, { .str_val = t->data.id } } }; if (!use_db) return 0; if (pa_dbf.use_table(pa_db, tuple_extensions_table) < 0) { LOG(L_ERR, "db_remove_tuple_extensions: Error in use_table\n"); return -1; } if (pa_dbf.delete(pa_db, keys, ops, k_vals, 2) < 0) { LOG(L_ERR, "db_remove_tuple_extensions: Can't delete record\n"); return -1; } return 0; } int db_update_tuple_extensions(presentity_t *p, presence_tuple_t *t) { db_remove_tuple_extensions(p, t); db_add_tuple_extensions(p, t); return 0; } int db_read_tuple_extensions(presentity_t *p, presence_tuple_t *t, db_con_t* db) { db_key_t keys[] = { col_pres_id, col_tupleid }; db_op_t ops[] = { OP_EQ, OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = p->pres_id } }, { DB_STR, 0, { .str_val = t->data.id } } }; int i; int r = 0; db_res_t *res = NULL; db_key_t result_cols[] = { col_element, col_status_extension }; if (!use_db) return 0; if (pa_dbf.use_table(db, tuple_extensions_table) < 0) { LOG(L_ERR, "db_read_tuple_extensions: Error in use_table\n"); return -1; } if (pa_dbf.query (db, keys, ops, k_vals, result_cols, 2, sizeof(result_cols) / sizeof(db_key_t), 0, &res) < 0) { LOG(L_ERR, "db_read_tuple_extensions(): Error while querying extensions\n"); return -1; } if (!res) return 0; /* ? */ for (i = 0; i < res->n; i++) { extension_element_t *n = NULL; db_row_t *row = &res->rows[i]; db_val_t *row_vals = ROW_VALUES(row); str element = STR_NULL; int status_extension = 0; #define get_str_val(i,dst) do{if(!row_vals[i].nul){dst.s=(char*)row_vals[i].val.string_val;dst.len=strlen(dst.s);}}while(0) #define get_int_val(rvi,dst) do{if(!rvi.nul){dst=rvi.val.int_val;}else dst=0;}while(0) get_str_val(0, element); get_int_val(row_vals[1], status_extension); #undef get_str_val #undef get_int_val n = create_extension_element(&element); if (n) add_tuple_extension_no_wb(t, n, status_extension); } pa_dbf.free_result(db, res); return r; } /* in memory operations */ void add_tuple_extension_no_wb(presence_tuple_t *t, extension_element_t *n, int is_status_extension) { if (is_status_extension) DOUBLE_LINKED_LIST_ADD(t->data.status.first_unknown_element, t->data.status.last_unknown_element, n); else DOUBLE_LINKED_LIST_ADD(t->data.first_unknown_element, t->data.last_unknown_element, n); } void free_tuple_extensions(presence_tuple_t *t) { extension_element_t *n, *nn; /* remove all extensions for this tuple */ n = t->data.first_unknown_element; while (n) { nn = n->next; free_extension_element(n); n = nn; } /* remove all status extensions for this tuple */ n = t->data.status.first_unknown_element; while (n) { nn = n->next; free_extension_element(n); n = nn; } t->data.first_unknown_element = NULL; t->data.last_unknown_element = NULL; t->data.status.first_unknown_element = NULL; t->data.status.last_unknown_element = NULL; } kamailio-4.0.4/obsolete/pa/reply.h0000644000000000000000000000230512223032460015515 0ustar rootroot/* * Presence Agent, reply building * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef REPLY_H #define REPLY_H #include "../../parser/msg_parser.h" /* * Send a reply */ int send_reply(struct sip_msg* _m); #endif /* REPLY_H */ kamailio-4.0.4/obsolete/pa/ChangeLog0000644000000000000000000001756712223032460016003 0ustar rootroot2006-01-12 * updated module documentation 2006-12-14 * corrected typing error in column name * corrected bug - tuple extensions were not stored into database * corrected XMLRPC - after changes in xmlrpc module CDATA sections didn't work as before and had to be thrown out 2006-12-13 * database column names are stored in variables and configurable via module parameters 2006-12-07 * updated doc to be compilable standalone 2006-11-06 * bug correction - noninitialized variable in ask_auth_rules and added return code from xcap_get_pres_rules verification - reported and patched by Bogdan 2006-04-09 * corrected bug (failed prepare_notify in handle_new_subscription caused watcher destroying but he wasn't removed from presentity's watcher list) 2006-03-09 * updated DB manipulation functions 2006-07-26 * first step for PIDF extensions support (storing/loading into/from DB not finished yet) 2006-07-24 * litle xcap_params optimization (allocation together with parent structure) 2006-07-21 * watcher lists replaced by double linked lists (better for remove) * db schema updates 2006-07-20 * get_presentity_uid doesn't allocate memory * removed db_update_presentity (only one - ineffective - call replaced by call to db_update_presence_tuple) * improved process_tuple_change - two possible DB updates reduced to one * clarified add_watcher/remove_watcher functions * removed notify_all_watchers, notify_all_winfo_watchers (not called) * removed common.h (function str_append was moved into CDS library is it needed?) 2006-07-19 * splitted code from presentity.c and presentity.h into smaller pieces (pres_timer.c/h, tuple.c/h, ...) * finished structures rewriting (joined PA structures with structures from presence library -> no translations between them for presence doc creation) 2006-07-12 * improved and tested dialog refreshing (contacts in NOTIFY responses and renewal SUBSCRIBE requests) * ??? PA structures contains common presence structures 2006-07-10 * added parameter async_auth_queries for switching between sync/async loading of auth rules (synchronous dowload is done from inside of critical section locked by pdomain mutext, which is not good for performance) * added parameter max_auth_requests_per_tick for async. auth queries * added parameter auth_rules_refresh_time for control how often are auth. rules refreshed (we have no "XCAP change" notifications) - it could be better than refresh on resubscription (criticised) 2006-07-07 * asynchronous loading of authorization rules (better response than synchronous) 2006-07-04 * removed unused parts from code (old which were rewritten; location related which need to be completely rewritten; resource lists which are better in RLS module) * cosmetical changes 2006-07-03 * improved handling new subscription - most work moved outside of critical secion 2006-06-30 * added short description of state aggregation 2006-06-29 * rearranged work with mimetypes * cleaned code of PA: - less tests - most left on parse_hfs and check_message - separated handling renewal and new subscription (better performance?) - removed location stuff 2006-05-25 * added Max-Forwards header into NOTIFY requests 2006-05-15 * added reaction on failed NOTIFY into non-winfo subscriptions * added parameter ignore_408_on_notify 2006-05-10 * corrected bug in PUBLISH handling (refreshing state with empty body destroyed notes and person elements and did not store tuples into DB) * corrected RPC function pa.publish (accepts empty file for status refreshes) 2006-05-09 * removed unused parameter xcap_root 2006-04-20 * added uid and uri into error log for "resubscription to nonexisting presentity" * rpc_trace doesn't put empty strings as separators (more readable using new serctl) 2006-04-18 * added profiling 2006-04-13 * added parameters subscribe_to_users and pa_subscription_uri (prepared for subscriptions to clients) 2006-04-12 * added missing parsing From header into message authorization function 2006-04-11 * changed QSA (reduced memory allocations) * changed parameter of "authorize_message" function (filename with im-rules instead of XCAP root) 2006-04-10 * changed QSA - corrected content-type propagation 2006-04-07 * added function check_subscription_status and removed AVP operations due to problems with them (after sending NOTIFY in handle_subscription are AVPs lost!) * store_offline_winfo and check_subscription_status uses global variable with last subscription status instead of AVP (see above) * added parameter pres_rules_file which specifies filename used in XCAP queries instead of default presence-rules.xml 2006-04-06 * xcap queries moved into xcap module 2006-04-04 * sending empty string in NOTIFY bodies - TCP requires content-length and if sending message with NULL body, it is not set * handle_subscription sets AVP subscription_status (pending, terminated, error) * store_offline_winfo stores status from AVP subscription_status 2006-04-03 * function for server contact extraction was moved into common libraries (it is used by RLS too) 2006-03-29 * added function for MESSAGE authorization 2006-03-28 * added parameter max_publish_expiration (max time for expiration of information published via PUBLISH request) 2006-03-27 * added (empty) function for message authorization * corrected bug in offline watcherinfo dumping (removed were messages with id less or equal than requested instead of equal only) 2006-03-23 * added debugging messages into send_winfo_notify due to problems with messages (Long UDP messages are sometimes ignored by clients; sometimes are too big to be sent. But there are problems with TCP too!) 2006-03-22 * using get_content_length instead of strlen(body) 2006-03-21 * flag (PFLAG_WATCHERINFO_CHANGED, WFLAG_SUBSCRIPTION_CHANGED) settings in internals, not in handle_subscription * sets subscription status for internal subscriptions (QSA) 2006-03-20 * setting flag PFLAG_WATCHERINFO_CHANGED only for new subscriptions because we don't send expiration in watcherinfo documents and thus it is only "spamming" if we send watcherinfo for resubscriptions too 2006-03-17 * changed XCAP query parameters 2006-03-02 * improved robustness of libs (CDS, presence) and PA * cosmetical changes 2006-03-01 * corrected BUG - Contact was sent in 200 response, now in all 2xx responses * cleaning WFLAG_SUBSCRIPTION_CHANGED after notification sent (in handle_subscription) * small cosmetical and robustness improving changes 2006-02-28 * call to timer_presentity in handle_subscription replaced by call to send_notify and remove_watcher_if_expired (i.e. sending notification to all watchers immediately -> sending notification to new/updated watcher) 2006-02-27 * corrected BUGs - mostly used dstr_get_str instead of dstr_get_data because there were some cases when was allocated memory buffers of zero length (this consumes memory using shm_malloc), but this memory was NOT freed then 2006-02-15 * RPC function pa.publish uses UID instead of uri! * XCAP uris for authorization documents contain UID instead of username! * added configurable max_subscription_expiration (undocumented) 2006-02-14 * added parameter for disabling processing internal subscriptions 2006-02-02 * added handling of element from RPID (whole elements are stored and put into NOTIFY messages; they are not processed in any way due to its complexity - see the rpid draft) => this will be changed to process ALL unknown elements from PIDF documents * improved documentation (offline winfo, parameters, ...) 2006-02-01 * added functions for storing offline auth. info (store_winfo and dump_stored_winfo) into database [they don't work in memory], documentation will be added soon 2006-01-30 * added function target_online * added parameter use_callbacks (int) - [if set to 0, no callback are used] * corrected handle_publish - returns 4xx for unsupported events instead of OK kamailio-4.0.4/obsolete/pa/publish.h0000644000000000000000000000306212223032460016031 0ustar rootroot/* * Presence Agent, publish handling * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef PUBLISH_H #define PUBLISH_H #include "../../parser/msg_parser.h" #include "qsa_interface.h" /* * Handle a publish Request */ int handle_publish(struct sip_msg* _m, char* _domain, char* _s2); /* Publish given parsed "presence document", the etag must be given. If it * was given by publisher, set has_tag to 1. If generated se has_tag to 0. */ int process_published_presentity_info(presentity_t *presentity, presentity_info_t *p, str *etag, time_t expires, int has_tag); #endif /* PUBLISH_H */ kamailio-4.0.4/obsolete/pa/auth.h0000644000000000000000000000107312223032460015324 0ustar rootroot#ifndef __AUTHORIZATION_H #define __AUTHORIZATION_H #include typedef enum { auth_none, auth_implicit, /* implicit authorization rules (may differ for packages, ...) */ auth_xcap } authorization_type_t; typedef struct { authorization_type_t type; } auth_params_t; #include "watcher.h" #include "presentity.h" #include "qsa_interface.h" #include "../../parser/msg_parser.h" watcher_status_t authorize_internal_watcher(presentity_t *p, internal_pa_subscription_t *is); watcher_status_t authorize_watcher(presentity_t *p, watcher_t *w); #endif kamailio-4.0.4/obsolete/pa/mimetypes.h0000644000000000000000000000054112223032460016376 0ustar rootroot#ifndef __MIMETYPES_H #define __MIMETYPES_H #include "../../parser/parse_content.h" typedef struct { int event_type; int mimes[MAX_MIMES_NR]; } event_mimetypes_t; int get_preferred_event_mimetype(struct sip_msg *_m, int et); event_mimetypes_t *find_event_mimetypes(int et); int check_mime_types(int *accepts_mimes, event_mimetypes_t *em); #endif kamailio-4.0.4/obsolete/pa/hslot.c0000644000000000000000000000457012223032460015514 0ustar rootroot/* * Presence Agent, hash table * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "hslot.h" /* * Initialize cache slot structure */ void init_slot(struct pdomain* _d, hslot_t* _s) { _s->n = 0; _s->first = 0; _s->d = _d; } /* * Deinitialize given slot structure */ void deinit_slot(hslot_t* _s) { presentity_t* ptr; /* Remove all elements */ while ((_s->first) && (_s->n > 0)) { ptr = _s->first; _s->first = _s->first->next; _s->n--; free_presentity(ptr); } _s->n = 0; _s->d = 0; } /* * Add an element to an slot's linked list */ void slot_add(hslot_t* _s, struct presentity* _p, struct presentity** _f, struct presentity** _l) { if (_s->first) { _p->next = _s->first; _p->prev = _s->first->prev; _s->first->prev = _p; if (_p->prev) _p->prev->next = _p; else *_f = _p; } else { if (*_l) { (*_l) ->next = _p; _p->prev = *_l; *_l = _p; } else { *_l = _p; *_f = _p; } } _s->first = _p; _p->slot = _s; _s->n ++; } /* * Remove an element from slot linked list */ void slot_rem(hslot_t* _s, struct presentity* _p, struct presentity** _f, struct presentity** _l) { if (_s->first == _p) { if (_p->next && (_p->next->slot == _s)) _s->first = _p->next; else _s->first = 0; } if (_p->prev) { _p->prev->next = _p->next; } else { *_f = _p->next; } if (_p->next) { _p->next->prev = _p->prev; } else { *_l = _p->prev; } _s->n--; _p->slot = 0; } kamailio-4.0.4/obsolete/pa/Makefile0000644000000000000000000000127312223032460015654 0ustar rootroot# $Id$ # # Presence Agent # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=pa.so LIBS= # DEFS+=-I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 # LIBS+=-L/usr/include/lib -L$(LOCALBASE)/lib -lxml2 # experimental (vku) DEFS+=-DSER INCLUDES += -I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include -I../../lib -I../.. LIBS = -L$(LOCALBASE)/lib -L/usr/pkg/lib -lxml2 SERLIBPATH=../../lib SER_LIBS=$(SERLIBPATH)/cds/ser_cds $(SERLIBPATH)/presence/ser_presence \ $(SERLIBPATH)/xcap/ser_xcap DEFS+=-DSER_MOD_INTERFACE SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/pa/paerrno.c0000644000000000000000000000212612223032460016024 0ustar rootroot/* * Presence Agent, error reporting * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "paerrno.h" paerr_t paerrno; kamailio-4.0.4/obsolete/acc_syslog/0000755000000000000000000000000012223032460015737 5ustar rootrootkamailio-4.0.4/obsolete/acc_syslog/doc/0000755000000000000000000000000012223032460016504 5ustar rootrootkamailio-4.0.4/obsolete/acc_syslog/doc/functions.xml0000644000000000000000000000625312223032460021244 0ustar rootroot
Functions
<function>acc_log_request(comment)</function> acc_request reports on a request, for example, it can be used to report on missed calls to off-line users who are replied 404. To avoid multiple reports on UDP request retransmission, you would need to embed the action in stateful processing. Meaning of the parameters is as follows: comment - Comment to be appended. acc_log_request usage ... acc_log_request("Some comment"); ...
<function>acc_db_request(comment, table)</function> Like acc_log_request, acc_db_request reports on a request. The report is sent to database at "db_url", in the table referred to in the second action parameter Meaning of the parameters is as follows: comment - Comment to be appended. table - Database table to be used. acc_db_request usage ... acc_log_request("Some comment", "Some table"); ...
<function>acc_rad_request(comment)</function> Like acc_log_request, acc_rad_request reports on a request. It reports to radius server as configured in "radius_config". Meaning of the parameters is as follows: comment - Comment to be appended. acc_rad_request usage ... acc_rad_request("Some comment"); ...
<function>acc_diam_request(comment)</function> Like acc_log_request, acc_diam_request reports on a request. It reports to Diameter server. Meaning of the parameters is as follows: comment - Comment to be appended. acc_diam_request usage ... acc_diam_request("Some comment"); ...
kamailio-4.0.4/obsolete/acc_syslog/doc/Makefile0000644000000000000000000000013312223032460020141 0ustar rootrootdocs = acc_syslog.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/acc_syslog/doc/params.xml0000644000000000000000000002070712223032460020517 0ustar rootroot
Parameters
<varname>log_level</varname> (integer) Log level at which accounting messages are issued to syslog. Default value is L_NOTICE. log_level example modparam("acc", "log_level", 2) # Set log_level to 2
<varname>log_fmt</varname> (string) Defines what parts of header fields will be printed to syslog, see "overview" for list of accepted values. Default value is "miocfs". log_fmt example modparam("acc", "log_fmt", "mfs")
<varname>early_media</varname> (integer) Should be early media (183) accounted too ? Default value is 0 (no). early_media example modparam("acc", "early_media", 1)
<varname>failed_transactions</varname> (integer) This parameter controls whether failed transactions (with final reply >= 300) should be accounted too. Default value is 0 (no). failed_transactions example modparam("acc", "failed_transactions", 1)
<varname>log_flag</varname> (integer) Request flag which needs to be set to account a transaction. Default value is 1. log_flag example modparam("acc", "log_flag", 2)
<varname>log_missed_flag</varname> (integer) Request flag which needs to be set to account missed calls. Default value is 2. log_missed_flag example modparam("acc", "log_missed_flag", 3)
<varname>report_ack</varname> (integer) Shall acc attempt to account e2e ACKs too ? Note that this is really only an attempt, as e2e ACKs may take a different path (unless RR enabled) and mismatch original INVITE (e2e ACKs are a separate transaction). Default value is 1 (yes). report_ack example modparam("acc", "report_ack", 0)
<varname>report_cancels</varname> (integer) By default, CANCEL reporting is disabled -- most accounting applications are happy to see INVITE's cancellation status. Turn on if you explicitly want to account CANCEL transactions. Default value is 0 (no). report_cancels example modparam("acc", "report_cancels", 1)
<varname>radius_config</varname> (string) This parameter is radius specific. Path to radius client configuration file, set the referred config file correctly and specify there address of server, shared secret (should equal that in /usr/local/etc/raddb/clients for freeRadius servers) and dictionary, see etc for an example of config file and dictionary. Default value is /usr/local/etc/radiusclient/radiusclient.conf. radius_config example modparam("acc", "radius_config", "/etc/radiusclient/radiusclient.conf")
<varname>service_type</varname> (integer) Radius service type used for accounting. Default value is 15 (SIP). service_type example modparam("acc", "service_type", 16)
<varname>radius_flag</varname> (integer) Request flag which needs to be set to account a transaction -- RADIUS specific. Default value is 1. radius_flag example modparam("acc", "radius_flag", 2)
<varname>radius_missed_flag</varname> (integer) Request flag which needs to be set to account missed calls -- RADIUS specific. Default value is 2. radius_missed_flag example modparam("acc", "radius_missed_flag", 3)
<varname>db_url</varname> (string) SQL address -- database specific. Default value is "mysql://ser:heslo@localhost/ser" db_url example modparam("acc", "db_url", "mysql://user:password@localhost/ser")
<varname>db_flag</varname> (integer) Request flag which needs to be set to account a transaction -- database specific. Default value is 1. db_flag example modparam("acc", "db_flag", 2)
<varname>db_missed_flag</varname> (integer) Request flag which needs to be set to account missed calls -- database specific. Default value is 2. db_missed_flag example modparam("acc", "db_missed_flag", 3)
<varname>diameter_flag</varname> (integer) Request flag which needs to be set to account a transaction -- DIAMETER specific. Default value is 1. diameter_flag example modparam("acc", "diameter_flag", 2)
<varname>diameter_missed_flag</varname> (integer) Request flag which needs to be set to account missed calls -- DIAMETER specific. Default value is 2. diameter_missed_flag example modparam("acc", "diameter_missed_flag", 3)
<varname>diameter_client_host</varname> (string) Hostname of the machine where the DIAMETER Client is running -- DIAMETER specific. Default value is "localhost". diameter_client_host example modparam("acc", "diameter_client_host", "iptel.org")
<varname>diameter_client_port</varname> (int) Port number where the Diameter Client is listening -- DIAMETER specific. Default value is 3000. diameter_client_host example modparam("acc", "diameter_client_port", 3000)
kamailio-4.0.4/obsolete/acc_syslog/doc/acc_syslog.xml0000644000000000000000000001446012223032460021361 0ustar rootroot
Jiri Kuthan iptel.org jiri@iptel.org 2002 2003 FhG FOKUS Acc Module
Overview acc module is used to report on transactions to syslog, SQL and RADIUS. To report on a transaction using syslog, use "setflag" to mark a transaction you are interested in with a flag, load accounting module and set its "log_flag" to the same flag number. The acc module will then report on completed transaction to syslog. A typical usage of the module takes no acc-specific script command -- the functionality binds invisibly through transaction processing. Script writers just need to mark the transaction for accounting with proper setflag. What is printed depends on module's "log_fmt" parameter. It's a string with characters specifying which parts of request should be printed: c = Call-Id d = To tag (Dst) f = From i = Inbound Request-URI m = Method o = Outbound Request-URI r = fRom s = Status t = To u = digest Username p = username Part of inbound Request-URI If a value is not present in request, "n/a" is accounted instead. A single INVITE may produce multiple accounting reports -- that's due to SIP forking feature Subsequent ACKs and other requests do not hit the server and can't be accounted unless record-routing is enforced. The ACKs assert very little useful information anyway and reporting on INVITE's 200 makes most accounting scenarios happy. There is no session accounting -- ser maintains no sessions. If one needs to correlate INVITEs with BYEs for example for purpose of billing, then it is better done in the entity which collects accounting information. Otherwise, SIP server would have to become sessions-stateful, which would very badly impact its scalability. If a UA fails in middle of conversation, a proxy will never learn it. In general, a better practice is to account from an end-device (such as PSTN gateway), which best knows about call status (including media status and PSTN status in case of the gateway). Support for SQL and RADIUS works analogously. You need to enable it by recompiling the module with properly set defines. Uncomment the SQL_ACC and RAD_ACC lines in modules/acc/Makefile. To compile SQL support, you need to have mysqlclient package on your system. To compile RADIUS support, you need to have radiusclient installed on your system (version 0.5.0 or higher is required) which is available from http://developer.berlios.de/projects/radiusclient-ng/. The radius client needs to be configured properly. To do so, use the template in sip_router/etc/radiusclient.conf and make sure that module's radius_config parameter points to its location. Uses along with FreeRADIUS and Radiator servers have been reported to us. Both mysql and radius libraries must be dynamically linkable. You need to configure your OS so that SER, when started, will find them. Typically, you do so by manipulating LD_LIBRARY_PATH environment variable or configuring ld.so. General Example
Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): tm Transaction Manager A database module (mysql,postgres,dbtext) If compiled with database support.
kamailio-4.0.4/obsolete/acc_syslog/attrs.h0000644000000000000000000001363512223032460017255 0ustar rootroot/* * Accounting module * * $Id$ * * Copyright (C) 2001-2003 FhG FOKUS * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _ATTRS_H #define _ATTRS_H #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../usr_avp.h" #include "../../trim.h" #include "../../str.h" #include "../../ut.h" /* * Parse the value of attrs parameter */ static int parse_attrs(avp_ident_t** avps, int* avps_n, char* attrs) { str token; token.s = strtok(attrs, ","); *avps = 0; *avps_n = 0; while(token.s) { token.len = strlen(token.s); trim(&token); if (token.len && token.s[0] == '$') { token.s++; token.len--; } else goto skip; *avps = pkg_realloc(*avps, sizeof(avp_ident_t) * (*avps_n + 1)); if (!*avps) { ERR("No memory left\n"); goto err; } if (parse_avp_ident(&token, &(*avps)[*avps_n]) < 0) { ERR("Error while parsing AVP id '%.*s'\n", token.len, ZSW(token.s)); goto err; } DBG("Found attribute $%.*s\n", (*avps)[*avps_n].name.s.len, (*avps)[*avps_n].name.s.s); (*avps_n)++; skip: token.s = strtok(0, ","); } return 0; err: if (*avps) pkg_free(*avps); return -1; } #define attrs_append(dst, src) \ do { \ if ((dst).len < (src).len) { \ ERR("Buffer too small\n"); \ goto error; \ } \ memcpy((dst).s, (src).s, (src).len); \ (dst).s += (src).len; \ (dst).len -= (src).len; \ } while(0) /* * Escape delimiter characters */ #define attrs_append_esc(dst, src, esc_quote) \ do { \ int i; \ char* w; \ \ if ((dst).len < ((src).len * 2)) { \ ERR("Buffer too small\n"); \ goto error; \ } \ \ w = (dst).s; \ for(i = 0; i < (src).len; i++) { \ switch((src).s[i]) { \ case '\n': *w++ = '\\'; *w++ = 'n'; break; \ case '\r': *w++ = '\\'; *w++ = 'r'; break; \ case '\t': *w++ = '\\'; *w++ = 't'; break; \ case '\\': *w++ = '\\'; *w++ = '\\'; break; \ case '\0': *w++ = '\\'; *w++ = '0'; break; \ case '"': \ if (esc_quote) { \ *w++ = '\\'; *w++ = 'q'; \ } else { \ *w++ = (src).s[i]; \ } \ break; \ case ':': *w++ = '\\'; *w++ = 'o'; break; \ case ',': *w++ = '\\'; *w++ = 'c'; break; \ default: *w++ = (src).s[i]; break; \ } \ } \ (dst).len -= w - (dst).s; \ (dst).s = w; \ } while(0) #define attrs_append_printf(dst, fmt, args...) \ do { \ int len = snprintf((dst).s, (dst).len, (fmt), ## args); \ if (len < 0 || len >= (dst).len) { \ ERR("Buffer too small\n"); \ goto error; \ } \ (dst).s += len; \ (dst).len -= len; \ } while(0) #define ATTRS_BUF_LEN 4096 static str* print_attrs(avp_ident_t* avps, int avps_n, int quote) { static str quote_s = STR_STATIC_INIT("\""); static str attrs_name_delim = STR_STATIC_INIT(":"); static str attrs_delim = STR_STATIC_INIT(","); static char buf[ATTRS_BUF_LEN]; static str res; int i; struct search_state st; avp_value_t val; str p; p.s = buf; p.len = ATTRS_BUF_LEN - 1; if (quote && avps_n) { attrs_append(p, quote_s); } for(i = 0; i < avps_n; i++) { avp_t *this_avp = search_first_avp(avps[i].flags, avps[i].name, &val, &st); if (!this_avp) continue; attrs_append(p, avps[i].name.s); attrs_append(p, attrs_name_delim); if (this_avp->flags & AVP_VAL_STR) attrs_append_esc(p, val.s, quote); else attrs_append_printf(p, "%d", val.n); while(search_next_avp(&st, &val)) { attrs_append(p, attrs_delim); attrs_append(p, avps[i].name.s); attrs_append(p, attrs_name_delim); attrs_append_esc(p, val.s, quote); } if (i < (avps_n - 1)) attrs_append(p, attrs_delim); } if (quote && avps_n) { attrs_append(p, quote_s); } *p.s = '\0'; res.s = buf; res.len = p.s - buf; return &res; error: return 0; } #endif /* _ATTRS_H */ kamailio-4.0.4/obsolete/acc_syslog/README0000644000000000000000000002623212223032460016624 0ustar rootroot1. Acc Module Jiri Kuthan iptel.org Copyright © 2002, 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. Dependencies 1.3. Parameters 1.3.1. log_level (integer) 1.3.2. log_fmt (string) 1.3.3. early_media (integer) 1.3.4. failed_transactions (integer) 1.3.5. log_flag (integer) 1.3.6. log_missed_flag (integer) 1.3.7. report_ack (integer) 1.3.8. report_cancels (integer) 1.3.9. radius_config (string) 1.3.10. service_type (integer) 1.3.11. radius_flag (integer) 1.3.12. radius_missed_flag (integer) 1.3.13. db_url (string) 1.3.14. db_flag (integer) 1.3.15. db_missed_flag (integer) 1.3.16. diameter_flag (integer) 1.3.17. diameter_missed_flag (integer) 1.3.18. diameter_client_host (string) 1.3.19. diameter_client_port (int) 1.4. Functions 1.4.1. acc_log_request(comment) 1.4.2. acc_db_request(comment, table) 1.4.3. acc_rad_request(comment) 1.4.4. acc_diam_request(comment) 1.1. Overview acc module is used to report on transactions to syslog, SQL and RADIUS. To report on a transaction using syslog, use "setflag" to mark a transaction you are interested in with a flag, load accounting module and set its "log_flag" to the same flag number. The acc module will then report on completed transaction to syslog. A typical usage of the module takes no acc-specific script command -- the functionality binds invisibly through transaction processing. Script writers just need to mark the transaction for accounting with proper setflag. What is printed depends on module's "log_fmt" parameter. It's a string with characters specifying which parts of request should be printed: * c = Call-Id * d = To tag (Dst) * f = From * i = Inbound Request-URI * m = Method * o = Outbound Request-URI * r = fRom * s = Status * t = To * u = digest Username * p = username Part of inbound Request-URI If a value is not present in request, "n/a" is accounted instead. Note * A single INVITE may produce multiple accounting reports -- that's due to SIP forking feature * Subsequent ACKs and other requests do not hit the server and can't be accounted unless record-routing is enforced. The ACKs assert very little useful information anyway and reporting on INVITE's 200 makes most accounting scenarios happy. * There is no session accounting -- ser maintains no sessions. If one needs to correlate INVITEs with BYEs for example for purpose of billing, then it is better done in the entity which collects accounting information. Otherwise, SIP server would have to become sessions-stateful, which would very badly impact its scalability. * If a UA fails in middle of conversation, a proxy will never learn it. In general, a better practice is to account from an end-device (such as PSTN gateway), which best knows about call status (including media status and PSTN status in case of the gateway). Support for SQL and RADIUS works analogously. You need to enable it by recompiling the module with properly set defines. Uncomment the SQL_ACC and RAD_ACC lines in modules/acc/Makefile. To compile SQL support, you need to have mysqlclient package on your system. To compile RADIUS support, you need to have radiusclient installed on your system (version 0.5.0 or higher is required) which is available from http://developer.berlios.de/projects/radiusclient-ng/. The radius client needs to be configured properly. To do so, use the template in sip_router/etc/radiusclient.conf and make sure that module's radius_config parameter points to its location. Uses along with FreeRADIUS and Radiator servers have been reported to us. Both mysql and radius libraries must be dynamically linkable. You need to configure your OS so that SER, when started, will find them. Typically, you do so by manipulating LD_LIBRARY_PATH environment variable or configuring ld.so. Example 1. General Example loadmodule "modules/acc/acc.so" modparam("acc", "log_level", 1) modparam("acc", "log_flag", 1) if (uri=~"sip:+49") /* calls to Germany */ { if (!proxy_authorize("iptel.org" /* realm */, "subscriber" /* table name */)) { proxy_challenge("iptel.org" /* realm */, "0" /* no qop */ ); break; } if (method=="INVITE" & !check_from()) { log("from!=digest\n"); sl_send_reply("403","Forbidden"); break; } setflag(1); /* set for accounting (the same value as in log_flag!) t_relay(); /* enter stateful mode now */ }; 1.2. Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): * tm. Transaction Manager * A database module (mysql,postgres,dbtext). If compiled with database support. 1.3. Parameters 1.3.1. log_level (integer) Log level at which accounting messages are issued to syslog. Default value is L_NOTICE. Example 2. log_level example modparam("acc", "log_level", 2) # Set log_level to 2 1.3.2. log_fmt (string) Defines what parts of header fields will be printed to syslog, see "overview" for list of accepted values. Default value is "miocfs". Example 3. log_fmt example modparam("acc", "log_fmt", "mfs") 1.3.3. early_media (integer) Should be early media (183) accounted too ? Default value is 0 (no). Example 4. early_media example modparam("acc", "early_media", 1) 1.3.4. failed_transactions (integer) This parameter controls whether failed transactions (with final reply >= 300) should be accounted too. Default value is 0 (no). Example 5. failed_transactions example modparam("acc", "failed_transactions", 1) 1.3.5. log_flag (integer) Request flag which needs to be set to account a transaction. Default value is 1. Example 6. log_flag example modparam("acc", "log_flag", 2) 1.3.6. log_missed_flag (integer) Request flag which needs to be set to account missed calls. Default value is 2. Example 7. log_missed_flag example modparam("acc", "log_missed_flag", 3) 1.3.7. report_ack (integer) Shall acc attempt to account e2e ACKs too ? Note that this is really only an attempt, as e2e ACKs may take a different path (unless RR enabled) and mismatch original INVITE (e2e ACKs are a separate transaction). Default value is 1 (yes). Example 8. report_ack example modparam("acc", "report_ack", 0) 1.3.8. report_cancels (integer) By default, CANCEL reporting is disabled -- most accounting applications are happy to see INVITE's cancellation status. Turn on if you explicitly want to account CANCEL transactions. Default value is 0 (no). Example 9. report_cancels example modparam("acc", "report_cancels", 1) 1.3.9. radius_config (string) This parameter is radius specific. Path to radius client configuration file, set the referred config file correctly and specify there address of server, shared secret (should equal that in /usr/local/etc/raddb/clients for freeRadius servers) and dictionary, see etc for an example of config file and dictionary. Default value is “/usr/local/etc/radiusclient/radiusclient.confâ€. Example 10. radius_config example modparam("acc", "radius_config", "/etc/radiusclient/radiusclient.conf") 1.3.10. service_type (integer) Radius service type used for accounting. Default value is 15 (SIP). Example 11. service_type example modparam("acc", "service_type", 16) 1.3.11. radius_flag (integer) Request flag which needs to be set to account a transaction -- RADIUS specific. Default value is 1. Example 12. radius_flag example modparam("acc", "radius_flag", 2) 1.3.12. radius_missed_flag (integer) Request flag which needs to be set to account missed calls -- RADIUS specific. Default value is 2. Example 13. radius_missed_flag example modparam("acc", "radius_missed_flag", 3) 1.3.13. db_url (string) SQL address -- database specific. Default value is "mysql://ser:heslo@localhost/ser" Example 14. db_url example modparam("acc", "db_url", "mysql://user:password@localhost/ser") 1.3.14. db_flag (integer) Request flag which needs to be set to account a transaction -- database specific. Default value is 1. Example 15. db_flag example modparam("acc", "db_flag", 2) 1.3.15. db_missed_flag (integer) Request flag which needs to be set to account missed calls -- database specific. Default value is 2. Example 16. db_missed_flag example modparam("acc", "db_missed_flag", 3) 1.3.16. diameter_flag (integer) Request flag which needs to be set to account a transaction -- DIAMETER specific. Default value is 1. Example 17. diameter_flag example modparam("acc", "diameter_flag", 2) 1.3.17. diameter_missed_flag (integer) Request flag which needs to be set to account missed calls -- DIAMETER specific. Default value is 2. Example 18. diameter_missed_flag example modparam("acc", "diameter_missed_flag", 3) 1.3.18. diameter_client_host (string) Hostname of the machine where the DIAMETER Client is running -- DIAMETER specific. Default value is "localhost". Example 19. diameter_client_host example modparam("acc", "diameter_client_host", "iptel.org") 1.3.19. diameter_client_port (int) Port number where the Diameter Client is listening -- DIAMETER specific. Default value is 3000. Example 20. diameter_client_host example modparam("acc", "diameter_client_port", 3000) 1.4. Functions 1.4.1. acc_log_request(comment) acc_request reports on a request, for example, it can be used to report on missed calls to off-line users who are replied 404. To avoid multiple reports on UDP request retransmission, you would need to embed the action in stateful processing. Meaning of the parameters is as follows: * comment - Comment to be appended. Example 21. acc_log_request usage ... acc_log_request("Some comment"); ... 1.4.2. acc_db_request(comment, table) Like acc_log_request, acc_db_request reports on a request. The report is sent to database at "db_url", in the table referred to in the second action parameter Meaning of the parameters is as follows: * comment - Comment to be appended. * table - Database table to be used. Example 22. acc_db_request usage ... acc_log_request("Some comment", "Some table"); ... 1.4.3. acc_rad_request(comment) Like acc_log_request, acc_rad_request reports on a request. It reports to radius server as configured in "radius_config". Meaning of the parameters is as follows: * comment - Comment to be appended. Example 23. acc_rad_request usage ... acc_rad_request("Some comment"); ... 1.4.4. acc_diam_request(comment) Like acc_log_request, acc_diam_request reports on a request. It reports to Diameter server. Meaning of the parameters is as follows: * comment - Comment to be appended. Example 24. acc_diam_request usage ... acc_diam_request("Some comment"); ... kamailio-4.0.4/obsolete/acc_syslog/acc_syslog.c0000644000000000000000000006261312223032460020241 0ustar rootroot/* * Accounting module * * $Id$ * * Copyright (C) 2001-2003 FhG FOKUS * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../modules/tm/t_hooks.h" #include "../../modules/tm/tm_load.h" #include "../../modules/tm/h_table.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../parser/digest/digest.h" #include "../../usr_avp.h" #include "../../id.h" #include "attrs.h" #include "../../modules/tm/tm_load.h" /* * TODO: * - Quote attribute values properly * - Save request timestamp * - Save response timestamp */ /* * a: attr * c: sip_callid * d: to_tag * f: sip_from * g: flags * i: inbound_ruri * m: sip_method * n: sip_cseq * o: outbound_ruri * p: source_ip * r: from_tag * s: server_id * t: sip_to * u: digest_username * x: request_timestamp * D: to_did * F: from_uri * I: from_uid * M: from_did * R: digest_realm * P: source_port * S: sip_status * T: to_uri * U: to_uid * X: response_timestamp */ #define ALL_LOG_FMT "acdfgimnoprstuxDFIMPRSTUX" #define ALL_LOG_FMT_LEN (sizeof(ALL_LOG_FMT) - 1) #define A_SEPARATOR ", " /* must be shorter than ACC! */ #define A_SEPARATOR_LEN (sizeof(A_SEPARATOR) - 1) #define A_EQ "=" #define A_EQ_LEN (sizeof(A_EQ) - 1) #define A_EOL "\n\0" #define A_EOL_LEN (sizeof(A_EOL) - 1) #define ACC "ACC: " /* Prefix of accounting messages in syslog */ #define ACC_LEN (sizeof(ACC) - 1) #define ACC_REQUEST "request accounted: " #define ACC_MISSED "call missed: " #define ACC_ANSWERED "transaction answered: " #define ACC_ACKED "request acknowledged: " #define NA "n/a" #define ATR(atr) atr_arr[cnt].s = A_##atr; atr_arr[cnt].len = sizeof(A_##atr) - 1; #define A_ATTRS "attrs" #define A_CALLID "callid" #define A_TOTAG "to_tag" #define A_FROM "from" #define A_FLAGS "flags" #define A_IURI "in_ruri" #define A_METHOD "sip_method" #define A_CSEQ "cseq" #define A_OURI "out_ruri" #define A_FROMTAG "from_tag" #define A_TO "to" #define A_DIGUSER "digest_username" #define A_REQTIMESTAMP "request_timestamp" #define A_TODID "to_did" #define A_FROMURI "from_uri" #define A_FROMUID "from_uid" #define A_FROMDID "from_did" #define A_DIGREALM "digest_realm" #define A_STATUS "sip_status" #define A_TOURI "to_uri" #define A_TOUID "to_uid" #define A_RESTIMESTAMP "response_timestamp" #define A_SRCIP "src_ip" #define A_SRCPORT "src_port" #define A_SERVERID "server_id" MODULE_VERSION struct tm_binds tmb; static int mod_init( void ); static int fix_log_flag( modparam_t type, void* val); static int fix_log_missed_flag( modparam_t type, void* val); static int log_level = L_NOTICE; /* noisiness level logging facilities are used */ static int early_media = 0; /* Enable/disable early media (183) accounting */ static int failed_transactions = 0; /* Enable/disable accounting of failed (>= 300) transactions */ static int report_cancels = 0; /* Enable/disable CANCEL reporting */ static int report_ack = 0; /* Enable/disable end-to-end ACK reports */ static int log_flag = 0; /* Flag that marks transactions to be accounted */ static int log_missed_flag = 0; /* Transaction having this flag set will be accounted in missed calls when fails */ static char* log_fmt = ALL_LOG_FMT; /* Formating string that controls what information will be collected and accounted */ /* Attribute-value pairs */ static char* attrs = ""; avp_ident_t* avps; int avps_n; static int acc_log_request0(struct sip_msg *rq, char *p1, char *p2); static int acc_log_missed0(struct sip_msg *rq, char *p1, char *p2); static int acc_log_request1(struct sip_msg *rq, char *p1, char *p2); static int acc_log_missed1(struct sip_msg *rq, char *p1, char *p2); static str na = STR_STATIC_INIT(NA); static cmd_export_t cmds[] = { {"acc_syslog_log", acc_log_request0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"acc_syslog_missed", acc_log_missed0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"acc_syslog_log", acc_log_request1, 1, fixup_var_str_1, REQUEST_ROUTE | FAILURE_ROUTE}, {"acc_syslog_missed", acc_log_missed1, 1, fixup_var_str_1, REQUEST_ROUTE | FAILURE_ROUTE}, {0, 0, 0, 0, 0} }; static param_export_t params[] = { {"early_media", PARAM_INT, &early_media }, {"failed_transactions", PARAM_INT, &failed_transactions }, {"report_ack", PARAM_INT, &report_ack }, {"report_cancels", PARAM_INT, &report_cancels }, {"log_flag", PARAM_INT, &log_flag }, {"log_flag", PARAM_STRING|PARAM_USE_FUNC, fix_log_flag}, {"log_missed_flag", PARAM_INT, &log_missed_flag }, {"log_missed_flag", PARAM_STRING|PARAM_USE_FUNC, fix_log_missed_flag}, {"log_level", PARAM_INT, &log_level }, {"log_fmt", PARAM_STRING, &log_fmt }, {"attrs", PARAM_STRING, &attrs }, {0, 0, 0} }; struct module_exports exports= { "acc_syslog", cmds, /* exported functions */ 0, /* RPC methods */ params, /* exported params */ mod_init, /* initialization module */ 0, /* response function */ 0, /* destroy function */ 0, /* oncancel function */ 0 /* per-child init function */ }; /* fixes log_flag param (resolves possible named flags) */ static int fix_log_flag( modparam_t type, void* val) { return fix_flag(type, val, "acc_syslog", "log_flag", &log_flag); } /* fixes log_missed_flag param (resolves possible named flags) */ static int fix_log_missed_flag( modparam_t type, void* val) { return fix_flag(type, val, "acc_syslog", "log_missed_flag", &log_missed_flag); } #define TM_BUF_LEN sizeof("10000-12-31 23:59:59") static inline int convert_time(str* buf, time_t time) { struct tm* tm; if (!buf->s || buf->len < TM_BUF_LEN) { LOG(L_ERR, "ERROR:acc:convert_time: Buffer too short\n"); return -1; } tm = gmtime(&time); buf->len = strftime(buf->s, buf->len, "%Y-%m-%d %H:%M:%S", tm); return 0; } static inline int skip_cancel(struct sip_msg *msg) { return (msg->REQ_METHOD == METHOD_CANCEL) && report_cancels == 0; } /* * Append a constant string, uses sizeof to figure the length * of the string */ #define append(buf, ptr) \ do { \ memcpy((buf).s, (ptr), sizeof(ptr) - 1); \ (buf).s += sizeof(ptr) - 1; \ (buf).len -= sizeof(ptr) - 1; \ } while(0); #define str_append_str(buf, str) \ do { \ memcpy((buf).s, (str).s, (str).len); \ (buf).s += (str).len; \ (buf).len -= (str).len; \ } while(0); int verify_fmt(char *fmt) { if (!fmt) { LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string zero\n"); return -1; } if (!(*fmt)) { LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string empty\n"); return -1; } if (strlen(fmt) > ALL_LOG_FMT_LEN) { LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string too long\n"); return -1; } while(*fmt) { if (!strchr(ALL_LOG_FMT, *fmt)) { LOG(L_ERR, "ERROR:acc:verify_fmt: char in log_fmt invalid: %c\n", *fmt); return -1; } fmt++; } return 1; } /* * Return true if accounting is enabled and the * transaction is marked for accounting */ static inline int is_acc_on(struct sip_msg *rq) { return log_flag && isflagset(rq, log_flag) == 1; } /* * Return true if missed_call accounting is enabled * and the transaction has the flag set */ static inline int is_mc_on(struct sip_msg *rq) { return log_missed_flag && isflagset(rq, log_missed_flag) == 1; } static inline void preparse_req(struct sip_msg *rq) { /* try to parse from for From-tag for accounted transactions; * don't be worried about parsing outcome -- if it failed, * we will report N/A. There is no need to parse digest credentials * here even if we account them, because the authentication function * will do it before us and if not then we will account n/a. */ parse_headers(rq, HDR_CALLID_F | HDR_FROM_F | HDR_TO_F | HDR_CSEQ_F, 0 ); parse_from_header(rq); } /* is this reply of interest for accounting ? */ static inline int should_acc_reply(struct cell* t, int code) { struct sip_msg *r; r = t->uas.request; /* validation */ if (r == 0) { LOG(L_ERR, "ERROR:acc:should_acc_reply: 0 request\n"); return 0; } /* negative transactions reported otherwise only if explicitly * demanded */ if (!failed_transactions && code >= 300) return 0; if (!is_acc_on(r)) return 0; if (skip_cancel(r)) return 0; if (code < 200 && ! (early_media && code == 183)) return 0; return 1; /* seed is through, we will account this reply */ } /* Extract username attribute from authorized credentials */ static inline str* cred_user(struct sip_msg* rq) { struct hdr_field* h; auth_body_t* cred; get_authorized_cred(rq->proxy_auth, &h); if (!h) get_authorized_cred(rq->authorization, &h); if (!h) return 0; cred = (auth_body_t*)(h->parsed); if (!cred || !cred->digest.username.user.len) return 0; return &cred->digest.username.user; } /* Extract realm attribute from authorized credentials */ static inline str* cred_realm(struct sip_msg* rq) { str* realm; struct hdr_field* h; auth_body_t* cred; get_authorized_cred(rq->proxy_auth, &h); if (!h) get_authorized_cred(rq->authorization, &h); if (!h) return 0; cred = (auth_body_t*)(h->parsed); if (!cred) return 0; realm = GET_REALM(&cred->digest); if (!realm->len || !realm->s) { return 0; } return realm; } /* Return To header field from the request in case of faked reply or * missing To header field in the reply */ static inline struct hdr_field* valid_to(struct cell* t, struct sip_msg* reply) { if (reply == FAKED_REPLY || !reply || !reply->to) { return t->uas.request->to; } else { return reply->to; } } /* create an array of str's for accounting using a formatting string; * this is the heart of the accounting module -- it prints whatever * requested in a way, that can be used for syslog, radius, * sql, whatsoever * tm sip_msg_clones does not clone (shmmem-zed) parsed fields, other then Via1,2. Such fields clone now or use from rq_rp */ static int fmt2strar(char *fmt, /* what would you like to account ? */ struct sip_msg *rq, /* accounted message */ str* ouri, /* Outbound Request-URI */ struct hdr_field *to, /* To header field (used to extract tag) */ str *phrase, int *total_len, /* total length of accounted values */ int *attr_len, /* total length of accounted attribute names */ str **val_arr, /* that's the output -- must have MAX_ACC_COLUMNS */ str *atr_arr, time_t req_time) /* Timestamp of the request */ { static char flags_buf[INT2STR_MAX_LEN], tm_buf[TM_BUF_LEN], rqtm_buf[TM_BUF_LEN], srcip_buf[IP_ADDR_MAX_STR_SIZE], srcport_buf[INT2STR_MAX_LEN], serverid_buf[INT2STR_MAX_LEN]; int cnt, tl, al; struct to_body* from, *pto; static str mycode, flags, tm_s, rqtm_s, src_ip, src_port, from_uid, to_uid, server_id_str; str *cr, *at; struct cseq_body *cseq; char* p; cnt = tl = al = 0; /* we don't care about parsing here; either the function * was called from script, in which case the wrapping function * is supposed to parse, or from reply processing in which case * TM should have preparsed from REQUEST_IN callback; what's not * here is replaced with NA */ while(*fmt) { if (cnt == ALL_LOG_FMT_LEN) { LOG(L_ERR, "ERROR:acc:fmt2strar: Formatting string is too long\n"); return 0; } switch(*fmt) { case 'a': /* attr */ at = print_attrs(avps, avps_n, 1); if (!at) { val_arr[cnt] = &na; } else { val_arr[cnt] = at; } ATR(ATTRS); break; case 'c': /* sip_callid */ val_arr[cnt] = (rq->callid && rq->callid->body.len) ? &rq->callid->body : &na; ATR(CALLID); break; case 'd': /* to_tag */ val_arr[cnt] = (to && (pto = (struct to_body*)(to->parsed)) && pto->tag_value.len) ? & pto->tag_value : &na; ATR(TOTAG); break; case 'f': /* sip_from */ val_arr[cnt] = (rq->from && rq->from->body.len) ? &rq->from->body : &na; ATR(FROM); break; case 'g': /* flags */ p = int2str(rq->flags, &flags.len); memcpy(flags_buf, p, flags.len); flags.s = flags_buf; val_arr[cnt] = &flags; ATR(FLAGS); break; case 'i': /* inbound_ruri */ val_arr[cnt] = &rq->first_line.u.request.uri; ATR(IURI); break; case 'm': /* sip_method */ val_arr[cnt] = &rq->first_line.u.request.method; ATR(METHOD); break; case 'n': /* sip_cseq */ if (rq->cseq && (cseq = get_cseq(rq)) && cseq->number.len) val_arr[cnt] = &cseq->number; else val_arr[cnt]=&na; ATR(CSEQ); break; case 'o': /* outbound_ruri */ val_arr[cnt] = ouri; ATR(OURI); break; case 'p': /* source_ip */ /* We need to make a copy of the string here because ip_addr2a uses static * buffer and subseqent calls to the function would destroy the result */ src_ip.s = srcip_buf; p = ip_addr2a(&rq->rcv.src_ip); src_ip.len = strlen(p); memcpy(src_ip.s, p, src_ip.len); val_arr[cnt] = &src_ip; ATR(SRCIP); break; case 'r': /* from_tag */ if (rq->from && (from = get_from(rq)) && from->tag_value.len) { val_arr[cnt] = &from->tag_value; } else { val_arr[cnt] = &na; } ATR(FROMTAG); break; case 's': /* server_id */ p = int2str(server_id, &server_id_str.len); memcpy(serverid_buf, p, server_id_str.len); server_id_str.s = serverid_buf; val_arr[cnt] = &server_id_str; ATR(SERVERID); break; case 't': /* sip_to */ val_arr[cnt] = (to && to->body.len) ? &to->body : &na; ATR(TO); break; case 'u': /* digest_username */ cr = cred_user(rq); if (cr) val_arr[cnt] = cr; else val_arr[cnt] = &na; ATR(DIGUSER); break; case 'x': /* request_timestamp */ rqtm_s.s = rqtm_buf; rqtm_s.len = TM_BUF_LEN; convert_time(&rqtm_s, req_time); val_arr[cnt] = &rqtm_s; ATR(REQTIMESTAMP); break; case 'D': /* to_did */ val_arr[cnt] = &na; ATR(TODID); break; case 'F': /* from_uri */ if (rq->from && (from = get_from(rq)) && from->uri.len) { val_arr[cnt] = &from->uri; } else val_arr[cnt] = &na; ATR(FROMURI); break; case 'I': /* from_uid */ if (get_from_uid(&from_uid, rq) < 0) { val_arr[cnt] = &na; } else { val_arr[cnt] = &from_uid; } ATR(FROMUID); break; case 'M': /* from_did */ val_arr[cnt] = &na; ATR(FROMDID); break; case 'P': /* source_port */ p = int2str(rq->rcv.src_port, &src_port.len); memcpy(srcport_buf, p, src_port.len); src_port.s = srcport_buf; val_arr[cnt] = &src_port; ATR(SRCPORT); break; case 'R': /* digest_realm */ cr = cred_realm(rq); if (cr) val_arr[cnt] = cr; else val_arr[cnt] = &na; ATR(DIGREALM); break; case 'S': /* sip_status */ if (phrase->len >= 3) { mycode.s = phrase->s; mycode.len = 3; val_arr[cnt] = &mycode; } else val_arr[cnt] = &na; ATR(STATUS); break; case 'T': /* to_uri */ if (rq->to && (pto = get_to(rq)) && pto->uri.len) val_arr[cnt] = &pto->uri; else val_arr[cnt] = &na; ATR(TOURI); break; case 'U': /* to_uid */ if (get_to_uid(&to_uid, rq) < 0) { val_arr[cnt] = &na; } else { val_arr[cnt] = &to_uid; } ATR(TOUID); break; case 'X': /* response_timestamp */ tm_s.s = tm_buf; tm_s.len = TM_BUF_LEN; convert_time(&tm_s, time(0)); val_arr[cnt] = &tm_s; ATR(RESTIMESTAMP); break; default: LOG(L_CRIT, "BUG:acc:fmt2strar: unknown char: %c\n", *fmt); return 0; } /* switch (*fmt) */ tl += val_arr[cnt]->len; al += atr_arr[cnt].len; fmt++; cnt++; } /* while (*fmt) */ *total_len = tl; *attr_len = al; return cnt; } static int log_request(struct sip_msg* rq, str* ouri, struct hdr_field* to, str* txt, str* phrase, time_t req_time) { static str* val_arr[ALL_LOG_FMT_LEN]; static str atr_arr[ALL_LOG_FMT_LEN]; int len, attr_cnt, attr_len, i; char *log_msg; str buf; if (skip_cancel(rq)) return 1; attr_cnt = fmt2strar(log_fmt, rq, ouri, to, phrase, &len, &attr_len, val_arr, atr_arr, req_time); if (!attr_cnt) { LOG(L_ERR, "ERROR:acc:log_request: fmt2strar failed\n"); return -1; } len += attr_len + ACC_LEN + txt->len + A_EOL_LEN + attr_cnt * (A_SEPARATOR_LEN + A_EQ_LEN) - A_SEPARATOR_LEN; log_msg = pkg_malloc(len); if (!log_msg) { LOG(L_ERR, "ERROR:acc:log_request: No memory left for %d bytes\n", len); return -1; } /* skip leading text and begin with first item's * separator ", " which will be overwritten by the * leading text later * */ buf.s = log_msg + ACC_LEN + txt->len - A_SEPARATOR_LEN; buf.len = len - ACC_LEN - txt->len + A_SEPARATOR_LEN; for (i = 0; i < attr_cnt; i++) { append(buf, A_SEPARATOR); str_append_str(buf, atr_arr[i]); append(buf, A_EQ); str_append_str(buf, *(val_arr[i])) } /* terminating text */ append(buf, A_EOL); /* leading text */ buf.s = log_msg; buf.len = len; append(buf, ACC); str_append_str(buf, *txt); LOG(log_level, "%s", log_msg); pkg_free(log_msg); return 1; } static void log_reply(struct cell* t , struct sip_msg* reply, unsigned int code, time_t req_time) { str code_str, *ouri; static str lead = STR_STATIC_INIT(ACC_ANSWERED); static char code_buf[INT2STR_MAX_LEN]; char* p; p = int2str(code, &code_str.len); memcpy(code_buf, p, code_str.len); code_str.s = code_buf; if (t->relayed_reply_branch >= 0) { ouri = &t->uac[t->relayed_reply_branch].uri; } else { ouri = GET_NEXT_HOP(t->uas.request); } log_request(t->uas.request, ouri, valid_to(t,reply), &lead, &code_str, req_time); } static void log_ack(struct cell* t , struct sip_msg *ack, time_t req_time) { struct sip_msg *rq; struct hdr_field *to; static str lead = STR_STATIC_INIT(ACC_ACKED); static char code_buf[INT2STR_MAX_LEN]; str code_str; char* p; rq = t->uas.request; if (ack->to) to = ack->to; else to = rq->to; p = int2str(t->uas.status, &code_str.len); memcpy(code_buf, p, code_str.len); code_str.s = code_buf; log_request(ack, GET_RURI(ack), to, &lead, &code_str, req_time); } static void log_missed(struct cell* t, struct sip_msg* reply, unsigned int code, time_t req_time) { str acc_text, *ouri; static str leading_text = STR_STATIC_INIT(ACC_MISSED); get_reply_status(&acc_text, reply, code); if (acc_text.s == 0) { LOG(L_ERR, "ERROR:acc:log_missed: get_reply_status failed\n" ); return; } if (t->relayed_reply_branch >= 0) { ouri = &t->uac[t->relayed_reply_branch].uri; } else { ouri = GET_NEXT_HOP(t->uas.request); } log_request(t->uas.request, ouri, valid_to(t, reply), &leading_text, &acc_text, req_time); pkg_free(acc_text.s); } /* these wrappers parse all what may be needed; they don't care about * the result -- accounting functions just display "unavailable" if there * is nothing meaningful */ static int acc_log_request1(struct sip_msg *rq, char* p1, char* p2) { str phrase; str txt = STR_STATIC_INIT(ACC_REQUEST); if (get_str_fparam(&phrase, rq, (fparam_t*)p1) < 0) { phrase.s = 0; phrase.len = 0; } preparse_req(rq); return log_request(rq, GET_RURI(rq), rq->to, &txt, &phrase, time(0)); } /* these wrappers parse all what may be needed; they don't care about * the result -- accounting functions just display "unavailable" if there * is nothing meaningful */ static int acc_log_missed1(struct sip_msg *rq, char* p1, char* p2) { str phrase; str txt = STR_STATIC_INIT(ACC_MISSED); if (get_str_fparam(&phrase, rq, (fparam_t*)p1) < 0) { phrase.s = 0; phrase.len = 0; } preparse_req(rq); return log_request(rq, GET_RURI(rq), rq->to, &txt, &phrase, time(0)); } /* these wrappers parse all what may be needed; they don't care about * the result -- accounting functions just display "unavailable" if there * is nothing meaningful */ static int acc_log_request0(struct sip_msg *rq, char* p1, char* p2) { static str phrase = STR_NULL; str txt = STR_STATIC_INIT(ACC_REQUEST); return log_request(rq, GET_RURI(rq), rq->to, &txt, &phrase, time(0)); } /* these wrappers parse all what may be needed; they don't care about * the result -- accounting functions just display "unavailable" if there * is nothing meaningful */ static int acc_log_missed0(struct sip_msg *rq, char* p1, char* p2) { static str phrase = STR_NULL; str txt = STR_STATIC_INIT(ACC_MISSED); preparse_req(rq); return log_request(rq, GET_RURI(rq), rq->to, &txt, &phrase, time(0)); } static void ack_handler(struct cell* t, int type, struct tmcb_params* ps) { if (is_acc_on(t->uas.request)) { preparse_req(ps->req); log_ack(t, ps->req, (time_t)*(ps->param)); } } /* initiate a report if we previously enabled MC accounting for this t */ static void failure_handler(struct cell *t, int type, struct tmcb_params* ps) { /* validation */ if (t->uas.request == 0) { DBG("DBG:acc:failure_handler: No uas.request, skipping local transaction\n"); return; } if (is_invite(t) && ps->code >= 300) { if (is_mc_on(t->uas.request)) { log_missed(t, ps->rpl, ps->code, (time_t)*(ps->param)); resetflag(t->uas.request, log_missed_flag); } } } /* initiate a report if we previously enabled accounting for this t */ static void replyout_handler(struct cell* t, int type, struct tmcb_params* ps) { if (t->uas.request == 0) { DBG("DBG:acc:replyout_handler: No uas.request, local transaction, skipping\n"); return; } /* acc_onreply is bound to TMCB_REPLY which may be called * from _reply, like when FR hits; we should not miss this * event for missed calls either */ failure_handler(t, type, ps); if (!should_acc_reply(t, ps->code)) return; if (is_acc_on(t->uas.request)) log_reply(t, ps->rpl, ps->code, (time_t)*(ps->param)); } /* parse incoming replies before cloning */ static void replyin_handler(struct cell *t, int type, struct tmcb_params* ps) { /* validation */ if (t->uas.request == 0) { LOG(L_ERR, "ERROR:acc:replyin_handler:replyin_handler: 0 request\n"); return; } /* don't parse replies in which we are not interested */ /* missed calls enabled ? */ if (((is_invite(t) && ps->code >= 300 && is_mc_on(t->uas.request)) || should_acc_reply(t, ps->code)) && (ps->rpl && ps->rpl != FAKED_REPLY)) { parse_headers(ps->rpl, HDR_TO_F, 0); } } /* prepare message and transaction context for later accounting */ void on_req(struct cell* t, int type, struct tmcb_params *ps) { time_t req_time; /* Pass the timestamp of the request as a parameter to callbacks */ req_time = time(0); if (is_acc_on(ps->req) || is_mc_on(ps->req)) { if (tmb.register_tmcb(0, t, TMCB_RESPONSE_OUT, replyout_handler, (void*)req_time, 0) <= 0) { LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_RESPONSE_OUT callback\n"); return; } if (report_ack) { if (tmb.register_tmcb(0, t, TMCB_E2EACK_IN, ack_handler, (void*)req_time, 0) <= 0) { LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_E2EACK_IN callback\n"); return; } } if (tmb.register_tmcb(0, t, TMCB_ON_FAILURE_RO, failure_handler, (void*)req_time, 0) <= 0) { LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_ON_FAILURE_RO callback\n"); return; } if (tmb.register_tmcb(0, t, TMCB_RESPONSE_IN, replyin_handler, (void*)req_time, 0) <= 0) { LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_RESPONSE_IN callback\n"); return; } /* do some parsing in advance */ preparse_req(ps->req); /* also, if that is INVITE, disallow silent t-drop */ if (ps->req->REQ_METHOD == METHOD_INVITE) { DBG("DEBUG: noisy_timer set for accounting\n"); t->flags |= T_NOISY_CTIMER_FLAG; } } } static int mod_init(void) { load_tm_f load_tm; /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) { LOG(L_ERR, "ERROR:acc:mod_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) return -1; if (verify_fmt(log_fmt)==-1) return -1; /* register callbacks*/ /* listen for all incoming requests */ if (tmb.register_tmcb( 0, 0, TMCB_REQUEST_IN, on_req, 0, 0) <= 0) { LOG(L_ERR,"ERROR:acc:mod_init: cannot register TMCB_REQUEST_IN " "callback\n"); return -1; } if (parse_attrs(&avps, &avps_n, attrs) < 0) { ERR("Error while parsing 'attrs' module parameter\n"); return -1; } return 0; } kamailio-4.0.4/obsolete/acc_syslog/Makefile0000644000000000000000000000035412223032460017401 0ustar rootroot# $Id$ # # acc module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=acc_syslog.so LIBS= DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/acc_syslog/acc_syslog.xml0000644000000000000000000006643512223032460020625 0ustar rootroot acc_syslog 7 acc_syslog Transaction Accounting into the System Log Description The acc_syslog SER module stores information about processed SIP transactions in the system log. Unlike most other modules, acc_syslog is normally not used by calling one of its functions. Instead, it is invoked by setting a certain flag and then starting a transaction by calling a function from the tm module, usually t_relay. There are two flags. Their names are determined by the module parameters log_flag and log_missed_flag respectively. The former is intended to be used for all transactions that are involved in a successfully established call, while the latter is to be used for transactions of a failed call attempt. If either of the flags is set, a line is written to the system log upon completion of the transaction. The difference between the two flags lies in the text that starts the entry. If the log_flag is set, the entry starts with the text "ACC: transaction answered", while the log_missed_flag causes the entry to start with "ACC: call missed". Neither flag is set by default. In order to activate the writing of accounting entries, you have to explicitely choose flags by setting the module parameters. Normally, you can use the same flag for both parameters since acc_syslog's internal logic takes care of figuring out the right introduction. It only uses "ACC: transaction answered" if the transaction ended with a final response sent upstream with a 2xx status, ie., was successful. Likewise, the module starts entries with "ACC: call missed" if the final response sent upstream had a status of 300 or up. Note that the acc_syslog module only writes accounting entries for individual transactions. A call usually consists at least of an INVITE and a BYE transaction for the start and end of a call. Searching the accounting records for the call and translating them into call detail records is not performed by the module. Functions <function>acc_syslog_log</function> ([<symbol>status</symbol>]) Allowed in request and failure processing. The acc_syslog_log() function forces acc_syslog to write a syslog entry with information taken from the currently processed request. The entry will by prefixed with the phrase "ACC: request accounted". If the argument status is given, it contains the status code that should be stored in the field sip_status. If it is missing, the field will be set to 0. <function>acc_syslog_missed</function> ([<symbol>status</symbol>]) Allowed in request and failure processing. The acc_syslog_log() function forces acc_syslog to write a syslog entry with information taken from the currently processed request. The entry will by prefixed with the phrase "ACC: call missed". If the argument status is given, it contains the status code that should be stored in the field sip_status. If it is missing, the field will be set to 0. Module Parameters <parameter>attrs</parameter> string "" The attrs parameter contains a comma separated list of those attributes whose values should be written in the accounting entry. See the description of the field attrs in the section Syslog Entries below for information on how these values are stored. <parameter>early_media</parameter> boolean no The early_media parameter determines, whether the arrival of a reponse 183 (Session Progress) should trigger writing of an accounting entry, too. If the parameter is set to yes, a 183 response will be treated as a successful response and an entry will be written if the log_flag is set. <parameter>failed_transaction</parameter> boolean no The failed_transactions parameter determines, whether transaction with a final response indicating a failure (ie., status codes of 300 and up) should be accounted too if the log_flag is set. The value of the failed_transactions parameter has no influence on accounting if the log_missed_flag is set, in which case the transaction will be accounted for only if the final response indicates a failure. <parameter>log_flag</parameter> flag name none The log_flag parameter sets the name of the flag that decides whether information about a succeeeded transaction (ie., one with a final response from the 2xx group) is to be written to the system log. By setting the early_media and failed_transactions to yes setting the log_flag also triggers writing for 183 provisional responses and failed transactions respectively. <parameter>log_fmt</parameter> string "acdfgimnoprstuxDFIMPRSTUX" The log_fmt parameter determines, which information is written to the system log. Its value is a string. The various fields described in the section Syslog Entries below are assigned a letter. If the letter for the field is included in the string, the field will be written. The mapping between letters and fields is as follows: a attr c sip_callid d to_tag f sip_from g flags i in_ruri m sip_method n sip_cseq o out_ruri p src_ip r from_tag t sip_to u digest_username x request_timestamp D to_did F from_uri I from_uid M from_did P src_port R digest_realm S sip_status T to_uri U to_uid X response_timestamp By default, all of the fields are active. <parameter>log_level</parameter> int 2 The log_level parameter sets the log level that acc_syslog will use when writing to the syslog. Its value is an integer that correspondents to the priority values defined by syslog as follows: 4 debug 3 info 2 notice 1 warning (or warn) -1 err (or error) -2 crit -3 alert Note that the log_level must be at less or equal than the core parameter debug or no entries will be written at all. <parameter>log_missed_flag</parameter> flag name none The log_missed_flag parameter sets the name of the flag that decides whether information about a failed transaction (ie., one with a final response of status 300 or up) is to be written to the system log. <parameter>report_ack</parameter> boolean no The report_ack parameter determines, whether a separate accounting entry should be written for the ACK following a 200 OK response. If activated, such entries will be written with the starting text "ACC: request acknowledged". Usually, having the entry for the first transaction is enough and no additional entry is necessary. There is, however, a chance that the ACK does not reach its destination and the call does in fact not start. If you need to know about those cases, you can enable the report_ack parameter and check that there is an ACK for every INVITE. <parameter>report_cancels</parameter> boolean no The report_cancels parameter determines, whether accounting entries should be made for CANCEL transactions. You can recognize a canceled transaction by its status 487 in the sip_status field. Because of this, there is usually no need for the extra entries the CANCEL transaction itself may create. Syslog Entries All accounting entries that are written to the system log consist of a single line. It is started with any of the prefixes mentioned above, followed by a colon and a comma-separated list of accounting fields. Each field has a name, followed by an equals sign followed by the fields value. Which entries will be written is determined by the module parameter log_fmt. By default, it prints all available fields. The following lists and explains all the accounting fields. <varname>attrs</varname> The attrs field will contain the attribute and their values that have been selected for storing with the accounting entry. The field will contain a comma separated list of entries. Each entry will start with the name of the attribute, followed by a colon, followed by its value enclosed in double quotation marks. Which attributes will be in the list is controlled by the attrs module parameter. <varname>digest_username</varname> The digest_username field will contain the username used in authenticating the request. If no authentication was done or the authentication failed, the field will be NULL. <varname>digest_realm</varname> The digest_realm field will contain the realm used in authenticating the request. If no authentication was done or it failed, the field will be NULL. <varname>flags</varname> The flags field will contain the combined numerical value of the flags that where set for the transaction when the entry was written. The value is determined by treating the flags as a bit field with the flag's number as the number of the corresponding bit. <varname>from_did</varname> The from_did field will contain the domain ID determined for the callee's domain and stored in the $fd.did attribute or NULL if the attribute was not set. <varname>from_tag</varname> The from_tag field will contain the value of the tag parameter of the From header field of the request. <varname>from_uid</varname> The from_uid field will contain the user ID determined for the callee and stored in the $fu.uid attribute or NULL if the attribute was not set. <varname>from_uri</varname> The from_uri field will contain the URI of the From header field of the request. <varname>in_ruri</varname> The in_ruri field will contain the Request-URI the request arrived with. <varname>out_ruri</varname> The out_ruri field will contain the Request-URI of the winning branch, ie., of that relayed request which caused the final response that was sent upstram. <varname>request_timestamp</varname> The request_timestamp field will contain the date and time when the request was received, ie., when the transaction started. <varname>response_timestamp</varname> The response_timestamp field will contain the date and time when the response was sent upstream, ie. when the transaction ended. <varname>server_id</varname> The server_id field will contain the server ID of the SER instance that processed the transaction. This is useful in a cluster of several SER machines. By giving each machine its own server_id you can later determine, which server the accounting entry originated from. <varname>sip_callid</varname> The sip_callid field will contain the content of the Call-ID header field of the request. <varname>sip_cseq</varname> The sip_cseq field will contain the sequence number contained in the CSeq header field of the request. The method in that header field (which should be identical to the method of the request) can be found in the sip_method field. <varname>sip_from</varname> The sip_from field will contain the content of the From header field of the request. <varname>sip_method</varname> The sip_method field will contain the method of the request. <varname>sip_status</varname> The sip_status field will contain the status code of the final response or, if early_media is set, 183 response transmitted upstream for the transaction. <varname>sip_to</varname> The sip_to field will contain the content of the To header field of the request. <varname>src_ip</varname> The src_ip field will contain the source IP address of the received request. <varname>src_port</varname> The src_port field will contain the source port of the received request. <varname>to_did</varname> The to_did field will contain the domain ID determined for the caller's domain and stored in the $td.did attribute or NULL if the attribute was not set. <varname>to_tag</varname> The to_tag field will contain the value of the tag parameter of the response sent upstream or NULL if the response was missing this parameter. <varname>to_uid</varname> The to_uid field will contain the user ID determined for the caller and stored in the $tu.uid attribute or NULL if the attribute was not set. <varname>to_uri</varname> The to_uri field will contain the URI of the To header field of the request. See Also ser ser.cfg acc_db acc_radius tm syslogd kamailio-4.0.4/obsolete/jabber_s/0000755000000000000000000000000012223032460015360 5ustar rootrootkamailio-4.0.4/obsolete/jabber_s/xjab_jconf.h0000644000000000000000000000324212223032460017635 0ustar rootroot/* * $Id$ * * eXtended JABber module * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _XJAB_JCONF_H_ #define _XHAB_JCONF_H_ #include "../../str.h" #define XJ_JCONF_NULL 0 #define XJ_JCONF_READY 1 #define XJ_JCONF_WAITING 2 #define XJ_JCONF_AUTH 4 /********** ***/ typedef struct _xj_jconf { int jcid; int status; str uri; str room; str server; str nick; str passwd; } t_xj_jconf, *xj_jconf; xj_jconf xj_jconf_new(str *u); int xj_jconf_init_sip(xj_jconf jcf, str *sid, char dl); int xj_jconf_init_jab(xj_jconf jcf); int xj_jconf_set_status(xj_jconf jcf, int s); int xj_jconf_cmp(void *a, void *b); int xj_jconf_free(xj_jconf jcf); int xj_jconf_check_addr(str *addr, char dl); #endif kamailio-4.0.4/obsolete/jabber_s/ser-jabber.cfg0000644000000000000000000000057712223032460020066 0ustar rootroot# # Minimalistic configuration file for SER that can be used to # test the jabber module. # debug = 4 fork = no children = 1 log_stderror = yes listen=127.0.0.1 loadpath "./modules" loadmodule "mysql" loadmodule "sl" loadmodule "tm" loadmodule "jabber" modparam("jabber", "db_url", "mysql://ser:heslo@localhost/ser") modparam("jabber", "jdomain", "iptel.org") route { break; } kamailio-4.0.4/obsolete/jabber_s/xode.h0000644000000000000000000002330412223032460016472 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include #include #include #include #include #include #include #include #include #include "expat.h" #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ /* ** Arrange to use either varargs or stdargs */ #define MAXSHORTSTR 203 /* max short string length */ #define QUAD_T unsigned long long #ifdef __STDC__ #include # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap, f) # define VA_END va_end(ap) #else /* __STDC__ */ # include # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap) # define VA_END va_end(ap) #endif /* __STDC__ */ #ifndef INCL_LIBXODE_H #define INCL_LIBXODE_H #ifdef __cplusplus extern "C" { #endif #ifndef HAVE_SNPRINTF extern int ap_snprintf(char *, size_t, const char *, ...); #define snprintf ap_snprintf #endif #ifndef HAVE_VSNPRINTF extern int ap_vsnprintf(char *, size_t, const char *, va_list ap); #define vsnprintf ap_vsnprintf #endif /* --------------------------------------------------------- */ /* */ /* Pool-based memory management routines */ /* */ /* --------------------------------------------------------- */ /* xode_pool_cleaner - callback type which is associated with a pool entry; invoked when the pool entry is free'd */ typedef void (*xode_pool_cleaner)(void *arg); /* pheap - singular allocation of memory */ struct xode_pool_heap { void *block; int size, used; }; /* pool - base node for a pool. Maintains a linked list of pool entries (pool_free) */ typedef struct xode_pool_struct { int size; struct xode_pool_free *cleanup; struct xode_pool_heap *heap; } _xode_pool, *xode_pool; /* pool creation routines */ xode_pool xode_pool_heap(int bytes); xode_pool xode_pool_new(void); /* pool wrappers for malloc */ void *xode_pool_malloc (xode_pool p, int size); void *xode_pool_mallocx (xode_pool p, int size, char c); void *xode_pool_malloco (xode_pool p, int size); /* wrapper around strdup, gains mem from pool */ char *xode_pool_strdup (xode_pool p, const char *src); /* calls f(arg) before the pool is freed during cleanup */ void xode_pool_cleanup (xode_pool p, xode_pool_cleaner f, void *arg); /* pool wrapper for free, called on a pool */ void xode_pool_free (xode_pool p); /* returns total bytes allocated in this pool */ int xode_pool_size (xode_pool p); /* --------------------------------------------------------- */ /* */ /* XML escaping utils */ /* */ /* --------------------------------------------------------- */ char *xode_strescape(xode_pool p, char *buf); /* Escape <>&'" chars */ char *xode_strunescape(xode_pool p, char *buf); /* --------------------------------------------------------- */ /* */ /* String pools (spool) functions */ /* */ /* --------------------------------------------------------- */ struct xode_spool_node { char *c; struct xode_spool_node *next; }; typedef struct xode_spool_struct { xode_pool p; int len; struct xode_spool_node *last; struct xode_spool_node *first; } *xode_spool; xode_spool xode_spool_new ( void ); /* create a string pool on a new pool */ xode_spool xode_spool_newfrompool ( xode_pool p ); /* create a string pool from an existing pool */ xode_pool xode_spool_getpool ( const xode_spool s ); /* returns the xode_pool used by this xode_spool */ void xode_spooler ( xode_spool s, ... ); /* append all the char * args to the pool, terminate args with s again */ char *xode_spool_tostr ( xode_spool s ); /* return a big string */ void xode_spool_add ( xode_spool s, char *str ); /* add a single char to the pool */ char *xode_spool_str ( xode_pool p, ... ); /* wrap all the spooler stuff in one function, the happy fun ball! */ int xode_spool_getlen ( const xode_spool s ); /* returns the total length of the string contained in the pool */ void xode_spool_free ( xode_spool s ); /* Free's the pool associated with the xode_spool */ /* --------------------------------------------------------- */ /* */ /* xodes - Document Object Model */ /* */ /* --------------------------------------------------------- */ #define XODE_TYPE_TAG 0 #define XODE_TYPE_ATTRIB 1 #define XODE_TYPE_CDATA 2 #define XODE_TYPE_LAST 2 #define XODE_TYPE_UNDEF -1 /* -------------------------------------------------------------------------- Node structure. Do not use directly! Always use accessors macros and methods! -------------------------------------------------------------------------- */ typedef struct xode_struct { char* name; unsigned short type; char* data; int data_sz; int complete; xode_pool p; struct xode_struct* parent; struct xode_struct* firstchild; struct xode_struct* lastchild; struct xode_struct* prev; struct xode_struct* next; struct xode_struct* firstattrib; struct xode_struct* lastattrib; } _xode, *xode; /* Node creation routines */ xode xode_wrap(xode x,const char* wrapper); xode xode_new(const char* name); xode xode_new_tag(const char* name); xode xode_new_frompool(xode_pool p, const char* name); xode xode_insert_tag(xode parent, const char* name); xode xode_insert_cdata(xode parent, const char* CDATA, unsigned int size); xode xode_insert_tagnode(xode parent, xode node); void xode_insert_node(xode parent, xode node); xode xode_from_str(char *str, int len); xode xode_from_strx(char *str, int len, int *err, int *pos); xode xode_from_file(char *file); xode xode_dup(xode x); /* duplicate x */ xode xode_dup_frompool(xode_pool p, xode x); /* Node Memory Pool */ xode_pool xode_get_pool(xode node); /* Node editing */ void xode_hide(xode child); void xode_hide_attrib(xode parent, const char *name); /* Node deletion routine, also frees the node pool! */ void xode_free(xode node); /* Locates a child tag by name and returns it */ xode xode_get_tag(xode parent, const char* name); char* xode_get_tagdata(xode parent, const char* name); /* Attribute accessors */ void xode_put_attrib(xode owner, const char* name, const char* value); char* xode_get_attrib(xode owner, const char* name); /* Bastard am I, but these are fun for internal use ;-) */ void xode_put_vattrib(xode owner, const char* name, void *value); void* xode_get_vattrib(xode owner, const char* name); /* Node traversal routines */ xode xode_get_firstattrib(xode parent); xode xode_get_firstchild(xode parent); xode xode_get_lastchild(xode parent); xode xode_get_nextsibling(xode sibling); xode xode_get_prevsibling(xode sibling); xode xode_get_parent(xode node); /* Node information routines */ char* xode_get_name(xode node); char* xode_get_data(xode node); int xode_get_datasz(xode node); int xode_get_type(xode node); int xode_has_children(xode node); int xode_has_attribs(xode node); /* Node-to-string translation */ char* xode_to_str(xode node); char* xode_to_prettystr(xode node); /* Puts \t and \n to make a human-easily readable string */ int xode_cmp(xode a, xode b); /* compares a and b for equality */ int xode_to_file(char *file, xode node); /* writes node to file */ /*********************** * XSTREAM Section ***********************/ #define XODE_STREAM_MAXNODE 1000000 #define XODE_STREAM_MAXDEPTH 100 #define XODE_STREAM_ROOT 0 /* root element */ #define XODE_STREAM_NODE 1 /* normal node */ #define XODE_STREAM_CLOSE 2 /* closed root node */ #define XODE_STREAM_ERROR 4 /* parser error */ typedef void (*xode_stream_onNode)(int type, xode x, void *arg); /* xstream event handler */ typedef struct xode_stream_struct { XML_Parser parser; xode node; char *cdata; int cdata_len; xode_pool p; xode_stream_onNode f; void *arg; int status; int depth; } *xode_stream, _xode_stream; xode_stream xode_stream_new(xode_pool p, xode_stream_onNode f, void *arg); /* create a new xstream */ int xode_stream_eat(xode_stream xs, char *buff, int len); /* parse new data for this xstream, returns last XSTREAM_* status */ /* convenience functions */ #ifdef __cplusplus } #endif #endif /* INCL_LIBXODE_H */ kamailio-4.0.4/obsolete/jabber_s/xjab_jcon.h0000644000000000000000000000656712223032460017504 0ustar rootroot/* * $Id$ * * eXtended JABber module - headers for functions used for JABBER srv connection * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _XJAB_JCON_H_ #define _XJAB_JCON_H_ #include "../../str.h" #include "xjab_jconf.h" #include "xjab_base.h" #include "tree234.h" #include "xjab_presence.h" #define XJ_NET_NUL 0 #define XJ_NET_ALL 0xFFFFFFFF #define XJ_NET_JAB 1 #define XJ_NET_AIM 2 #define XJ_NET_ICQ 4 #define XJ_NET_MSN 8 #define XJ_NET_YAH 16 #define XJ_AIM_NAME "aim." #define XJ_AIM_LEN 4 #define XJ_ICQ_NAME "icq" #define XJ_ICQ_LEN 3 #define XJ_MSN_NAME "msn." #define XJ_MSN_LEN 4 #define XJ_YAH_NAME "yahoo." #define XJ_YAH_LEN 6 #define XJ_JMSG_NORMAL 1 #define XJ_JMSG_CHAT 2 #define XJ_JMSG_GROUPCHAT 4 #define XJ_JCMD_SUBSCRIBE 1 #define XJ_JCMD_UNSUBSCRIBE 2 typedef struct _xj_jcon { int sock; // communication socket int port; // port of the server int juid; // internal id of the Jabber user int seq_nr; // sequence number char *hostname; // hostname of the Jabber server char *stream_id; // stream id of the session char *resource; // resource ID xj_jkey jkey; // id of connection int expire; // time when the open connection is expired int allowed; // allowed IM networks int ready; // time when the connection is ready for sending messages int nrjconf; // number of open conferences tree234 *jconf; // open conferences xj_pres_list plist; // presence list } t_xj_jcon, *xj_jcon; /** --- **/ xj_jcon xj_jcon_init(char*, int); int xj_jcon_free(xj_jcon); int xj_jcon_connect(xj_jcon); int xj_jcon_disconnect(xj_jcon); void xj_jcon_set_juid(xj_jcon, int); int xj_jcon_get_juid(xj_jcon); int xj_jcon_get_roster(xj_jcon); int xj_jcon_set_roster(xj_jcon, char*, char*); int xj_jcon_user_auth(xj_jcon, char*, char*, char*); int xj_jcon_send_presence(xj_jcon, char*, char*, char*, char*); int xj_jcon_send_subscribe(xj_jcon, char*, char*, char*); int xj_jcon_send_msg(xj_jcon, char*, int, char*, int, int); int xj_jcon_send_sig_msg(xj_jcon, char*, int, char*, int, char*, int); int xj_jcon_is_ready(xj_jcon, char *, int, char); xj_jconf xj_jcon_get_jconf(xj_jcon, str*, char); xj_jconf xj_jcon_check_jconf(xj_jcon, char*); int xj_jcon_del_jconf(xj_jcon, str*, char, int); int xj_jcon_jconf_presence(xj_jcon, xj_jconf, char*, char*); /********** ***/ int xj_jcon_set_attrs(xj_jcon, xj_jkey, int, int); int xj_jcon_update(xj_jcon, int); /********** ***/ #endif kamailio-4.0.4/obsolete/jabber_s/xjab_worker.h0000644000000000000000000000666112223032460020057 0ustar rootroot/* * $Id$ * * eXtended JABber module * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2003-03-11 major locking changes - uses locking.h (andrei) * 2004-07-28 s/lock_set_t/gen_lock_set_t/ because of a type conflict * on darwin (andrei) */ #ifndef _XJAB_WORKER_H_ #define _XJAB_WORKER_H_ #include "../../str.h" #include "../../lib/srdb2/db.h" #include "../../locking.h" #include "../../modules/tm/tm_load.h" #include "xjab_util.h" #include "tree234.h" /********** ***/ typedef struct _xj_jalias { int size; // number of aliases str *jdm; // Jabber domain char dlm; // user part delimiter str *proxy; // outbound proxy str *a; // aliases char *d; // user part delimiter for aliases } t_xj_jalias, *xj_jalias; typedef struct _xj_worker { int pid; // process id int wpipe; // communication pipe - write int rpipe; // communication pipe - read int nr; // number of jobs tree234 *sip_ids; // sip ids allocated for the worker } t_xj_worker, *xj_worker; typedef struct _xj_wlist { int len; // length of the list int maxj; // maximum jobs / worker int cachet; int delayt; int sleept; gen_lock_set_t *sems; // semaphores xj_jalias aliases; // added aliases xj_worker workers; // the list of workers } t_xj_wlist, *xj_wlist; /********** LOOK AT IMPLEMENTATION OF FUNCTIONS FOR DESCRIPTION ***/ xj_wlist xj_wlist_init(int **, int, int, int, int, int); int xj_wlist_set_pid(xj_wlist, int, int); int xj_wlist_get(xj_wlist, xj_jkey, xj_jkey*); int xj_wlist_check(xj_wlist, xj_jkey, xj_jkey*); int xj_wlist_set_flag(xj_wlist, xj_jkey, int); void xj_wlist_del(xj_wlist, xj_jkey, int); void xj_wlist_free(xj_wlist); int xj_wlist_set_aliases(xj_wlist, char *, char *, char *); int xj_wlist_check_aliases(xj_wlist, str*); int xj_wlist_clean_jobs(xj_wlist, int, int); int xj_worker_process(xj_wlist, char*, int, int, db_cmd_t*); int xj_address_translation(str *src, str *dst, xj_jalias als, int flag); int xj_manage_jab(char *buf, int len, int *pos, xj_jalias als, xj_jcon jbc); void xj_sig_handler(int s); /********** ***/ int xj_send_sip_msg(str *, str *, str *, str *, int *); int xj_send_sip_msgz(str *,str *, str *, char *, int *); void xj_tuac_callback( struct cell *t, int type, struct tmcb_params *ps); void xj_worker_check_jcons(xj_wlist, xj_jcon_pool, int, fd_set*); void xj_worker_check_qmsg(xj_wlist, xj_jcon_pool); void xj_worker_check_watcher(xj_wlist, xj_jcon_pool, xj_jcon, xj_sipmsg); #endif kamailio-4.0.4/obsolete/jabber_s/xode.c0000644000000000000000000004735012223032460016474 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include "xode.h" static int _xode_strcmp(const char *a, const char *b) { if(a == NULL || b == NULL) return -1; return strcmp(a,b); } /* Internal routines */ static xode _xode_new(xode_pool p, const char* name, unsigned int type) { xode result = NULL; if (type > XODE_TYPE_LAST) return NULL; if (type != XODE_TYPE_CDATA && name == NULL) return NULL; if (p == NULL) { p = xode_pool_heap(1*1024); } /* Allocate & zero memory */ result = (xode)xode_pool_malloc(p, sizeof(_xode)); memset(result, '\0', sizeof(_xode)); /* Initialize fields */ if (type != XODE_TYPE_CDATA) result->name = xode_pool_strdup(p,name); result->type = type; result->p = p; return result; } static xode _xode_appendsibling(xode lastsibling, const char* name, unsigned int type) { xode result; result = _xode_new(xode_get_pool(lastsibling), name, type); if (result != NULL) { /* Setup sibling pointers */ result->prev = lastsibling; lastsibling->next = result; } return result; } static xode _xode_insert(xode parent, const char* name, unsigned int type) { xode result; if(parent == NULL || name == NULL) return NULL; /* If parent->firstchild is NULL, simply create a new node for the first child */ if (parent->firstchild == NULL) { result = _xode_new(parent->p, name, type); parent->firstchild = result; } /* Otherwise, append this to the lastchild */ else { result= _xode_appendsibling(parent->lastchild, name, type); } result->parent = parent; parent->lastchild = result; return result; } static xode _xode_search(xode firstsibling, const char* name, unsigned int type) { xode current; /* Walk the sibling list, looking for a XODE_TYPE_TAG xode with the specified name */ current = firstsibling; while (current != NULL) { if (name != NULL && (current->type == type) && (_xode_strcmp(current->name, name) == 0)) return current; else current = current->next; } return NULL; } static char* _xode_merge(xode_pool p, char* dest, unsigned int destsize, const char* src, unsigned int srcsize) { char* result; result = (char*)xode_pool_malloc(p, destsize + srcsize + 1); memcpy(result, dest, destsize); memcpy(result+destsize, src, srcsize); result[destsize + srcsize] = '\0'; /* WARNING: major ugly hack: since we're throwing the old data away, let's jump in the xode_pool and subtract it from the size, this is for xmlstream's big-node checking */ p->size -= destsize; return result; } static void _xode_hidesibling(xode child) { if(child == NULL) return; if(child->prev != NULL) child->prev->next = child->next; if(child->next != NULL) child->next->prev = child->prev; } static void _xode_tag2str(xode_spool s, xode node, int flag) { xode tmp; if(flag==0 || flag==1) { xode_spooler(s,"<",xode_get_name(node),s); tmp = xode_get_firstattrib(node); while(tmp) { xode_spooler(s," ",xode_get_name(tmp),"='",xode_strescape(xode_get_pool(node),xode_get_data(tmp)),"'",s); tmp = xode_get_nextsibling(tmp); } if(flag==0) xode_spool_add(s,"/>"); else xode_spool_add(s,">"); } else { xode_spooler(s,"",s); } } static xode_spool _xode_tospool(xode node) { xode_spool s; int level=0,dir=0; xode tmp; if(!node || xode_get_type(node) != XODE_TYPE_TAG) return NULL; s = xode_spool_newfrompool(xode_get_pool(node)); if(!s) return(NULL); while(1) { if(dir==0) { if(xode_get_type(node) == XODE_TYPE_TAG) { if(xode_has_children(node)) { _xode_tag2str(s,node,1); node = xode_get_firstchild(node); level++; continue; } else { _xode_tag2str(s,node,0); } } else { xode_spool_add(s,xode_strescape(xode_get_pool(node),xode_get_data(node))); } } tmp = xode_get_nextsibling(node); if(!tmp) { node = xode_get_parent(node); level--; if(level>=0) _xode_tag2str(s,node,2); if(level<1) break; dir = 1; } else { node = tmp; dir = 0; } } return s; } /* External routines */ /* * xode_new_tag -- create a tag node * Automatically creates a memory xode_pool for the node. * * parameters * name -- name of the tag * * returns * a pointer to the tag node * or NULL if it was unsuccessful */ xode xode_new(const char* name) { return _xode_new(NULL, name, XODE_TYPE_TAG); } /* * alias for 'xode_new' */ xode xode_new_tag(const char* name) { return _xode_new(NULL, name, XODE_TYPE_TAG); } /* * xode_new_tag_pool -- create a tag node within given pool * * parameters * p -- previously created memory pool * name -- name of the tag * * returns * a pointer to the tag node * or NULL if it was unsuccessful */ xode xode_new_frompool(xode_pool p, const char* name) { return _xode_new(p, name, XODE_TYPE_TAG); } /* * xode_insert_tag -- append a child tag to a tag * * parameters * parent -- pointer to the parent tag * name -- name of the child tag * * returns * a pointer to the child tag node * or NULL if it was unsuccessful */ xode xode_insert_tag(xode parent, const char* name) { return _xode_insert(parent, name, XODE_TYPE_TAG); } /* * xode_insert_cdata -- append character data to a tag * If last child of the parent is CDATA, merges CDATA nodes. Otherwise * creates a CDATA node, and appends it to the parent's child list. * * parameters * parent -- parent tag * CDATA -- character data * size -- size of CDATA * or -1 for null-terminated CDATA strings * * returns * a pointer to the child CDATA node * or NULL if it was unsuccessful */ xode xode_insert_cdata(xode parent, const char* CDATA, unsigned int size) { xode result; if(CDATA == NULL || parent == NULL) return NULL; if(size == -1) size = strlen(CDATA); if ((parent->lastchild != NULL) && (parent->lastchild->type == XODE_TYPE_CDATA)) { result = parent->lastchild; result->data = _xode_merge(result->p, result->data, result->data_sz, CDATA, size); result->data_sz = result->data_sz + size; } else { result = _xode_insert(parent, "", XODE_TYPE_CDATA); if (result != NULL) { result->data = (char*)xode_pool_malloc(result->p, size + 1); memcpy(result->data, CDATA, size); result->data[size] = '\0'; result->data_sz = size; } } return result; } /* * xode_gettag -- find given tag in an xode tree * * parameters * parent -- pointer to the parent tag * name -- "name" for the child tag of that name * "name/name" for a sub child (recurses) * "?attrib" to match the first tag with that attrib defined * "?attrib=value" to match the first tag with that attrib and value * or any combination: "name/name/?attrib", etc * * results * a pointer to the tag matching search criteria * or NULL if search was unsuccessful */ xode xode_get_tag(xode parent, const char* name) { char *str, *slash, *qmark, *equals; xode step, ret; if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL; if(strstr(name, "/") == NULL && strstr(name,"?") == NULL) return _xode_search(parent->firstchild, name, XODE_TYPE_TAG); /* jer's note: why can't I modify the name directly, why do I have to strdup it? damn c grrr! */ str = strdup(name); slash = strstr(str, "/"); qmark = strstr(str, "?"); equals = strstr(str, "="); if(qmark != NULL && (slash == NULL || qmark < slash)) { /* of type ?attrib */ *qmark = '\0'; qmark++; if(equals != NULL) { *equals = '\0'; equals++; } for(step = parent->firstchild; step != NULL; step = xode_get_nextsibling(step)) { if(xode_get_type(step) != XODE_TYPE_TAG) continue; if(*str != '\0') if(_xode_strcmp(xode_get_name(step),str) != 0) continue; if(xode_get_attrib(step,qmark) == NULL) continue; if(equals != NULL && _xode_strcmp(xode_get_attrib(step,qmark),equals) != 0) continue; break; } free(str); return step; } *slash = '\0'; ++slash; for(step = parent->firstchild; step != NULL; step = xode_get_nextsibling(step)) { if(xode_get_type(step) != XODE_TYPE_TAG) continue; if(_xode_strcmp(xode_get_name(step),str) != 0) continue; ret = xode_get_tag(step, slash); if(ret != NULL) { free(str); return ret; } } free(str); return NULL; } /* return the cdata from any tag */ char *xode_get_tagdata(xode parent, const char *name) { xode tag; tag = xode_get_tag(parent, name); if(tag == NULL) return NULL; return xode_get_data(tag); } void xode_put_attrib(xode owner, const char* name, const char* value) { xode attrib; if(owner == NULL || name == NULL || value == NULL) return; /* If there are no existing attributes, allocate a new one to start the list */ if (owner->firstattrib == NULL) { attrib = _xode_new(owner->p, name, XODE_TYPE_ATTRIB); owner->firstattrib = attrib; owner->lastattrib = attrib; } else { attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); if(attrib == NULL) { attrib = _xode_appendsibling(owner->lastattrib, name, XODE_TYPE_ATTRIB); owner->lastattrib = attrib; } } /* Update the value of the attribute */ attrib->data_sz = strlen(value); attrib->data = xode_pool_strdup(owner->p, value); } char* xode_get_attrib(xode owner, const char* name) { xode attrib; if (owner != NULL && owner->firstattrib != NULL) { attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); if (attrib != NULL) return (char*)attrib->data; } return NULL; } void xode_put_vattrib(xode owner, const char* name, void *value) { xode attrib; if (owner != NULL) { attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); if (attrib == NULL) { xode_put_attrib(owner, name, ""); attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); } if (attrib != NULL) attrib->firstchild = (xode)value; } } void* xode_get_vattrib(xode owner, const char* name) { xode attrib; if (owner != NULL && owner->firstattrib != NULL) { attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); if (attrib != NULL) return (void*)attrib->firstchild; } return NULL; } xode xode_get_firstattrib(xode parent) { if (parent != NULL) return parent->firstattrib; return NULL; } xode xode_get_firstchild(xode parent) { if (parent != NULL) return parent->firstchild; return NULL; } xode xode_get_lastchild(xode parent) { if (parent != NULL) return parent->lastchild; return NULL; } xode xode_get_nextsibling(xode sibling) { if (sibling != NULL) return sibling->next; return NULL; } xode xode_get_prevsibling(xode sibling) { if (sibling != NULL) return sibling->prev; return NULL; } xode xode_get_parent(xode node) { if (node != NULL) return node->parent; return NULL; } char* xode_get_name(xode node) { if (node != NULL) return node->name; return NULL; } char* xode_get_data(xode node) { xode cur; if(node == NULL) return NULL; if(xode_get_type(node) == XODE_TYPE_TAG) /* loop till we find a CDATA */ { for(cur = xode_get_firstchild(node); cur != NULL; cur = xode_get_nextsibling(cur)) if(xode_get_type(cur) == XODE_TYPE_CDATA) return cur->data; }else{ return node->data; } return NULL; } int xode_get_datasz(xode node) { if( node == NULL ) { return (int)NULL; } else if(xode_get_type(node) == XODE_TYPE_TAG) /* loop till we find a CDATA */ { xode cur; for(cur = xode_get_firstchild(node); cur != NULL; cur = xode_get_nextsibling(cur)) if(xode_get_type(cur) == XODE_TYPE_CDATA) return cur->data_sz; }else{ return node->data_sz; } return (int)NULL; } int xode_get_type(xode node) { if (node != NULL) { return node->type; } return (int)NULL; } int xode_has_children(xode node) { if ((node != NULL) && (node->firstchild != NULL)) return 1; return 0; } int xode_has_attribs(xode node) { if ((node != NULL) && (node->firstattrib != NULL)) return 1; return 0; } xode_pool xode_get_pool(xode node) { if (node != NULL) return node->p; return (xode_pool)NULL; } void xode_hide(xode child) { xode parent; if(child == NULL || child->parent == NULL) return; parent = child->parent; /* first fix up at the child level */ _xode_hidesibling(child); /* next fix up at the parent level */ if(parent->firstchild == child) parent->firstchild = child->next; if(parent->lastchild == child) parent->lastchild = child->prev; } void xode_hide_attrib(xode parent, const char *name) { xode attrib; if(parent == NULL || parent->firstattrib == NULL || name == NULL) return; attrib = _xode_search(parent->firstattrib, name, XODE_TYPE_ATTRIB); if(attrib == NULL) return; /* first fix up at the child level */ _xode_hidesibling(attrib); /* next fix up at the parent level */ if(parent->firstattrib == attrib) parent->firstattrib = attrib->next; if(parent->lastattrib == attrib) parent->lastattrib = attrib->prev; } /* * xode2str -- convert given xode tree into a string * * parameters * node -- pointer to the xode structure * * results * a pointer to the created string * or NULL if it was unsuccessful */ char *xode_to_str(xode node) { return xode_spool_tostr(_xode_tospool(node)); } /* loop through both a and b comparing everything, attribs, cdata, children, etc */ int xode_cmp(xode a, xode b) { int ret = 0; while(1) { if(a == NULL && b == NULL) return 0; if(a == NULL || b == NULL) return -1; if(xode_get_type(a) != xode_get_type(b)) return -1; switch(xode_get_type(a)) { case XODE_TYPE_ATTRIB: ret = _xode_strcmp(xode_get_name(a), xode_get_name(b)); if(ret != 0) return -1; ret = _xode_strcmp(xode_get_data(a), xode_get_data(b)); if(ret != 0) return -1; break; case XODE_TYPE_TAG: ret = _xode_strcmp(xode_get_name(a), xode_get_name(b)); if(ret != 0) return -1; ret = xode_cmp(xode_get_firstattrib(a), xode_get_firstattrib(b)); if(ret != 0) return -1; ret = xode_cmp(xode_get_firstchild(a), xode_get_firstchild(b)); if(ret != 0) return -1; break; case XODE_TYPE_CDATA: ret = _xode_strcmp(xode_get_data(a), xode_get_data(b)); if(ret != 0) return -1; } a = xode_get_nextsibling(a); b = xode_get_nextsibling(b); } } xode xode_insert_tagnode(xode parent, xode node) { xode child; child = xode_insert_tag(parent, xode_get_name(node)); if (xode_has_attribs(node)) xode_insert_node(child, xode_get_firstattrib(node)); if (xode_has_children(node)) xode_insert_node(child, xode_get_firstchild(node)); return child; } /* places copy of node and node's siblings in parent */ void xode_insert_node(xode parent, xode node) { if(node == NULL || parent == NULL) return; while(node != NULL) { switch(xode_get_type(node)) { case XODE_TYPE_ATTRIB: xode_put_attrib(parent, xode_get_name(node), xode_get_data(node)); break; case XODE_TYPE_TAG: xode_insert_tagnode(parent, node); break; case XODE_TYPE_CDATA: xode_insert_cdata(parent, xode_get_data(node), xode_get_datasz(node)); } node = xode_get_nextsibling(node); } } /* produce full duplicate of x with a new xode_pool, x must be a tag! */ xode xode_dup(xode x) { xode x2; if(x == NULL) return NULL; x2 = xode_new(xode_get_name(x)); if (xode_has_attribs(x)) xode_insert_node(x2, xode_get_firstattrib(x)); if (xode_has_children(x)) xode_insert_node(x2, xode_get_firstchild(x)); return x2; } xode xode_dup_frompool(xode_pool p, xode x) { xode x2; if(x == NULL) return NULL; x2 = xode_new_frompool(p, xode_get_name(x)); if (xode_has_attribs(x)) xode_insert_node(x2, xode_get_firstattrib(x)); if (xode_has_children(x)) xode_insert_node(x2, xode_get_firstchild(x)); return x2; } xode xode_wrap(xode x,const char *wrapper) { xode wrap; if(x==NULL||wrapper==NULL) return NULL; wrap=xode_new_frompool(xode_get_pool(x),wrapper); if(wrap==NULL) return NULL; wrap->firstchild=x; wrap->lastchild=x; x->parent=wrap; return wrap; } void xode_free(xode node) { if(node == NULL) return; xode_pool_free(node->p); } void _xode_to_prettystr( xode_spool s, xode x, int deep ) { int i; xode y; if(xode_get_type(x) != XODE_TYPE_TAG) return; for(i=0; i"); xode_spool_add(s,"\n"); if( xode_get_data(x)) { for(i=0; i<=deep; i++) xode_spool_add(s, "\t"); xode_spool_add( s , xode_get_data(x)); } y = xode_get_firstchild(x); while( y ) { _xode_to_prettystr(s , y, deep+1); y = xode_get_nextsibling(y); xode_spool_add(s,"\n"); } for(i=0; i" , s ); return; } char * xode_to_prettystr( xode x ) { xode_spool s; if( !x) return NULL; s = xode_spool_newfrompool( xode_get_pool(x)); _xode_to_prettystr( s , x, 0 ); return xode_spool_tostr(s); } kamailio-4.0.4/obsolete/jabber_s/xjab_base.h0000644000000000000000000000453512223032460017456 0ustar rootroot/** * $Id$ * * eXtended JABber module * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*** * --- * * History * ------- * 2003-06-05 previously added macro replaced with 'xj_extract_aor', (dcm) * 2003-05-09 added macro for adjusting a SIP address, (dcm) */ #ifndef _XJAB_BASE_H_ #define _XJAB_BASE_H_ #include "../../str.h" #define XJ_NULL 0 #define XJ_SEND_MESSAGE 1 #define XJ_JOIN_JCONF 2 #define XJ_EXIT_JCONF 4 #define XJ_GO_ONLINE 8 #define XJ_GO_OFFLINE 16 #define XJ_REG_WATCHER 32 #define XJ_DEL_WATCHER 64 #define XJ_FLAG_OPEN 0 #define XJ_FLAG_CLOSE 1 typedef void (*pa_callback_f)(str* _user, str* _contact, int _state, void *p); /********** ***/ typedef struct _xj_jkey { int hash; int flag; str *id; } t_xj_jkey, *xj_jkey; /********** ***/ typedef struct _xj_sipmsg { int type; // type of message xj_jkey jkey; // pointer to FROM str to; // destination str msg; // message body pa_callback_f cbf; // callback function void *p; // callback parameter } t_xj_sipmsg, *xj_sipmsg; /********** LOOK AT IMPLEMENTATION OF FUNCTIONS FOR DESCRIPTION ***/ void xj_sipmsg_free(xj_sipmsg); /********** ***/ int xj_jkey_cmp(void*, void*); void xj_jkey_free_p(void*); void xj_jkey_free(xj_jkey); /********** ***/ int xj_get_hash(str*, str*); char *shahash(const char *); int xj_extract_aor(str*, int); #endif kamailio-4.0.4/obsolete/jabber_s/xjab_jconf.c0000644000000000000000000001320212223032460017625 0ustar rootroot/* * $Id$ * * eXtended JABber module * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "xjab_jconf.h" #include "xjab_base.h" /** * */ xj_jconf xj_jconf_new(str *u) { xj_jconf jcf = NULL; if(!u || !u->s || u->len<=0) return NULL; jcf = (xj_jconf)pkg_malloc(sizeof(t_xj_jconf)); if(jcf == NULL) { DBG("XJAB:xj_jconf_new: error - no pkg memory.\n"); return NULL; } jcf->uri.s = (char*)pkg_malloc((u->len+1)*sizeof(char)); if(jcf->uri.s == NULL) { DBG("XJAB:xj_jconf_new: error - no pkg memory!\n"); pkg_free(jcf); return NULL; } strncpy(jcf->uri.s, u->s, u->len); jcf->uri.len = u->len; jcf->uri.s[jcf->uri.len] = 0; jcf->jcid = 0; jcf->status = XJ_JCONF_NULL; jcf->room.s = NULL; jcf->room.len = 0; jcf->server.s = NULL; jcf->server.len = 0; jcf->nick.s = NULL; jcf->nick.len = 0; return jcf; } /** * */ int xj_jconf_init_sip(xj_jconf jcf, str *sid, char dl) { char *p, *p0; int n = 0; if(!jcf || !jcf->uri.s || jcf->uri.len <= 0 || !sid || !sid->s || sid->len <= 0) return -1; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jconf_init_sip: parsing uri\n"); #endif p = jcf->uri.s; while(p<(jcf->uri.s + jcf->uri.len) && *p != '@') p++; if(*p != '@') goto bad_format; p0 = p; while(p0 > jcf->uri.s) { p0--; if(*p0 == dl) { switch(n) { case 0: jcf->server.s = p0+1; jcf->server.len = p - jcf->server.s; break; case 1: jcf->room.s = p0+1; jcf->room.len = p - jcf->room.s; break; case 2: jcf->nick.s = p0+1; jcf->nick.len = p - jcf->nick.s; break; } n++; p = p0; } } if(n != 2 || p0 != jcf->uri.s) goto bad_format; if(p0 == jcf->uri.s && *p0 != dl) { jcf->nick.s = p0; jcf->nick.len = p - jcf->nick.s; } else { jcf->nick.s = p = sid->s; while(p < sid->s + sid->len && *p!='@') { if(*p == ':') jcf->nick.s = p+1; p++; } jcf->nick.len = p - jcf->nick.s; } jcf->jcid = xj_get_hash(&jcf->room, &jcf->server); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jconf_init_sip: conference id=%d\n", jcf->jcid); #endif return 0; bad_format: DBG("XJAB:xj_jconf_init_sip: error parsing uri - bad format\n"); return -2; } /** * */ int xj_jconf_init_jab(xj_jconf jcf) { char *p, *p0; if(!jcf || !jcf->uri.s || jcf->uri.len <= 0) return -1; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jconf_init_jab: parsing uri\n"); #endif p = jcf->uri.s; while(p<(jcf->uri.s + jcf->uri.len) && *p != '@') p++; if(*p != '@' || p==jcf->uri.s) goto bad_format; p0 = p+1; while(p0 < ((jcf->uri.s + jcf->uri.len)) && *p0 != '/') p0++; jcf->server.s = p+1; jcf->server.len = p0 - jcf->server.s; jcf->room.s = jcf->uri.s; jcf->room.len = p - jcf->room.s; if(p0 < jcf->uri.s + jcf->uri.len) { jcf->nick.s = p0+1; jcf->nick.len = jcf->uri.s + jcf->uri.len - jcf->nick.s; } jcf->jcid = xj_get_hash(&jcf->room, &jcf->server); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jconf_init_jab: conference id=%d\n", jcf->jcid); #endif return 0; bad_format: DBG("XJAB:xj_jconf_init_jab: error parsing uri - bad format\n"); return -2; } /** * */ int xj_jconf_set_status(xj_jconf jcf, int s) { if(!jcf || !jcf->uri.s || jcf->uri.len <= 0) return -1; jcf->status = s; return 0; } /** * */ int xj_jconf_cmp(void *a, void *b) { int n; if(a == NULL) return -1; if(b == NULL) return 1; // DBG("XJAB: xj_jconf_cmp: comparing <%.*s> / <%.*s>\n",((str *)a)->len, // ((str *)a)->s, ((str *)b)->len, ((str *)b)->s); if(((xj_jconf)a)->jcid < ((xj_jconf)b)->jcid) return -1; if(((xj_jconf)a)->jcid > ((xj_jconf)b)->jcid) return 1; if(((xj_jconf)a)->room.len < ((xj_jconf)b)->room.len) return -1; if(((xj_jconf)a)->room.len > ((xj_jconf)b)->room.len) return 1; if(((xj_jconf)a)->server.len < ((xj_jconf)b)->server.len) return -1; if(((xj_jconf)a)->server.len > ((xj_jconf)b)->server.len) return 1; n = strncmp(((xj_jconf)a)->room.s, ((xj_jconf)b)->room.s, ((xj_jconf)a)->room.len); if(n<0) return -1; if(n>0) return 1; n = strncmp(((xj_jconf)a)->server.s, ((xj_jconf)b)->server.s, ((xj_jconf)a)->server.len); if(n<0) return -1; if(n>0) return 1; return 0; } /** * */ int xj_jconf_free(xj_jconf jcf) { if(!jcf) return 0; if(jcf->uri.s != NULL) pkg_free(jcf->uri.s); pkg_free(jcf); jcf = NULL; return 0; } /** * */ int xj_jconf_check_addr(str *addr, char dl) { char *p; int i; if(!addr || !addr->s || addr->len <= 0) return -1; p = addr->s; i= 0; while((p < addr->s+addr->len) && *p != '@') { if(*p==dl) i++; p++; } if(i==2 && *p=='@') return 0; return -1; } kamailio-4.0.4/obsolete/jabber_s/doc/0000755000000000000000000000000012223032460016125 5ustar rootrootkamailio-4.0.4/obsolete/jabber_s/doc/gateways.txt0000644000000000000000000001070712223032460020517 0ustar rootrootIM Gateway - capabilities and limitations - v1.0 ================================================ The gateway allows the message passing between SIP users and other IM users (ICQ, AIM, Yahoo ...). IM Gateway uses for this a Jabber server which has the facility of supporting many IM protocols (they are called IM transports). So, in fact, the gateway is between SIP and Jabber. Because the most of the IM protocols are not open source and they are the property of some companies, Jabber does not provide full support for these protocols. From time to time, the owner of the protocol changes the protocol structure, and for a while, that transport is no longer available. There are some guys doing some kind of reverse engineering on these protocols. But not whole protocol structure is discovered, each transport has its own limitations. In additions, many of the IM native clients allow only one user to be online at a moment, and IM servers could easy say that the hosts which have more than one open connection are not using a native client and block those addresses or the accounts. Next, are presented the IM transports that are running on our Jabber server, or they are wanted to be available. The capabilities and limitations are what can be found on the net about transports. AIM transport ------------- It is only one well known version of aim-transport. (web location: http://aim-transport.jabberstudio.org/) AIM-t provides basic interoperability between Jabber and AIM/ICQ network. Currently AIM-t is undergoing massive changes. It will be a standalone component once this is finished. * capabilities - currently supporting basic presence and messaging. - reliable messaging (via server only) - simple user info retrieval (ICQ only) may take some time, just wait... - SMS sending (ICQ) send "SEND-SMS:number:text" to any ICQ user, for example "SEND-SMS:+4917012345:Test" - away message retrieval (AIM only) - offline messages (ICQ only) * limitations - NO file transfer - NO multichat - NO user search - NO incoming SMS - NO import of server buddy lists - NO handling of incoming ICQ auth requests - ICQ messages may get dropped by AOL servers if your contact uses an old ICQv5 client. ICQ transport ------------- Excepting the AIM transport, there exists another distribution for ICQ transport. (web location: http://icqv7-t.sourceforge.net/) * capabilities - messaging (through server only) - presence notification - SMS support * limitations - no file transfer The AIM transport for ICQ. - see AIM transport A new versions of ICQ transport seems to be available now. But now place from where it can be downloaded - I sent a mail asking for a distribution. A short description can be found at http://mailman.jabber.org/pipermail/jdev/2002-July/012343.html IRC transport ------------- (web location: http://download.jabber.org/contrib/irc-transport.tar.gz) This version of IRC transport is a beta version * capabilities - It will connect to irc servers and allow you to talk to them, but it is still in heavy development * limitations - private messaging does not yet function - transport registration/notify will not be supported - groupchat 1.0 and 1.1 will not be supported in favor of the 2.0 iq:conferenec/iq:browse protocol you will be able to log into and create password protected groups now (although this may not work yet) - there still may be some other bugs but they aren't caught yet. - in testing, it has been fairly stable, although some functionality may not be there yet. - requires jabberd version 1.4 several functions used, including MIO, MTQ, and xhash functions were not available in previous jabber server versions MSN transport ------------- (web location: http://msn-transport.jabberstudio.org/) * capabilities - provides basic interoperability between Jabber and MSN (messaging/presence) * limitations - there are memory leaks in MSN-t so is better to be run in a seperate process and restart it from time to time - MSN-t uses TCP port 1863 for incoming connections so you have to open this port if you use a firewall. - the other parts excepting messaging/presence Yahoo transport --------------- (web location: http://yahoo-transport.jabberstudio.org/) As of 2002-07-10 this transport has ceased functioning (probably due to YIM protocol changes). The transport is being worked on - see JDEV mailing list : http://mailman.jabber.org/pipermail/jdev/2002-July/012327.html * capabilities - log into Yahoo Messenger server, and send/recv messages. * limitations kamailio-4.0.4/obsolete/jabber_s/doc/xjab.cfg0000644000000000000000000000444112223032460017535 0ustar rootroot# # configuration for Jabber module testing # # $ID: daniel $ # # # sample of Jabber module usage # debug=9 # debug level (cmd line: -dddddddddd) #fork=yes # (cmd. line: -D) fork=no log_stderror=yes # (cmd line: -E) #log_stderror=no # (cmd line: -E) children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 listen=10.0.0.1 # for more info: sip_router -h #modules loadmodule "../sip_router/modules/print/print.so" loadmodule "../sip_router/modules/textops/textops.so" loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/im/im.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/mysql/mysql.so" loadmodule "../sip_router/modules/jabber/jabber.so" modparam("jabber","db_url","mysql://user:***@127.0.0.1/sip_jab") modparam("jabber","jaddress","127.0.0.1") modparam("jabber","jport",5222) modparam("jabber","workers",2) modparam("jabber","max_jobs",10) modparam("jabber","aliases","3;icq.domain.com;msn.domain.com;yahoo.domain.com;") modparam("jabber","jdomain","jabber.domain.com") route{ if ((search("To:.*@icq\.domain\.com")) || (search("To:.*@jabber\.domain\.de")) || (search("To:.*@msn\.domain\.com")) || (search("To:.*@yahoo\.domain\.com"))) { if (method=="MESSAGE") { log("MESSAGE received -> manage it with XJAB\n"); if(search("\n:online")) { if (jab_go_online()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not found"); }; break; }; if(search("\n:offline")) { if (jab_go_offline()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not found"); }; break; }; if(search("\n:join")) { if (jab_join_jconf()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not Found"); }; break; }; if(search("\n:exit")) { if (jab_exit_jconf()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not Found"); }; break; }; if (jab_send_message()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("503","Service Unavailable"); }; break; }; }; forward(uri:host,uri:port); } kamailio-4.0.4/obsolete/jabber_s/doc/web/0000755000000000000000000000000012223032460016702 5ustar rootrootkamailio-4.0.4/obsolete/jabber_s/doc/web/subscribe.php0000644000000000000000000003330012223032460021373 0ustar rootroot"; exit(); } # function dbg_msg($message) { # echo "$message"; } ?> IM Gateway registration
Subscription page for Instant Messaging gateway
You MUST have a SIP account on IPTEL.ORG


SIP username:
SIP password:

My Jabber account:      

IM service:
IM nickname:
IM account:
IM password:

     


User info

- for any operation you MUST provide your 'iptel.org' username and password

AIM Gateway subscription
- choose 'AIM' as 'IM service'
- 'IM nickname' is your display name for AIM network
- 'IM account' is your AIM account name (ex: 'alpha')
- 'IM password' is the password of your AIM account
- click on 'Subscribe'
ICQ Gateway subscription
- choose 'ICQ' as 'IM service'
- 'IM nickname' is your display name for ICQ network
- 'IM account' is your ICQ number (ex: '158251040')
- 'IM password' is the password of your ICQ account
- click on 'Subscribe'
MSN Gateway subscription
- choose 'MSN' as 'IM service'
- 'IM nickname' is your display name for MSN network
- 'IM account' is your MSN account (ex: 'alpha@hotmail.com' or 'alpha@msn.com')
- 'IM password' is the password of your MSN account
- click on 'Subscribe'
Yahoo Gateway subscription
- choose 'Yahoo' as 'IM service'
- 'IM nickname' is your display name for Yahoo network
- 'IM account' is your Yahoo account (ex: 'alpha')
- 'IM password' is the password of your Yahoo account
- click on 'Subscribe'

IM Gateway unsubscription
- choose the 'IM service' from which you want to unsubscribe
- click on 'Unsubscribe'

Instant Messaging Gateway



"; $dblink = mysql_connect($sip_db_srv, $sip_db_usr, $sip_db_pas) or html_die("Could not connect to SIP database server"); mysql_select_db($sip_db_db, $dblink) or html_die("Could not select SIP database"); $query = "SELECT $sip_db_cusr FROM $sip_db_tab WHERE $sip_db_cusr='$sipname' AND $sip_db_cpas='$sippasswd'"; dbg_msg("$query
"); $result = mysql_query($query) or html_die("Invalid SQL query"); if(mysql_num_rows($result) == 0) html_die("Invalid SIP username or password"); mysql_close($dblink); ***************************************** */ # #------------------------------------------------------ # # ----- # check if is already registered to Jabber gateway # ----- $sipuri = "sip:".$sipname."@iptel.org"; $dblink = mysql_connect($jab_db_srv, $jab_db_usr, $jab_db_pas) or html_die("Could not connect to Jabber database"); mysql_select_db($jab_db_db, $dblink) or html_die("Could not use Jabber database"); # ---- if($action == "Disable") { $query = "UPDATE jusers SET tyep=1 WHERE sip_id='$sipuri'"; $result = mysql_query($query, $dblink); if(mysql_affected_rows() != 1) { mysql_close($dblink); html_die("
Cannot find Jabber ID of '$sipname' in database"); } mysql_close($dblink); html_die("
Your IM account was updated"); } # ---- $query = "SELECT jab_id FROM jusers WHERE sip_id='$sipuri'"; $result = mysql_query($query, $dblink) or html_die("Invalid SQL query"); if(mysql_num_rows($result) == 0) { // no Jabber account - create one $fd = jab_connect($jserver, $jport); if(!$fd) html_die("Could not connect to Jabber server"); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $jid1 = stristr($buf_recv, "id='"); $jid1 = substr($jid1, 4); if($jid1) { $jid2 = strstr($jid1, "'"); if($jid2) { $jid = substr($jid1, 0, strlen($jid1)-strlen($jid2)); dbg_msg("JID: $jid
"); } } $jcid = $jcid + 1; jab_get_reg($fd, $jcid, $jserver); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $jcid = $jcid + 1; $new_passwd = "#".$sipname."%"; jab_set_reg($fd, $jcid, $jserver, $sipname, $new_passwd); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } if(stristr($buf_recv, " id='$jcid'") && stristr($buf_recv, " type='error'")) { mysql_close($dblink); jab_disconnect($fd); html_die("
Something bizarre with account '$sipname'"); } # ----- # Add user in database # ----- $query = "INSERT INTO jusers (jab_id, jab_passwd, sip_id) VALUES ('$sipname', '$new_passwd', '$sipuri')"; $result = mysql_query($query, $dblink); if(mysql_affected_rows() != 1) { mysql_close($dblink); jab_disconnect($fd); html_die("
Can not insert '$sipname' in database"); } jab_disconnect($fd); } # ----- if($action == "Enable") { $query = "UPDATE jusers SET type=0 WHERE sip_id='$sipuri'"; $result = mysql_query($query, $dblink); if(mysql_affected_rows() != 1) { mysql_close($dblink); html_die("
Cannot find Jabber ID of '$sipname' in database"); } mysql_close($dblink); html_die("
Your IM account was updated"); } # ----- $query="SELECT juid,jab_id,jab_passwd,type FROM jusers WHERE sip_id='$sipuri' and type=0"; $result = mysql_query($query, $dblink) or html_die("Invalid SQL query"); if(mysql_num_rows($result) != 1 || (!($row = mysql_fetch_array($result)))) { mysql_close($dblink); html_die("
You do not have an associated Jabber account or it is disabled!
Press 'Enable' in order to create a new one or to activate an old one.
If error persists, please inform the administrator."); } $juid = $row[0]; $jab_id = $row[1]; $jab_passwd = $row[2]; $jab_type = $row[3]; dbg_msg("Jabber User ID: $juid
"); $fd = jab_connect($jserver, $jport); if(!$fd) html_die("Could not connect to Jabber server"); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $jid1 = stristr($buf_recv, "id='"); $jid1 = substr($jid1, 4); if($jid1) { $jid2 = strstr($jid1, "'"); if($jid2) { $jid = substr($jid1, 0, strlen($jid1)-strlen($jid2)); dbg_msg("JID: $jid
"); } } $jcid = $jcid + 1; jab_get_auth($fd, $jcid, $jab_id); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $jcid = $jcid + 1; jab_set_auth($fd, $jcid, $jab_id, $jab_passwd); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } if(stristr($buf_recv, " id='$jcid'") && stristr($buf_recv, " type='error'")) { jab_disconnect($fd); html_die("
Wrong username or password at Jabber authentication"); } # ----- # browse agents # ----- $jcid = $jcid + 1; jab_get_agents($fd, $jcid, $jserver); $buf_agents = fread($fd, 4096); while(!$buf_agents) { usleep(100); $buf_agents = fread($fd, 4096); } # dbg_msg("\n"); # ----- $imag1 = stristr($buf_agents, ""); } } # ----- if(isset($imag)) { if($action == "Subscribe" && isset($imname) && $imname != "") { echo "

IM ($imtype) subscription


"; # ----- # unsubscribe the previous IM account (if exists) # ----- $jcid = $jcid + 1; jab_set_unreg($fd, $jcid, $icqag); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } sleep(1); # ----- # subscription # ----- $jcid = $jcid + 1; jab_get_reg($fd, $jcid, $imag); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $imkey1 = stristr($buf_recv, ""); $imkey1 = substr($imkey1, 5); if($imkey1) { $imkey2 = strstr($imkey1, ""); if($imkey2) { $imkey = substr($imkey1, 0, strlen($imkey1)-strlen($imkey2)); dbg_msg("IM key: $imkey
"); } } if(!isset($imkey)) { jab_disconnect($fd); mysql_close($dblink); html_die("
Session key for IM ($imtype) Transport not found"); } $jcid = $jcid + 1; jab_set_regk($fd, $jcid, $imag, $imname, $impasswd, $imnick, $imkey); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } if(stristr($buf_recv, " id='$jcid'") && stristr($buf_recv, " type='error'")) { $err1 = stristr($buf_recv, ""); $err1 = substr($err1, 1); if($err1) { $err2 = strstr($err1, ""); if($err2) $err = substr($err1, 0, strlen($err1)-strlen($err2)); } jab_disconnect($fd); mysql_close($dblink); html_die("
Error registering your IM ($imtype) account: $err"); } jab_send_presence($fd, $imag."/registered", "subscribed"); # ----- # Update database $query = "SELECT ".$imtype."_id FROM ".$imtype." WHERE juid='$juid'"; $result = mysql_query($query, $dblink) or html_die("Invalid SQL query"); if(mysql_num_rows($result) == 0) { # INSERT $query = "INSERT INTO ".$imtype." (juid, ".$imtype."_id, ".$imtype."_passwd, ".$imtype."_nick) VALUES ('$juid', '$imname', '$impasswd', '$imnick')"; dbg_msg("$query
"); $result = mysql_query($query, $dblink); if(mysql_affected_rows() != 1) { echo "
Can not register '$sipname'/'$imname'
"; } else { echo "Your IM ($imtype) account was successfully registered
"; } } else { # UPDATE $query = "UPDATE ".$imtype." SET ".$imtype."_id='$imname', ".$imtype."_passwd='$impasswd', ".$imtype."_nick='$imnick' WHERE juid='$juid'"; dbg_msg("$query
"); $result = mysql_query($query, $dblink); if(!$result) { echo "
Can not update '$sipname'/'$imname'
"; } else { if(mysql_affected_rows() == 1) { echo "Your IM ($imtype) account was successfully updated
"; } else { echo "No modification in your IM ($imtype) account
"; } } } } else { echo "

IM ($imtype) unsubscription


"; # ----- # unsubscribe the IM account # ----- $jcid = $jcid + 1; jab_set_unreg($fd, $jcid, $icqag); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } sleep(1); $query = "DELETE FROM ".$imtype." WHERE juid='$juid'"; dbg_msg("$query
"); $result = mysql_query($query, $dblink); if(!$result) { echo "
Can not remove IM ($imtype) information from database
"; } else { echo "
Unsubscription from IM ($imtype) completed
"; } } } sleep(1); jab_disconnect($fd); mysql_close($dblink); } ?> kamailio-4.0.4/obsolete/jabber_s/doc/web/libjab.php0000644000000000000000000000410712223032460020640 0ustar rootroot"; fputs ($fd,$stream); return $fd; } function jab_disconnect($fd) { $stream = ""; fputs ($fd,$stream); fclose($fd); } function jab_get_reg($fd, $id, $server) { $str = ""; fputs ($fd,$str); } function jab_set_reg($fd, $id, $server, $username, $password) { $str = "$username$password"; fputs($fd, $str); } function jab_set_regk($fd, $id, $server, $username, $password, $nick, $key) { $str = "$username$password$nick$key"; fputs($fd, $str); } function jab_set_unreg($fd, $id, $server) { $str = ""; fputs($fd, $str); } function jab_get_agents($fd, $id, $server) { $str = ""; fputs($fd, $str); } function jab_get_auth($fd, $id, $user) { $str = "$user"; fputs($fd, $str); } function jab_set_auth($fd, $id, $user, $passwd) { $str = "$userwebjb$passwd"; fputs($fd, $str); } function jab_send_presence($fd, $to, $presence) { $str = ""; fputs($fd, $str); } ?>kamailio-4.0.4/obsolete/jabber_s/doc/functions.xml0000644000000000000000000000503012223032460020655 0ustar rootroot
$Revision$ $Date$ Functions
<function>jab_send_message()</function> Converts SIP MESSAGE message to a Jabber message and sends it to Jabber server. <function>jab_send_message()</function> usage ... jab_send_message(); ...
<function>jab_join_jconf()</function> Join a Jabber conference--the nickname, room name and conference server address should be included in To header as: nickname%roomname%conference_server@jdomain . If the nickname is missing, then the SIP username is used. <function>jab_join_jconf()</function> usage ... jab_join_jconf(); ...
<function>jab_exit_jconf()</function> Leave a Jabber conference--the nickname, room name and conference server address should be included in To header as: nickname%roomname%conference_server@jdomain . <function>jab_exit_jconf()</function> usage ... jab_exit_jconf(); ...
<function>jab_go_online()</function> Register to the Jabber server with associated Jabber ID of the SIP user. <function>jab_go_online()</function> usage ... jab_go_online(); ...
<function>jab_go_offline()</function> Log off from Jabber server the associated Jabber ID of the SIP user. <function>jab_go_offline()</function> usage ... jab_go_offline(); ...
kamailio-4.0.4/obsolete/jabber_s/doc/regjab.pl0000755000000000000000000000725512223032460017730 0ustar rootroot#!/usr/bin/perl -w # # Register a new user with Jabber gateway # # The parameters must be only the username part of the sip address # Change the $sip_domain according with your SIP server domain name # use Socket; use DBD::mysql; if(@ARGV == 0) { die ("Syntax: regjab.pl username1 username2 ..."); } $db_driver = "mysql"; $sip_domain = "iptel.org"; ##### MySQL connection to JabberGW database $jab_db_nm = "sip_jab"; #JabberGW's database name $jab_db_hn = "127.0.0.1"; #JabberGW's database server address $jab_db_pt = "3306"; #JabberGW's database server port $jab_db_us = "user"; #JabberGW's database username $jab_db_ps = "*********"; #JabberGW's database password $jab_db_dsn = "DBI:$db_driver:database=$jab_db_nm;host=$jab_db_hn;port=$jab_db_pt"; ##### ser users table $jab_tb_nm = "jusers"; #JabberGW's subscriber table ##### username column $jab_cl_usj = "jab_id"; $jab_cl_psj = "jab_passwd"; $jab_cl_uss = "sip_id"; $jab_server = "localhost"; #Jabber server address $jab_srvid = "jabber.x.com"; #Jabber server id $jab_port = 5222; #Jabber server port $iaddr = inet_aton($jab_server) or die "no host: $jab_server"; $paddr = sockaddr_in($jab_port, $iaddr); $proto = getprotobyname('tcp'); ### Connect to MySQL database $jab_dbh = DBI->connect($jab_db_dsn, $jab_db_us, $jab_db_ps); if(!$jab_dbh ) { print ('ERROR: Jabber\'s MySQL server not responding!'); exit; } foreach $jab_usr (@ARGV) { print "Subscribe SIP user <$jab_usr> to Jabber gateway\n"; #checkif user has already a Jabber ID $sip_uri = "sip:$jab_usr\@$sip_domain"; $jab_pwd = "qw1$jab_usr#"; $jab_sth = $jab_dbh->prepare("SELECT $jab_cl_usj FROM $jab_tb_nm WHERE $jab_cl_uss='$sip_uri'"); if (!$jab_sth) { die($jab_dbh->errstr); } if (!$jab_sth->execute) { die($jab_sth->errstr); } if(!($jab_sth->fetchrow_hashref())) { # create Jabber account print "Register SIP user <$jab_usr> to Jabber server\n"; socket(SOCK, PF_INET,SOCK_STREAM, $proto) or die "socket: $!"; connect(SOCK, $paddr) or die "connect: $!"; $jcid = 0; $err_no = 0; $line = ""; send(SOCK, $line, 0); recv(SOCK, $line, 256, 0); $jid_p = index($line, "id="); if($jid_p > 0) { $jid_p = $jid_p+4; $jid_p1 = index($line, "'", $jid_p); if($jid_p1 > 0) { $jid = substr($line, $jid_p, $jid_p1-$jid_p); print("JID: $jid\n"); } } $jcid =$jcid + 1; $line = ""; send(SOCK, $line, 0); recv(SOCK, $line, 512, 0); # here we should check what Jabber really wants # - I know for our srv, so skip it # $jcid =$jcid + 1; $line = "$jab_usr$jab_pwd"; send(SOCK, $line, 0); recv(SOCK, $line, 512, 0); if(index($line, " id='$jcid'")>0 && index($line, " type='error'")>0) { #error creating Jabber user print("Error: creating Jabber user <$jab_usr>\n$line\n"); $err_no = 1; } $line = ""; send(SOCK, $line, 0); close(SOCK) or die "close: $!"; # add to Jabber database if($err_no == 0) { $rows = $jab_dbh->do("INSERT INTO $jab_tb_nm ($jab_cl_usj, $jab_cl_psj, $jab_cl_uss) VALUES ('$jab_usr', '$jab_pwd', '$sip_uri')"); if($rows == 1) { print("SIP user <$jab_usr> added to Jabber database\n"); } else { print("Error: SIP user <$jab_usr> not added to Jabber database\n"); } } } else { print("SIP user <$jab_usr> is already in Jabber database\n"); } } $jab_sth->finish(); #### Disconnect from the database. $jab_dbh->disconnect(); kamailio-4.0.4/obsolete/jabber_s/doc/admin.xml0000644000000000000000000001275712223032460017753 0ustar rootroot
$Revision$ $Date$ Administator's Guide A more complete guide about SIMPLE2Jabber gateway can be found at SER Homepage. The part below will be removed soon, only the manual from web will be updated. The Jabber server setup is not a subject of this guide. Check http://www.jabber.org for that. Useful scripts, for creating Jabber Gateway database, or for managing the Jabber accounts form web are located in 'doc' subdirectory of the module. Main steps of using the Jabber gateway: Create the MySQL database. Setup the local Jabber server. Set the module parameter values in cfg file of SER, load the dependent modules, set up the routing rules for Jabber gateway. Run SER. The administrator of SER/Jabber gateway must inform the users what are the aliases for Jabber/Other IM networks. Other IM systems could be AIM, ICQ, MSN, Yahoo, and so on. These aliases depend on the server hostname where runs SER and how local Jabber server is setup. Next is presented a use case. Prologue: SER is running on "server.org". Local Jabber server is running on "jabsrv.server.org". Jabber network alias (first part of "jdomain") is "jabber.server.org" The aliases for other IM networks must be the same as JID set in Jabber configuration file for each IM transport. The JIDs of Jabber transports must start with the name of the network. For AIM, JID must start with "aim.", for ICQ with "icq" (that because I use icqv7-t), for MSN with "msn." and for Yahoo with "yahoo.". The gateway needs these to find out what transport is working and which not. For our use case these could be like "aim.server.org", "icq.server.org", "msn.server.org", "yahoo.server.org". It is indicated to have these aliases in DNS, thus the client application can resolve the DNS name. Otherwise there must be set the outbound proxy to SER server. *** Routing rules for Jabber gateway First step is to configure SER to recognize messages for Jabber gateway. Look at "doc/xjab.cfg" to see a sample. The idea is to look in messages for destination address and if it contains Jabber alias or other IM alias, that means the message is for Jabber gateway. Next step is to find out what means that message for Jabber gateway. It could be a special message what triggers the gateway to take an action or is a simple message which should be delivered to Jabber network (using the method "jab_send_message"). The special messages are for: Registering to Jabber server (go online in Jabber network)--here must be called "jab_go_online" method. Leaving the Jabber network (go offline in Jabber network)--here must be called jab_go_offline method. Joining a Jabber conference room--here must be called "jab_join_jconf". Leaving a Jabber conference room--here must be called "jab_exit_jconf". The destination address must follow the following patterns: For Jabber network: "username<delim>jabber_server@jabber_alias". For Jabber conference: "nickname<delim>room<delim>conference_server@jabber_alias". For AIM network: "aim_username@aim_alias". For ICQ network: "icq_number@icq_alias". For MSN network: "msn_username<delim>msn_server@msn_alias". msn_server can be "msn.com" or "hotmail.com". For YAHOO network: "yahoo_username@yahoo_alias". "jabber_alias" is the first part of "jdomain".
kamailio-4.0.4/obsolete/jabber_s/doc/xjab.sql0000644000000000000000000000256212223032460017577 0ustar rootroot# # DATABASE definition # DROP DATABASE IF EXISTS sip_jab; CREATE DATABASE sip_jab; USE sip_jab; # jabber users CREATE TABLE jusers( juid INT NOT NULL AUTO_INCREMENT, jab_id VARCHAR(128) NOT NULL, jab_passwd VARCHAR(50), sip_id VARCHAR(128) NOT NULL, type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), KEY(jab_id), KEY(sip_id) ); # icq users CREATE TABLE icq( juid INT NOT NULL, icq_id INT NOT NULL, icq_passwd VARCHAR(50), icq_nick VARCHAR(50), type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), # --- REFERENCES jusers(juid) ON UPDATE CASCADE ON DELETE CASCADE, KEY(icq_id) ); # msn users CREATE TABLE msn( juid INT NOT NULL, msn_id VARCHAR(128) NOT NULL, msn_passwd VARCHAR(50), msn_nick VARCHAR(50), type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), # --- REFERENCES jusers(juid) ON UPDATE CASCADE ON DELETE CASCADE, KEY(msn_id) ); # aim users CREATE TABLE aim( juid INT NOT NULL, aim_id VARCHAR(128) NOT NULL, aim_passwd VARCHAR(50), aim_nick VARCHAR(50), type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), # --- REFERENCES jusers(juid) ON UPDATE CASCADE ON DELETE CASCADE, KEY(aim_id) ); # yahoo users CREATE TABLE yahoo( juid INT NOT NULL, yahoo_id VARCHAR(128) NOT NULL, yahoo_passwd VARCHAR(50), yahoo_nick VARCHAR(50), type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), # --- REFERENCES jusers(juid) ON UPDATE CASCADE ON DELETE CASCADE, KEY(yahoo_id) ); kamailio-4.0.4/obsolete/jabber_s/doc/xxjab.cfg0000644000000000000000000001051212223032460017721 0ustar rootroot# # configuration for Jabber module testing # (sample config file using the module with presence support) # # $ID: daniel $ # debug=9 # debug level (cmd line: -dddddddddd) #fork=yes # (cmd. line: -D) fork=no log_stderror=yes # (cmd line: -E) #log_stderror=no # (cmd line: -E) children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 #listen=10.0.0.179 listen=193.175.135.68 fifo="/tmp/ser_fifo" # for more info: sip_router -h # ------------------ module loading ---------------------------------- #modules loadmodule "../sip_router/modules/print/print.so" loadmodule "../sip_router/modules/textops/textops.so" loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/mysql/mysql.so" loadmodule "../sip_router/modules/jabber/jabber.so" loadmodule "../sip_router/modules/registrar/registrar.so" loadmodule "../sip_router/modules/usrloc/usrloc.so" loadmodule "../sip_router/modules/pa/pa.so" # ----------------- setting module-specific parameters --------------- # -- registrar params -- modparam("registrar", "default_expires", 120) modparam("registrar", "use_domain", 1) # -- usrloc params -- modparam("usrloc", "use_domain", 1) modparam("usrloc", "db_mode", 0) # -- jabber params -- modparam("jabber","db_url","mysql://user:password@127.0.0.1/sip_jab") modparam("jabber","jaddress","jabber.server.com") modparam("jabber","jport",5222) modparam("jabber","workers",2) modparam("jabber","max_jobs",10) modparam("jabber","cache_time",200) modparam("jabber","delay_time",60) modparam("jabber","jdomain","jabber.server.com=*") modparam("jabber","aliases","4;aim.jabber.server.com;icq.jabber.server.com;msn.jabber.server.com=%;yahoo.jabber.server.com;") route{ #if ( !mf_process_maxfwd_header("10") ) #{ # sl_send_reply("483","To Many Hops"); # drop(); #}; if (uri=~"[@:]sip\.server\.com([;:].*)*") { # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { if (t_newtran()) { save("location"); log("REGISTER received -> reply okay\n"); }; if(search("egistration")) { log("XJAB: Going ONline in Jabber network!!!\n"); if(jab_go_online()) { sl_send_reply("200", "Accepted"); } else { sl_send_reply("404","Not found"); }; } else { log("XJAB: Going OFFline in Jabber network!!!\n"); if(jab_go_offline()) { sl_send_reply("200", "Accepted"); } else { sl_send_reply("404","Not found"); }; }; break; }; if (method=="SUBSCRIBE") { if (t_newtran()) { subscribe("registrar"); }; break; }; if(!lookup("location")) { sl_send_reply("404","Not found"); break; }; }; if ((search("To:.*@icq\.jabber\.server\.com")) || (search("To:.*@jabber\.server\.com")) || (search("To:.*@msn\.jabber\.server\.com")) || (search("To:.*@yahoo\.jabber\.server\.com"))) { if (! t_newtran()) { sl_reply_error(); break; }; if (method=="MESSAGE") { log("MESSAGE received -> manage it with XJAB\n"); if(search("\n:on")) { if (jab_go_online()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not found"); }; break; }; if(search("\n:off")) { if (jab_go_offline()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not found"); }; break; }; if(search("\n:join")) { if (jab_join_jconf()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not Found"); }; break; }; if(search("\n:exit")) { if (jab_exit_jconf()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not Found"); }; break; }; if (jab_send_message()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("503","Service Unavailable"); }; break; }; if (method=="SUBSCRIBE") { subscribe("jabber"); break; }; log("NON_Message request received for JABBER gateway->dropt!\n"); sl_send_reply("202","Accepted"); break; }; if (!t_relay()) { sl_reply_error(); }; #forward(uri:host,uri:port); } kamailio-4.0.4/obsolete/jabber_s/doc/jabber.xml0000644000000000000000000001122412223032460020074 0ustar rootroot
Daniel Mierla FhG FOKUS
mierla@fokus.fraunhofer.de
2003 FhG FOKUS $Revision$ $Date$
Jabber Module
Overview This is new version of Jabber module that integrates XODE XML parser for parsing Jabber messages. That introduces a new module dependency: expat library. Expat is a common XML library and is the fastest available for Linux/Unix, the second over all, after msxml library. It is integrated in most of well known Linux distributions.
New Features Presence support (see doc/xxjab.cfg for a sample cfg file) (January 2003). SIP to Jabber conference support (December 2003). Possibility to manage all kinds of Jabber messages (message/presence/iq) (December 2003). Aliases -- Possibility to set host aliases for addresses (see parameter's desc.) (December 2003). Send received SIP MESSAGE messages to different IM networks (Jabber, ICQ, MSN, AIM, Yahoo) using a Jabber server (December 2003). Send incoming Jabber instant messages as IM MESSAGE messages. Gateways detection -- Ability to see whether an IM gateway is up or down.
Dependencies
SER Modules The following modules must be loaded before this module: A database module. pa (Optionally) - Presence Agent. tm - Transaction Manager.
External Libraries or Applications The following libraries or applications must be installed before running SER with this module loaded: Expat library.
User's Guide The user must activate his Jabber account associated with his SIP id. For each other IM network on which he wants to send messages, he must set an account for that IM network. The gateway is not able to create new account in foreign networks, excepting local Jabber server. When you want to send a message to someone in other IM network, you must set the destination of the message according with the pattern corresponding to that IM network (see last part of ). Sending a message to user@jabber.xxx.org which is in Jabber network, the destination must be: user<delim>jabber.xxx.org@jabber_alias. For someone who is in Yahoo network the destination must be: user@yahoo_alias The SER administrator have to set the Jabber transports for each IM network in order to be able to send messages to those networks. The alias of each IM network can be found out from SER admin. You cannot send messages from your SIP client to your associated Jabber account--is something like sending messages to yourself.
kamailio-4.0.4/obsolete/jabber_s/doc/faq.txt0000644000000000000000000000665412223032460017450 0ustar rootrootJabber module (SIP*IM Gateway) FAQ ================================== [ OBSOLETED ] 1. Description -------------- 1.1 What is SIP to IM Gateway? A: SIP to IM gateway is a module for SER that allows SIP clients to send/receive instant messages to/from other IM networks (like ICQ, AIM, MSN ...). 1.2 How does the gateway work? A: The gateway uses Jabber server for sending/receiving messages to/from IM networks. The modules translate SIP messages to Jabber format and send them to Jabber server, which is able to send instant messages across many IM networks. Actually, the module is a gateway between SIP and Jabber protocol. 2. End-User questions --------------------- 2.1 What I need to use the gateway? A: Anyone who wants to use the gateway MUST have a SIP account on 'iptel.org'. Next step (IM Gateway registration) consists in registering your IM id for each network that you want to communicate with. For this, you have to fill the registration form at 'http://www.iptel.org/im/subscribe.php'. 2.2 How can I change my IM accounts from IM gateway? A: To change the IM account just fill again the subscription for the appropriate IM network and press 'Subscribe'. 2.3 How can I remove my IM account from IM gateway? A: To remove an IM account just select the appropriate IM network and press 'Unsubscribe'. 2.4 How destination must be set? A: For each IM network exists a destination address format. Usually is like: 'user_id@im.iptel.org', where 'im' must be replaced with IM network id (icq, aim, msn, yahoo, ...). Examples: - ICQ: icq_number@icq.iptel.org - MSN: user_name%hotmail.com@msn.iptel.org or user_name%msn.com@msn.iptel.org - AIM: user_name@aim.iptel.org 2.5 How could IM users send messages to SIP users? A: They must sent messages to the IM id that SIP users have set at IM gateway registration. 2.6 I also need a Jabber user id? A: No, Jabber user associated to you is created on IM gateway registration. There is used an internal Jabber server and that avoids Jabber ids conflict. 3. Configuration questions -------------------------- 3.1 How is the gateway started? A: Load the module in SER, after you have set the right parameters and, also, start the jabber server. There should be a file 'imgwd' in subfolder 'etc/' of sources that can be used as starting/stopping script (be sure you set the correct paths). 3.2 What are the parameters? A: The parameters are: - "contact" - contact header of SIM message - if is not set the header is filled with the content of the 'from' address - "db_url" - MySQL database url - including user, password, host, port and database name - "jaddress" - address (IP or hostname) of the Jabber server - "jport" - Jabber server port - "workers" - number of workers - "max_jobs" - maximum number of jobs per worker - "cache_time" - time (in seconds) to keep alive a Jabber connection - "delay_time" - time (in seconds) to keep first messages before sending them to Jabber - to avoid the sending before Jabber server connects to IM networks - "sleep_time" - time (in seconds) between checking for expired connections 4. Technical questions ---------------------- 4.1 What SIP IM software clients should be use? A: Any application that supports SIP for instant messaging. MSN Messager is the only one of the well known IM application clients that supports SIP. Other SIP IM clients are JSIP and HotSIP. 5. References ------------- - http://www.iptel.org - info contact: Daniel MIERLA kamailio-4.0.4/obsolete/jabber_s/doc/Makefile0000644000000000000000000000012712223032460017565 0ustar rootrootdocs = jabber.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/jabber_s/doc/params.xml0000644000000000000000000001605212223032460020136 0ustar rootroot
$Revision$ $Date$ Parameters
<varname>db_url</varname> (string) SQL URL of database. Default value is "mysql://root@127.0.0.1/sip_jab". Set <varname>db_url</varname> parameter ... modparam("jabber", "db_url", "mysql://username:password@host/sip_jab") ...
<varname>jaddress</varname> (string) IP or hostname of Jabber server - it must be the same as the value from <host> tag of Jabber server config file. Default value is "127.0.0.1". Set <varname>jaddress</varname> parameter ... modparam("jabber", "jaddress", "1.2.3.4") ...
<varname>jport</varname> (integer) Port number of Jabber server. Default value is 5222. Set <varname>jport</varname> parameter ... modparam("jabber", "jport", 1234) ...
<varname>jdomain</varname> (string) Format: jabber.sipserver.com=<delim>. If the destination is for Jabber network the URI should be like: username<delim>jabber_server@jdomain or nickname<delim>roomname<delim>conference_server@jdomain <delim> must be a un-reserved character. By default this character is * . The destination will be transformed to username@jabber_server or roomname@conference_server/nickname before the message is sent to Jabber server. Default value is none. Set <varname>jdomain</varname> parameter ... modparam("jabber", "jdomain", "jabber.sipserver.com=*") ...
<varname>aliases</varname> (string) Aliases for IM networks. Format: "N;alias1=<delim1>;...;aliasN=<delimN>;" Destinations like '*@aliasX' could have other format than those specified for Jabber network. All <delim> from user part of the destination address will be changed to <delimX> if the destination address contains <aliasX>. (Ex: jdomain is 'jabber.x.com=*' and msn_alias is 'msn.x.com=%'. The destination address forM MSN Network, on SIP side, is like 'username*hotmail.com@msn.x.com'. The destination address will be transformed to 'username%hotmail.com@msn.x.com'. 'msn.x.com' must be the same as the JID associated with MSN transport in Jabber configuration file (usually is 'jabberd.xml')) Default value is none. Set <varname>jdomain</varname> parameter ... modparam("jabber", "aliases", "1;msn.x.com=%") ...
<varname>proxy</varname> (string) Outbound proxy address. Format: ip_address:port hostname:port All SIP messages generated by gateway will be sent to that address. If is missing, the message will be delivered to the hostname of the destination address Default value is none. Set <varname>proxy</varname> parameter ... modparam("jabber", "proxy", "10.0.0.1:5060 sipserver.com:5060") ...
<varname>registrar</varname> (string) The address in whose behalf the INFO and ERROR messages are sent. Default value is "jabber_gateway@127.0.0.1". Set <varname>registrar</varname> parameter ... modparam("jabber", "registrar", "jabber_gateway@127.0.0.1") ...
<varname>workers</varname> (integer) Number of workers. Default value is 2. Set <varname>workers</varname> parameter ... modparam("jabber", "workers", 2) ...
<varname>max_jobs</varname> (integer) Maximum jobs per worker. Default value is 10. Set <varname>max_jobs</varname> parameter ... modparam("jabber", "max_jobs", 10) ...
<varname>cache_time</varname> (integer) Cache time of a Jabber connection. Default value is 600. Set <varname>cache_time</varname> parameter ... modparam("jabber", "cache_time", 600) ...
<varname>delay_time</varname> (integer) Time to keep a SIP message (in seconds). Default value is 90 seconds. Set <varname>delay_time</varname> parameter ... modparam("jabber", "delay_time", 90) ...
<varname>sleep_time</varname> (integer) Time between expired Jabber connections checking (in seconds). Default value is 20 seconds. Set <varname>sleep_time</varname> parameter ... modparam("jabber", "sleep_time", 20) ...
<varname>check_time</varname> (integer) Time between checking the status of JabberGW workers (in seconds). Default value is 20 seconds. Set <varname>check_time</varname> parameter ... modparam("jabber", "check_time", 20) ...
kamailio-4.0.4/obsolete/jabber_s/tree234.c0000644000000000000000000012053012223032460016715 0ustar rootroot/* * $Id$ * * tree234.c: reasonably generic counted 2-3-4 tree routines. * * This file is copyright 1999-2001 Simon Tatham. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include #include #include #include "tree234.h" #include "../../mem/shm_mem.h" //#define smalloc malloc //#define sfree free #define smalloc shm_malloc #define sfree shm_free #define mknew(typ) ( (typ *) smalloc (sizeof (typ)) ) #ifdef TEST #define LOG123(x) (printf x) #else #define LOG123(x) #endif typedef struct node234_Tag node234; struct tree234_Tag { node234 *root; cmpfn234 cmp; }; struct node234_Tag { node234 *parent; node234 *kids[4]; int counts[4]; void *elems[3]; }; /* * Create a 2-3-4 tree. */ tree234 *newtree234(cmpfn234 cmp) { tree234 *ret = mknew(tree234); LOG123(("created tree %p\n", ret)); ret->root = NULL; ret->cmp = cmp; return ret; } /* * Free a 2-3-4 tree (not including freeing the elements). */ static void freenode234(node234 *n) { if (!n) return; freenode234(n->kids[0]); freenode234(n->kids[1]); freenode234(n->kids[2]); freenode234(n->kids[3]); sfree(n); } void freetree234(tree234 *t) { if(t == NULL) return; freenode234(t->root); sfree(t); } /* * Free a 2-3-4 tree (including freeing the elements with 'fn' function). */ static void free2node234(node234 *n, freefn fn ) { if (!n) return; free2node234(n->kids[0], fn); free2node234(n->kids[1], fn); free2node234(n->kids[2], fn); free2node234(n->kids[3], fn); fn(n->elems[0]); fn(n->elems[1]); fn(n->elems[2]); sfree(n); } void free2tree234(tree234 *t, freefn fn) { if(t == NULL) return; free2node234(t->root, fn); sfree(t); } /* * Internal function to count a node. */ static int countnode234(node234 *n) { int count = 0; int i; if (!n) return 0; for (i = 0; i < 4; i++) count += n->counts[i]; for (i = 0; i < 3; i++) if (n->elems[i]) count++; return count; } /* * Count the elements in a tree. */ int count234(tree234 *t) { if (t->root) return countnode234(t->root); else return 0; } /* * Add an element e to a 2-3-4 tree t. Returns e on success, or if * an existing element compares equal, returns that. */ static void *add234_internal(tree234 *t, void *e, int index) { node234 *n, **np, *left, *right; void *orig_e = e; int c, lcount, rcount; n=0; /* warning fix */ LOG123(("adding node %p to tree %p\n", e, t)); if (t->root == NULL) { t->root = mknew(node234); t->root->elems[1] = t->root->elems[2] = NULL; t->root->kids[0] = t->root->kids[1] = NULL; t->root->kids[2] = t->root->kids[3] = NULL; t->root->counts[0] = t->root->counts[1] = 0; t->root->counts[2] = t->root->counts[3] = 0; t->root->parent = NULL; t->root->elems[0] = e; LOG123((" created root %p\n", t->root)); return orig_e; } np = &t->root; while (*np) { int childnum; n = *np; LOG123((" node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); if (index >= 0) { if (!n->kids[0]) { /* * Leaf node. We want to insert at kid position * equal to the index: * * 0 A 1 B 2 C 3 */ childnum = index; } else { /* * Internal node. We always descend through it (add * always starts at the bottom, never in the * middle). */ do { /* this is a do ... while (0) to allow `break' */ if (index <= n->counts[0]) { childnum = 0; break; } index -= n->counts[0] + 1; if (index <= n->counts[1]) { childnum = 1; break; } index -= n->counts[1] + 1; if (index <= n->counts[2]) { childnum = 2; break; } index -= n->counts[2] + 1; if (index <= n->counts[3]) { childnum = 3; break; } return NULL; /* error: index out of range */ } while (0); } } else { if ((c = t->cmp(e, n->elems[0])) < 0) childnum = 0; else if (c == 0) return n->elems[0]; /* already exists */ else if (n->elems[1] == NULL || (c = t->cmp(e, n->elems[1])) < 0) childnum = 1; else if (c == 0) return n->elems[1]; /* already exists */ else if (n->elems[2] == NULL || (c = t->cmp(e, n->elems[2])) < 0) childnum = 2; else if (c == 0) return n->elems[2]; /* already exists */ else childnum = 3; } np = &n->kids[childnum]; LOG123((" moving to child %d (%p)\n", childnum, *np)); } /* * We need to insert the new element in n at position np. */ left = NULL; lcount = 0; right = NULL; rcount = 0; while (n) { LOG123((" at %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG123((" need to insert %p/%d [%p] %p/%d at position %d\n", left, lcount, e, right, rcount, np - n->kids)); if (n->elems[1] == NULL) { /* * Insert in a 2-node; simple. */ if (np == &n->kids[0]) { LOG123((" inserting on left of 2-node\n")); n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1]; n->elems[1] = n->elems[0]; n->kids[1] = right; n->counts[1] = rcount; n->elems[0] = e; n->kids[0] = left; n->counts[0] = lcount; } else { /* np == &n->kids[1] */ LOG123((" inserting on right of 2-node\n")); n->kids[2] = right; n->counts[2] = rcount; n->elems[1] = e; n->kids[1] = left; n->counts[1] = lcount; } if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; if (n->kids[2]) n->kids[2]->parent = n; LOG123((" done\n")); break; } else if (n->elems[2] == NULL) { /* * Insert in a 3-node; simple. */ if (np == &n->kids[0]) { LOG123((" inserting on left of 3-node\n")); n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2]; n->elems[2] = n->elems[1]; n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1]; n->elems[1] = n->elems[0]; n->kids[1] = right; n->counts[1] = rcount; n->elems[0] = e; n->kids[0] = left; n->counts[0] = lcount; } else if (np == &n->kids[1]) { LOG123((" inserting in middle of 3-node\n")); n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2]; n->elems[2] = n->elems[1]; n->kids[2] = right; n->counts[2] = rcount; n->elems[1] = e; n->kids[1] = left; n->counts[1] = lcount; } else { /* np == &n->kids[2] */ LOG123((" inserting on right of 3-node\n")); n->kids[3] = right; n->counts[3] = rcount; n->elems[2] = e; n->kids[2] = left; n->counts[2] = lcount; } if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; if (n->kids[2]) n->kids[2]->parent = n; if (n->kids[3]) n->kids[3]->parent = n; LOG123((" done\n")); break; } else { node234 *m = mknew(node234); m->parent = n->parent; LOG123((" splitting a 4-node; created new node %p\n", m)); /* * Insert in a 4-node; split into a 2-node and a * 3-node, and move focus up a level. * * I don't think it matters which way round we put the * 2 and the 3. For simplicity, we'll put the 3 first * always. */ if (np == &n->kids[0]) { m->kids[0] = left; m->counts[0] = lcount; m->elems[0] = e; m->kids[1] = right; m->counts[1] = rcount; m->elems[1] = n->elems[0]; m->kids[2] = n->kids[1]; m->counts[2] = n->counts[1]; e = n->elems[1]; n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2]; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else if (np == &n->kids[1]) { m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = left; m->counts[1] = lcount; m->elems[1] = e; m->kids[2] = right; m->counts[2] = rcount; e = n->elems[1]; n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2]; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else if (np == &n->kids[2]) { m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1]; m->elems[1] = n->elems[1]; m->kids[2] = left; m->counts[2] = lcount; /* e = e; */ n->kids[0] = right; n->counts[0] = rcount; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else { /* np == &n->kids[3] */ m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1]; m->elems[1] = n->elems[1]; m->kids[2] = n->kids[2]; m->counts[2] = n->counts[2]; n->kids[0] = left; n->counts[0] = lcount; n->elems[0] = e; n->kids[1] = right; n->counts[1] = rcount; e = n->elems[2]; } m->kids[3] = n->kids[3] = n->kids[2] = NULL; m->counts[3] = n->counts[3] = n->counts[2] = 0; m->elems[2] = n->elems[2] = n->elems[1] = NULL; if (m->kids[0]) m->kids[0]->parent = m; if (m->kids[1]) m->kids[1]->parent = m; if (m->kids[2]) m->kids[2]->parent = m; if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; LOG123((" left (%p): %p/%d [%p] %p/%d [%p] %p/%d\n", m, m->kids[0], m->counts[0], m->elems[0], m->kids[1], m->counts[1], m->elems[1], m->kids[2], m->counts[2])); LOG123((" right (%p): %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1])); left = m; lcount = countnode234(left); right = n; rcount = countnode234(right); } if (n->parent) np = (n->parent->kids[0] == n ? &n->parent->kids[0] : n->parent->kids[1] == n ? &n->parent->kids[1] : n->parent->kids[2] == n ? &n->parent->kids[2] : &n->parent->kids[3]); n = n->parent; } /* * If we've come out of here by `break', n will still be * non-NULL and all we need to do is go back up the tree * updating counts. If we've come here because n is NULL, we * need to create a new root for the tree because the old one * has just split into two. */ if (n) { while (n->parent) { int count = countnode234(n); int childnum; childnum = (n->parent->kids[0] == n ? 0 : n->parent->kids[1] == n ? 1 : n->parent->kids[2] == n ? 2 : 3); n->parent->counts[childnum] = count; n = n->parent; } } else { LOG123((" root is overloaded, split into two\n")); t->root = mknew(node234); t->root->kids[0] = left; t->root->counts[0] = lcount; t->root->elems[0] = e; t->root->kids[1] = right; t->root->counts[1] = rcount; t->root->elems[1] = NULL; t->root->kids[2] = NULL; t->root->counts[2] = 0; t->root->elems[2] = NULL; t->root->kids[3] = NULL; t->root->counts[3] = 0; t->root->parent = NULL; if (t->root->kids[0]) t->root->kids[0]->parent = t->root; if (t->root->kids[1]) t->root->kids[1]->parent = t->root; LOG123((" new root is %p/%d [%p] %p/%d\n", t->root->kids[0], t->root->counts[0], t->root->elems[0], t->root->kids[1], t->root->counts[1])); } return orig_e; } void *add234(tree234 *t, void *e) { if (!t->cmp) /* tree is unsorted */ return NULL; return add234_internal(t, e, -1); } void *addpos234(tree234 *t, void *e, int index) { if (index < 0 || /* index out of range */ t->cmp) /* tree is sorted */ return NULL; /* return failure */ return add234_internal(t, e, index); /* this checks the upper bound */ } /* * Look up the element at a given numeric index in a 2-3-4 tree. * Returns NULL if the index is out of range. */ void *index234(tree234 *t, int index) { node234 *n; if (!t->root) return NULL; /* tree is empty */ if (index < 0 || index >= countnode234(t->root)) return NULL; /* out of range */ n = t->root; while (n) { if (index < n->counts[0]) n = n->kids[0]; else if (index -= n->counts[0] + 1, index < 0) return n->elems[0]; else if (index < n->counts[1]) n = n->kids[1]; else if (index -= n->counts[1] + 1, index < 0) return n->elems[1]; else if (index < n->counts[2]) n = n->kids[2]; else if (index -= n->counts[2] + 1, index < 0) return n->elems[2]; else n = n->kids[3]; } /* We shouldn't ever get here. I wonder how we did. */ return NULL; } /* * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not * found. e is always passed as the first argument to cmp, so cmp * can be an asymmetric function if desired. cmp can also be passed * as NULL, in which case the compare function from the tree proper * will be used. */ void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation, int *index) { node234 *n; void *ret; int c; int idx, ecount, kcount, cmpret; if (t->root == NULL) return NULL; if (cmp == NULL) cmp = t->cmp; n = t->root; /* * Attempt to find the element itself. */ idx = 0; ecount = -1; /* * Prepare a fake `cmp' result if e is NULL. */ cmpret = 0; if (e == NULL) { assert(relation == REL234_LT || relation == REL234_GT); if (relation == REL234_LT) cmpret = +1; /* e is a max: always greater */ else if (relation == REL234_GT) cmpret = -1; /* e is a min: always smaller */ } while (1) { for (kcount = 0; kcount < 4; kcount++) { if (kcount >= 3 || n->elems[kcount] == NULL || (c = cmpret ? cmpret : cmp(e, n->elems[kcount])) < 0) { break; } if (n->kids[kcount]) idx += n->counts[kcount]; if (c == 0) { ecount = kcount; break; } idx++; } if (ecount >= 0) break; if (n->kids[kcount]) n = n->kids[kcount]; else break; } if (ecount >= 0) { /* * We have found the element we're looking for. It's * n->elems[ecount], at tree index idx. If our search * relation is EQ, LE or GE we can now go home. */ if (relation != REL234_LT && relation != REL234_GT) { if (index) *index = idx; return n->elems[ecount]; } /* * Otherwise, we'll do an indexed lookup for the previous * or next element. (It would be perfectly possible to * implement these search types in a non-counted tree by * going back up from where we are, but far more fiddly.) */ if (relation == REL234_LT) idx--; else idx++; } else { /* * We've found our way to the bottom of the tree and we * know where we would insert this node if we wanted to: * we'd put it in in place of the (empty) subtree * n->kids[kcount], and it would have index idx * * But the actual element isn't there. So if our search * relation is EQ, we're doomed. */ if (relation == REL234_EQ) return NULL; /* * Otherwise, we must do an index lookup for index idx-1 * (if we're going left - LE or LT) or index idx (if we're * going right - GE or GT). */ if (relation == REL234_LT || relation == REL234_LE) { idx--; } } /* * We know the index of the element we want; just call index234 * to do the rest. This will return NULL if the index is out of * bounds, which is exactly what we want. */ ret = index234(t, idx); if (ret && index) *index = idx; return ret; } void *find234(tree234 *t, void *e, cmpfn234 cmp) { return findrelpos234(t, e, cmp, REL234_EQ, NULL); } void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation) { return findrelpos234(t, e, cmp, relation, NULL); } void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index) { return findrelpos234(t, e, cmp, REL234_EQ, index); } /* * Delete an element e in a 2-3-4 tree. Does not free the element, * merely removes all links to it from the tree nodes. */ static void *delpos234_internal(tree234 *t, int index) { node234 *n; void *retval; int ei = -1; retval = 0; n = t->root; LOG123(("deleting item %d from tree %p\n", index, t)); while (1) { while (n) { int ki; node234 *sub; LOG123((" node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d index=%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3], index)); if (index < n->counts[0]) { ki = 0; } else if (index -= n->counts[0]+1, index < 0) { ei = 0; break; } else if (index < n->counts[1]) { ki = 1; } else if (index -= n->counts[1]+1, index < 0) { ei = 1; break; } else if (index < n->counts[2]) { ki = 2; } else if (index -= n->counts[2]+1, index < 0) { ei = 2; break; } else { ki = 3; } /* * Recurse down to subtree ki. If it has only one element, * we have to do some transformation to start with. */ LOG123((" moving to subtree %d\n", ki)); sub = n->kids[ki]; if (!sub->elems[1]) { LOG123((" subtree has only one element!\n", ki)); if (ki > 0 && n->kids[ki-1]->elems[1]) { /* * Case 3a, left-handed variant. Child ki has * only one element, but child ki-1 has two or * more. So we need to move a subtree from ki-1 * to ki. * * . C . . B . * / \ -> / \ * [more] a A b B c d D e [more] a A b c C d D e */ node234 *sib = n->kids[ki-1]; int lastelem = (sib->elems[2] ? 2 : sib->elems[1] ? 1 : 0); sub->kids[2] = sub->kids[1]; sub->counts[2] = sub->counts[1]; sub->elems[1] = sub->elems[0]; sub->kids[1] = sub->kids[0]; sub->counts[1] = sub->counts[0]; sub->elems[0] = n->elems[ki-1]; sub->kids[0] = sib->kids[lastelem+1]; sub->counts[0] = sib->counts[lastelem+1]; if (sub->kids[0]) sub->kids[0]->parent = sub; n->elems[ki-1] = sib->elems[lastelem]; sib->kids[lastelem+1] = NULL; sib->counts[lastelem+1] = 0; sib->elems[lastelem] = NULL; n->counts[ki] = countnode234(sub); LOG123((" case 3a left\n")); LOG123((" index and left subtree count before adjustment: %d, %d\n", index, n->counts[ki-1])); index += n->counts[ki-1]; n->counts[ki-1] = countnode234(sib); index -= n->counts[ki-1]; LOG123((" index and left subtree count after adjustment: %d, %d\n", index, n->counts[ki-1])); } else if (ki < 3 && n->kids[ki+1] && n->kids[ki+1]->elems[1]) { /* * Case 3a, right-handed variant. ki has only * one element but ki+1 has two or more. Move a * subtree from ki+1 to ki. * * . B . . C . * / \ -> / \ * a A b c C d D e [more] a A b B c d D e [more] */ node234 *sib = n->kids[ki+1]; int j; sub->elems[1] = n->elems[ki]; sub->kids[2] = sib->kids[0]; sub->counts[2] = sib->counts[0]; if (sub->kids[2]) sub->kids[2]->parent = sub; n->elems[ki] = sib->elems[0]; sib->kids[0] = sib->kids[1]; sib->counts[0] = sib->counts[1]; for (j = 0; j < 2 && sib->elems[j+1]; j++) { sib->kids[j+1] = sib->kids[j+2]; sib->counts[j+1] = sib->counts[j+2]; sib->elems[j] = sib->elems[j+1]; } sib->kids[j+1] = NULL; sib->counts[j+1] = 0; sib->elems[j] = NULL; n->counts[ki] = countnode234(sub); n->counts[ki+1] = countnode234(sib); LOG123((" case 3a right\n")); } else { /* * Case 3b. ki has only one element, and has no * neighbor with more than one. So pick a * neighbor and merge it with ki, taking an * element down from n to go in the middle. * * . B . . * / \ -> | * a A b c C d a A b B c C d * * (Since at all points we have avoided * descending to a node with only one element, * we can be sure that n is not reduced to * nothingness by this move, _unless_ it was * the very first node, ie the root of the * tree. In that case we remove the now-empty * root and replace it with its single large * child as shown.) */ node234 *sib; int j; if (ki > 0) { ki--; index += n->counts[ki] + 1; } sib = n->kids[ki]; sub = n->kids[ki+1]; sub->kids[3] = sub->kids[1]; sub->counts[3] = sub->counts[1]; sub->elems[2] = sub->elems[0]; sub->kids[2] = sub->kids[0]; sub->counts[2] = sub->counts[0]; sub->elems[1] = n->elems[ki]; sub->kids[1] = sib->kids[1]; sub->counts[1] = sib->counts[1]; if (sub->kids[1]) sub->kids[1]->parent = sub; sub->elems[0] = sib->elems[0]; sub->kids[0] = sib->kids[0]; sub->counts[0] = sib->counts[0]; if (sub->kids[0]) sub->kids[0]->parent = sub; n->counts[ki+1] = countnode234(sub); sfree(sib); /* * That's built the big node in sub. Now we * need to remove the reference to sib in n. */ for (j = ki; j < 3 && n->kids[j+1]; j++) { n->kids[j] = n->kids[j+1]; n->counts[j] = n->counts[j+1]; n->elems[j] = j<2 ? n->elems[j+1] : NULL; } n->kids[j] = NULL; n->counts[j] = 0; if (j < 3) n->elems[j] = NULL; LOG123((" case 3b ki=%d\n", ki)); if (!n->elems[0]) { /* * The root is empty and needs to be * removed. */ LOG123((" shifting root!\n")); t->root = sub; sub->parent = NULL; sfree(n); } } } n = sub; } if (!retval) retval = n->elems[ei]; if (ei==-1) return NULL; /* although this shouldn't happen */ /* * Treat special case: this is the one remaining item in * the tree. n is the tree root (no parent), has one * element (no elems[1]), and has no kids (no kids[0]). */ if (!n->parent && !n->elems[1] && !n->kids[0]) { LOG123((" removed last element in tree\n")); sfree(n); t->root = NULL; return retval; } /* * Now we have the element we want, as n->elems[ei], and we * have also arranged for that element not to be the only * one in its node. So... */ if (!n->kids[0] && n->elems[1]) { /* * Case 1. n is a leaf node with more than one element, * so it's _really easy_. Just delete the thing and * we're done. */ int i; LOG123((" case 1\n")); for (i = ei; i < 2 && n->elems[i+1]; i++) n->elems[i] = n->elems[i+1]; n->elems[i] = NULL; /* * Having done that to the leaf node, we now go back up * the tree fixing the counts. */ while (n->parent) { int childnum; childnum = (n->parent->kids[0] == n ? 0 : n->parent->kids[1] == n ? 1 : n->parent->kids[2] == n ? 2 : 3); n->parent->counts[childnum]--; n = n->parent; } return retval; /* finished! */ } else if (n->kids[ei]->elems[1]) { /* * Case 2a. n is an internal node, and the root of the * subtree to the left of e has more than one element. * So find the predecessor p to e (ie the largest node * in that subtree), place it where e currently is, and * then start the deletion process over again on the * subtree with p as target. */ node234 *m = n->kids[ei]; void *target; LOG123((" case 2a\n")); while (m->kids[0]) { m = (m->kids[3] ? m->kids[3] : m->kids[2] ? m->kids[2] : m->kids[1] ? m->kids[1] : m->kids[0]); } target = (m->elems[2] ? m->elems[2] : m->elems[1] ? m->elems[1] : m->elems[0]); n->elems[ei] = target; index = n->counts[ei]-1; n = n->kids[ei]; } else if (n->kids[ei+1]->elems[1]) { /* * Case 2b, symmetric to 2a but s/left/right/ and * s/predecessor/successor/. (And s/largest/smallest/). */ node234 *m = n->kids[ei+1]; void *target; LOG123((" case 2b\n")); while (m->kids[0]) { m = m->kids[0]; } target = m->elems[0]; n->elems[ei] = target; n = n->kids[ei+1]; index = 0; } else { /* * Case 2c. n is an internal node, and the subtrees to * the left and right of e both have only one element. * So combine the two subnodes into a single big node * with their own elements on the left and right and e * in the middle, then restart the deletion process on * that subtree, with e still as target. */ node234 *a = n->kids[ei], *b = n->kids[ei+1]; int j; LOG123((" case 2c\n")); a->elems[1] = n->elems[ei]; a->kids[2] = b->kids[0]; a->counts[2] = b->counts[0]; if (a->kids[2]) a->kids[2]->parent = a; a->elems[2] = b->elems[0]; a->kids[3] = b->kids[1]; a->counts[3] = b->counts[1]; if (a->kids[3]) a->kids[3]->parent = a; sfree(b); n->counts[ei] = countnode234(a); /* * That's built the big node in a, and destroyed b. Now * remove the reference to b (and e) in n. */ for (j = ei; j < 2 && n->elems[j+1]; j++) { n->elems[j] = n->elems[j+1]; n->kids[j+1] = n->kids[j+2]; n->counts[j+1] = n->counts[j+2]; } n->elems[j] = NULL; n->kids[j+1] = NULL; n->counts[j+1] = 0; /* * It's possible, in this case, that we've just removed * the only element in the root of the tree. If so, * shift the root. */ if (n->elems[0] == NULL) { LOG123((" shifting root!\n")); t->root = a; a->parent = NULL; sfree(n); } /* * Now go round the deletion process again, with n * pointing at the new big node and e still the same. */ n = a; index = a->counts[0] + a->counts[1] + 1; } } } void *delpos234(tree234 *t, int index) { if (index < 0 || index >= countnode234(t->root)) return NULL; return delpos234_internal(t, index); } void *del234(tree234 *t, void *e) { int index; if (!findrelpos234(t, e, NULL, REL234_EQ, &index)) return NULL; /* it wasn't in there anyway */ return delpos234_internal(t, index); /* it's there; delete it. */ } #ifdef TEST /* * Test code for the 2-3-4 tree. This code maintains an alternative * representation of the data in the tree, in an array (using the * obvious and slow insert and delete functions). After each tree * operation, the verify() function is called, which ensures all * the tree properties are preserved: * - node->child->parent always equals node * - tree->root->parent always equals NULL * - number of kids == 0 or number of elements + 1; * - tree has the same depth everywhere * - every node has at least one element * - subtree element counts are accurate * - any NULL kid pointer is accompanied by a zero count * - in a sorted tree: ordering property between elements of a * node and elements of its children is preserved * and also ensures the list represented by the tree is the same * list it should be. (This last check also doubly verifies the * ordering properties, because the `same list it should be' is by * definition correctly ordered. It also ensures all nodes are * distinct, because the enum functions would get caught in a loop * if not.) */ #include #define srealloc realloc /* * Error reporting function. */ void error(char *fmt, ...) { va_list ap; printf("ERROR: "); va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); printf("\n"); } /* The array representation of the data. */ void **array; int arraylen, arraysize; cmpfn234 cmp; /* The tree representation of the same data. */ tree234 *tree; typedef struct { int treedepth; int elemcount; } chkctx; int chknode(chkctx *ctx, int level, node234 *node, void *lowbound, void *highbound) { int nkids, nelems; int i; int count; /* Count the non-NULL kids. */ for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++); /* Ensure no kids beyond the first NULL are non-NULL. */ for (i = nkids; i < 4; i++) if (node->kids[i]) { error("node %p: nkids=%d but kids[%d] non-NULL", node, nkids, i); } else if (node->counts[i]) { error("node %p: kids[%d] NULL but count[%d]=%d nonzero", node, i, i, node->counts[i]); } /* Count the non-NULL elements. */ for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++); /* Ensure no elements beyond the first NULL are non-NULL. */ for (i = nelems; i < 3; i++) if (node->elems[i]) { error("node %p: nelems=%d but elems[%d] non-NULL", node, nelems, i); } if (nkids == 0) { /* * If nkids==0, this is a leaf node; verify that the tree * depth is the same everywhere. */ if (ctx->treedepth < 0) ctx->treedepth = level; /* we didn't know the depth yet */ else if (ctx->treedepth != level) error("node %p: leaf at depth %d, previously seen depth %d", node, level, ctx->treedepth); } else { /* * If nkids != 0, then it should be nelems+1, unless nelems * is 0 in which case nkids should also be 0 (and so we * shouldn't be in this condition at all). */ int shouldkids = (nelems ? nelems+1 : 0); if (nkids != shouldkids) { error("node %p: %d elems should mean %d kids but has %d", node, nelems, shouldkids, nkids); } } /* * nelems should be at least 1. */ if (nelems == 0) { error("node %p: no elems", node, nkids); } /* * Add nelems to the running element count of the whole tree. */ ctx->elemcount += nelems; /* * Check ordering property: all elements should be strictly > * lowbound, strictly < highbound, and strictly < each other in * sequence. (lowbound and highbound are NULL at edges of tree * - both NULL at root node - and NULL is considered to be < * everything and > everything. IYSWIM.) */ if (cmp) { for (i = -1; i < nelems; i++) { void *lower = (i == -1 ? lowbound : node->elems[i]); void *higher = (i+1 == nelems ? highbound : node->elems[i+1]); if (lower && higher && cmp(lower, higher) >= 0) { error("node %p: kid comparison [%d=%s,%d=%s] failed", node, i, lower, i+1, higher); } } } /* * Check parent pointers: all non-NULL kids should have a * parent pointer coming back to this node. */ for (i = 0; i < nkids; i++) if (node->kids[i]->parent != node) { error("node %p kid %d: parent ptr is %p not %p", node, i, node->kids[i]->parent, node); } /* * Now (finally!) recurse into subtrees. */ count = nelems; for (i = 0; i < nkids; i++) { void *lower = (i == 0 ? lowbound : node->elems[i-1]); void *higher = (i >= nelems ? highbound : node->elems[i]); int subcount = chknode(ctx, level+1, node->kids[i], lower, higher); if (node->counts[i] != subcount) { error("node %p kid %d: count says %d, subtree really has %d", node, i, node->counts[i], subcount); } count += subcount; } return count; } void verify(void) { chkctx ctx; int i; void *p; ctx.treedepth = -1; /* depth unknown yet */ ctx.elemcount = 0; /* no elements seen yet */ /* * Verify validity of tree properties. */ if (tree->root) { if (tree->root->parent != NULL) error("root->parent is %p should be null", tree->root->parent); chknode(&ctx, 0, tree->root, NULL, NULL); } printf("tree depth: %d\n", ctx.treedepth); /* * Enumerate the tree and ensure it matches up to the array. */ for (i = 0; NULL != (p = index234(tree, i)); i++) { if (i >= arraylen) error("tree contains more than %d elements", arraylen); if (array[i] != p) error("enum at position %d: array says %s, tree says %s", i, array[i], p); } if (ctx.elemcount != i) { error("tree really contains %d elements, enum gave %d", ctx.elemcount, i); } if (i < arraylen) { error("enum gave only %d elements, array has %d", i, arraylen); } i = count234(tree); if (ctx.elemcount != i) { error("tree really contains %d elements, count234 gave %d", ctx.elemcount, i); } } void internal_addtest(void *elem, int index, void *realret) { int i, j; void *retval; if (arraysize < arraylen+1) { arraysize = arraylen+1+256; array = (array == NULL ? smalloc(arraysize*sizeof(*array)) : srealloc(array, arraysize*sizeof(*array))); } i = index; /* now i points to the first element >= elem */ retval = elem; /* expect elem returned (success) */ for (j = arraylen; j > i; j--) array[j] = array[j-1]; array[i] = elem; /* add elem to array */ arraylen++; if (realret != retval) { error("add: retval was %p expected %p", realret, retval); } verify(); } void addtest(void *elem) { int i; void *realret; realret = add234(tree, elem); i = 0; while (i < arraylen && cmp(elem, array[i]) > 0) i++; if (i < arraylen && !cmp(elem, array[i])) { void *retval = array[i]; /* expect that returned not elem */ if (realret != retval) { error("add: retval was %p expected %p", realret, retval); } } else internal_addtest(elem, i, realret); } void addpostest(void *elem, int i) { void *realret; realret = addpos234(tree, elem, i); internal_addtest(elem, i, realret); } void delpostest(int i) { int index = i; void *elem = array[i], *ret; /* i points to the right element */ while (i < arraylen-1) { array[i] = array[i+1]; i++; } arraylen--; /* delete elem from array */ if (tree->cmp) ret = del234(tree, elem); else ret = delpos234(tree, index); if (ret != elem) { error("del returned %p, expected %p", ret, elem); } verify(); } void deltest(void *elem) { int i; i = 0; while (i < arraylen && cmp(elem, array[i]) > 0) i++; if (i >= arraylen || cmp(elem, array[i]) != 0) return; /* don't do it! */ delpostest(i); } /* A sample data set and test utility. Designed for pseudo-randomness, * and yet repeatability. */ /* * This random number generator uses the `portable implementation' * given in ANSI C99 draft N869. It assumes `unsigned' is 32 bits; * change it if not. */ int randomnumber(unsigned *seed) { *seed *= 1103515245; *seed += 12345; return ((*seed) / 65536) % 32768; } int mycmp(void *av, void *bv) { char const *a = (char const *)av; char const *b = (char const *)bv; return strcmp(a, b); } #define lenof(x) ( sizeof((x)) / sizeof(*(x)) ) char *strings[] = { "a", "ab", "absque", "coram", "de", "palam", "clam", "cum", "ex", "e", "sine", "tenus", "pro", "prae", "banana", "carrot", "cabbage", "broccoli", "onion", "zebra", "penguin", "blancmange", "pangolin", "whale", "hedgehog", "giraffe", "peanut", "bungee", "foo", "bar", "baz", "quux", "murfl", "spoo", "breen", "flarn", "octothorpe", "snail", "tiger", "elephant", "octopus", "warthog", "armadillo", "aardvark", "wyvern", "dragon", "elf", "dwarf", "orc", "goblin", "pixie", "basilisk", "warg", "ape", "lizard", "newt", "shopkeeper", "wand", "ring", "amulet" }; #define NSTR lenof(strings) int findtest(void) { const static int rels[] = { REL234_EQ, REL234_GE, REL234_LE, REL234_LT, REL234_GT }; const static char *const relnames[] = { "EQ", "GE", "LE", "LT", "GT" }; int i, j, rel, index; char *p, *ret, *realret, *realret2; int lo, hi, mid, c; for (i = 0; i < NSTR; i++) { p = strings[i]; for (j = 0; j < sizeof(rels)/sizeof(*rels); j++) { rel = rels[j]; lo = 0; hi = arraylen-1; while (lo <= hi) { mid = (lo + hi) / 2; c = strcmp(p, array[mid]); if (c < 0) hi = mid-1; else if (c > 0) lo = mid+1; else break; } if (c == 0) { if (rel == REL234_LT) ret = (mid > 0 ? array[--mid] : NULL); else if (rel == REL234_GT) ret = (mid < arraylen-1 ? array[++mid] : NULL); else ret = array[mid]; } else { assert(lo == hi+1); if (rel == REL234_LT || rel == REL234_LE) { mid = hi; ret = (hi >= 0 ? array[hi] : NULL); } else if (rel == REL234_GT || rel == REL234_GE) { mid = lo; ret = (lo < arraylen ? array[lo] : NULL); } else ret = NULL; } realret = findrelpos234(tree, p, NULL, rel, &index); if (realret != ret) { error("find(\"%s\",%s) gave %s should be %s", p, relnames[j], realret, ret); } if (realret && index != mid) { error("find(\"%s\",%s) gave %d should be %d", p, relnames[j], index, mid); } if (realret && rel == REL234_EQ) { realret2 = index234(tree, index); if (realret2 != realret) { error("find(\"%s\",%s) gave %s(%d) but %d -> %s", p, relnames[j], realret, index, index, realret2); } } #if 0 printf("find(\"%s\",%s) gave %s(%d)\n", p, relnames[j], realret, index); #endif } } realret = findrelpos234(tree, NULL, NULL, REL234_GT, &index); if (arraylen && (realret != array[0] || index != 0)) { error("find(NULL,GT) gave %s(%d) should be %s(0)", realret, index, array[0]); } else if (!arraylen && (realret != NULL)) { error("find(NULL,GT) gave %s(%d) should be NULL", realret, index); } realret = findrelpos234(tree, NULL, NULL, REL234_LT, &index); if (arraylen && (realret != array[arraylen-1] || index != arraylen-1)) { error("find(NULL,LT) gave %s(%d) should be %s(0)", realret, index, array[arraylen-1]); } else if (!arraylen && (realret != NULL)) { error("find(NULL,LT) gave %s(%d) should be NULL", realret, index); } } int main(void) { int in[NSTR]; int i, j, k; unsigned seed = 0; for (i = 0; i < NSTR; i++) in[i] = 0; array = NULL; arraylen = arraysize = 0; tree = newtree234(mycmp); cmp = mycmp; verify(); for (i = 0; i < 10000; i++) { j = randomnumber(&seed); j %= NSTR; printf("trial: %d\n", i); if (in[j]) { printf("deleting %s (%d)\n", strings[j], j); deltest(strings[j]); in[j] = 0; } else { printf("adding %s (%d)\n", strings[j], j); addtest(strings[j]); in[j] = 1; } findtest(); } while (arraylen > 0) { j = randomnumber(&seed); j %= arraylen; deltest(array[j]); } freetree234(tree); /* * Now try an unsorted tree. We don't really need to test * delpos234 because we know del234 is based on it, so it's * already been tested in the above sorted-tree code; but for * completeness we'll use it to tear down our unsorted tree * once we've built it. */ tree = newtree234(NULL); cmp = NULL; verify(); for (i = 0; i < 1000; i++) { printf("trial: %d\n", i); j = randomnumber(&seed); j %= NSTR; k = randomnumber(&seed); k %= count234(tree)+1; printf("adding string %s at index %d\n", strings[j], k); addpostest(strings[j], k); } while (count234(tree) > 0) { printf("cleanup: tree size %d\n", count234(tree)); j = randomnumber(&seed); j %= count234(tree); printf("deleting string %s from index %d\n", array[j], j); delpostest(j); } return 0; } #endif kamailio-4.0.4/obsolete/jabber_s/xstream.c0000644000000000000000000001331212223032460017207 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include "xode.h" /* xode_stream is a way to have a consistent method of handling incoming XML Stream based events... it doesn't handle the generation of an XML Stream, but provides some facilities to help do that */ static void _xode_put_expatattribs(xode owner, const char** atts) { int i = 0; if (atts == NULL) return; while (atts[i] != '\0') { xode_put_attrib(owner, atts[i], atts[i+1]); i += 2; } } /******* internal expat callbacks *********/ static void _xode_stream_startElement(xode_stream xs, const char* name, const char** atts) { xode_pool p; /* if xode_stream is bad, get outa here */ if(xs->status > XODE_STREAM_NODE) return; if(xs->node == NULL) { p = xode_pool_heap(5*1024); /* 5k, typically 1-2k each plus copy of self and workspace */ xs->node = xode_new_frompool(p,name); _xode_put_expatattribs(xs->node, atts); if(xs->status == XODE_STREAM_ROOT) { xs->status = XODE_STREAM_NODE; /* flag status that we're processing nodes now */ (xs->f)(XODE_STREAM_ROOT, xs->node, xs->arg); /* send the root, f must free all nodes */ xs->node = NULL; } }else{ xs->node = xode_insert_tag(xs->node, name); _xode_put_expatattribs(xs->node, atts); } /* depth check */ xs->depth++; if(xs->depth > XODE_STREAM_MAXDEPTH) xs->status = XODE_STREAM_ERROR; } static void _xode_stream_endElement(xode_stream xs, const char* name) { xode parent; /* if xode_stream is bad, get outa here */ if(xs->status > XODE_STREAM_NODE) return; /* if it's already NULL we've received , tell the app and we're outta here */ if(xs->node == NULL) { xs->status = XODE_STREAM_CLOSE; (xs->f)(XODE_STREAM_CLOSE, NULL, xs->arg); }else{ parent = xode_get_parent(xs->node); /* we are the top-most node, feed to the app who is responsible to delete it */ if(parent == NULL) (xs->f)(XODE_STREAM_NODE, xs->node, xs->arg); xs->node = parent; } xs->depth--; } static void _xode_stream_charData(xode_stream xs, const char *str, int len) { /* if xode_stream is bad, get outa here */ if(xs->status > XODE_STREAM_NODE) return; if(xs->node == NULL) { /* we must be in the root of the stream where CDATA is irrelevant */ return; } xode_insert_cdata(xs->node, str, len); } static void _xode_stream_cleanup(void *arg) { xode_stream xs = (xode_stream)arg; xode_free(xs->node); /* cleanup anything left over */ XML_ParserFree(xs->parser); } /* creates a new xode_stream with given pool, xode_stream will be cleaned up w/ pool */ xode_stream xode_stream_new(xode_pool p, xode_stream_onNode f, void *arg) { xode_stream newx; if(p == NULL || f == NULL) { fprintf(stderr,"Fatal Programming Error: xode_streamnew() was improperly called with NULL.\n"); return NULL; } newx = xode_pool_malloco(p, sizeof(_xode_stream)); newx->p = p; newx->f = f; newx->arg = arg; /* create expat parser and ensure cleanup */ newx->parser = XML_ParserCreate(NULL); XML_SetUserData(newx->parser, (void *)newx); XML_SetElementHandler(newx->parser, (void (*)(void*, const char*, const char**))_xode_stream_startElement, (void (*)(void*, const char*))_xode_stream_endElement); XML_SetCharacterDataHandler(newx->parser, (void (*)(void*, const char*, int))_xode_stream_charData); xode_pool_cleanup(p, _xode_stream_cleanup, (void *)newx); return newx; } /* attempts to parse the buff onto this stream firing events to the handler, returns the last known status */ int xode_stream_eat(xode_stream xs, char *buff, int len) { char *err; xode xerr; static char maxerr[] = "maximum node size reached"; static char deeperr[] = "maximum node depth reached"; if(xs == NULL) { fprintf(stderr,"Fatal Programming Error: xode_streameat() was improperly called with NULL.\n"); return XODE_STREAM_ERROR; } if(len == 0 || buff == NULL) return xs->status; if(len == -1) /* easy for hand-fed eat calls */ len = strlen(buff); if(!XML_Parse(xs->parser, buff, len, 0)) { err = (char *)XML_ErrorString(XML_GetErrorCode(xs->parser)); xs->status = XODE_STREAM_ERROR; }else if(xode_pool_size(xode_get_pool(xs->node)) > XODE_STREAM_MAXNODE || xs->cdata_len > XODE_STREAM_MAXNODE){ err = maxerr; xs->status = XODE_STREAM_ERROR; }else if(xs->status == XODE_STREAM_ERROR){ /* set within expat handlers */ err = deeperr; }else{ err = deeperr; } /* fire parsing error event, make a node containing the error string */ if(xs->status == XODE_STREAM_ERROR) { xerr = xode_new("error"); xode_insert_cdata(xerr,err,-1); (xs->f)(XODE_STREAM_ERROR, xerr, xs->arg); } return xs->status; } kamailio-4.0.4/obsolete/jabber_s/xjab_presence.h0000644000000000000000000000432312223032460020343 0ustar rootroot/* * $Id$ * * XJAB module * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _XJAB_PRESENCE_H_ #define _XJAB_PRESENCE_H_ #include "../../str.h" #include "xjab_base.h" #define XJ_PS_CHECK -1 #define XJ_PS_OFFLINE 0 #define XJ_PS_ONLINE 1 #define XJ_PS_TERMINATED 2 #define XJ_PS_REFUSED 3 #define XJ_PRES_STATUS_NULL 0 #define XJ_PRES_STATUS_SUBS 1 #define XJ_PRES_STATUS_WAIT 2 typedef struct _xj_pres_cell { int key; str userid; int state; int status; pa_callback_f cbf; void *cbp; struct _xj_pres_cell *prev; struct _xj_pres_cell *next; } t_xj_pres_cell, *xj_pres_cell; typedef struct _xj_pres_list { int nr; xj_pres_cell clist; } t_xj_pres_list, *xj_pres_list; xj_pres_cell xj_pres_cell_new(); void xj_pres_cell_free(xj_pres_cell); void xj_pres_cell_free_all(xj_pres_cell); int xj_pres_cell_init(xj_pres_cell, str*, pa_callback_f, void*); int xj_pres_cell_init(xj_pres_cell, str*, pa_callback_f, void*); int xj_pres_cell_update(xj_pres_cell, pa_callback_f, void*); xj_pres_list xj_pres_list_init(); void xj_pres_list_free(xj_pres_list); xj_pres_cell xj_pres_list_add(xj_pres_list, xj_pres_cell); int xj_pres_list_del(xj_pres_list, str*); xj_pres_cell xj_pres_list_check(xj_pres_list, str*); void xj_pres_list_notifyall(xj_pres_list,int); #endif kamailio-4.0.4/obsolete/jabber_s/sha.c0000644000000000000000000001300212223032460016273 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Gabber * Copyright (C) 1999-2000 Dave Smith & Julian Missig * */ /* Implements the Secure Hash Algorithm (SHA1) Copyright (C) 1999 Scott G. Miller Released under the terms of the GNU General Public License v2 see file COPYING for details Credits: Robert Klep -- Expansion function fix Thomas "temas" Muldowney : -- shahash() for string fun -- Will add the int32 stuff in a few --- FIXME: This source takes int to be a 32 bit integer. This may vary from system to system. I'd use autoconf if I was familiar with it. Anyone want to help me out? */ //#include #include #include #include #include #ifndef MACOS # include # include #endif #ifndef WIN32 # include # define INT64 long long #else # define snprintf _snprintf # define INT64 __int64 #endif #define switch_endianness(x) (x<<24 & 0xff000000) | \ (x<<8 & 0x00ff0000) | \ (x>>8 & 0x0000ff00) | \ (x>>24 & 0x000000ff) /* Initial hash values */ #define Ai 0x67452301 #define Bi 0xefcdab89 #define Ci 0x98badcfe #define Di 0x10325476 #define Ei 0xc3d2e1f0 /* SHA1 round constants */ #define K1 0x5a827999 #define K2 0x6ed9eba1 #define K3 0x8f1bbcdc #define K4 0xca62c1d6 /* Round functions. Note that f2() is used in both rounds 2 and 4 */ #define f1(B,C,D) ((B & C) | ((~B) & D)) #define f2(B,C,D) (B ^ C ^ D) #define f3(B,C,D) ((B & C) | (B & D) | (C & D)) /* left circular shift functions (rotate left) */ #define rol1(x) ((x<<1) | ((x>>31) & 1)) #define rol5(A) ((A<<5) | ((A>>27) & 0x1f)) #define rol30(B) ((B<<30) | ((B>>2) & 0x3fffffff)) /* Hashes 'data', which should be a pointer to 512 bits of data (sixteen 32 bit ints), into the ongoing 160 bit hash value (five 32 bit ints) 'hash' */ int sha_hash(int *data, int *hash) { int W[80]; unsigned int A=hash[0], B=hash[1], C=hash[2], D=hash[3], E=hash[4]; unsigned int t, x, TEMP; for (t=0; t<16; t++) { #ifndef WORDS_BIGENDIAN W[t]=switch_endianness(data[t]); #else W[t]=data[t]; #endif } /* SHA1 Data expansion */ for (t=16; t<80; t++) { x=W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]; W[t]=rol1(x); } /* SHA1 main loop (t=0 to 79) This is broken down into four subloops in order to use the correct round function and constant */ for (t=0; t<20; t++) { TEMP=rol5(A) + f1(B,C,D) + E + W[t] + K1; E=D; D=C; C=rol30(B); B=A; A=TEMP; } for (; t<40; t++) { TEMP=rol5(A) + f2(B,C,D) + E + W[t] + K2; E=D; D=C; C=rol30(B); B=A; A=TEMP; } for (; t<60; t++) { TEMP=rol5(A) + f3(B,C,D) + E + W[t] + K3; E=D; D=C; C=rol30(B); B=A; A=TEMP; } for (; t<80; t++) { TEMP=rol5(A) + f2(B,C,D) + E + W[t] + K4; E=D; D=C; C=rol30(B); B=A; A=TEMP; } hash[0]+=A; hash[1]+=B; hash[2]+=C; hash[3]+=D; hash[4]+=E; return 0; } /* Takes a pointer to a 160 bit block of data (five 32 bit ints) and initializes it to the start constants of the SHA1 algorithm. This must be called before using hash in the call to sha_hash */ int sha_init(int *hash) { hash[0]=Ai; hash[1]=Bi; hash[2]=Ci; hash[3]=Di; hash[4]=Ei; return 0; } int strprintsha(char *dest, int *hashval) { int x; char *hashstr = dest; for (x=0; x<5; x++) { snprintf(hashstr, 9, "%08x", hashval[x]); hashstr+=8; } /*old way */ //snprintf(hashstr++, 1, "\0"); /*new way - by bogdan*/ *hashstr = 0; return 0; } char *shahash(const char *str) { char read_buffer[65]; //int read_buffer[64]; int c=1, i; INT64 length=0; int strsz; static char final[40]; int *hashval; hashval = (int *)malloc(20); sha_init(hashval); strsz = strlen(str); if(strsz == 0) { memset(read_buffer, 0, 65); read_buffer[0] = 0x80; sha_hash((int *)read_buffer, hashval); } while (strsz>0) { memset(read_buffer, 0, 65); strncpy((char*)read_buffer, str, 64); c = strlen((char *)read_buffer); length+=c; strsz-=c; if (strsz<=0) { length<<=3; read_buffer[c]=(char)0x80; for (i=c+1; i<64; i++) read_buffer[i]=0; if (c>55) { /* we need to do an entire new block */ sha_hash((int *)read_buffer, hashval); for (i=0; i<14; i++) ((int*)read_buffer)[i]=0; } #ifndef WORDS_BIGENDIAN for (i=0; i<8; i++) { read_buffer[56+i]=(char)(length>>(56-(i*8))) & 0xff; } #else memcpy(read_buffer+56, &length, 8); #endif } sha_hash((int *)read_buffer, hashval); str+=64; } strprintsha((char *)final, hashval); free(hashval); return (char *)final; } kamailio-4.0.4/obsolete/jabber_s/README0000644000000000000000000003714312223032460016250 0ustar rootroot jabber Module Daniel Mierla FhG FOKUS Edited by Daniel Mierla Copyright © 2003 FhG FOKUS _________________________________________________________ Table of Contents 1. User's Guide 1.1. Overview 1.1.1. New Features 1.2. Admin's Guide 1.3. User's Guide 1.4. Dependencies 1.4.1. SER Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. db_url (string) 1.5.2. jaddress (string) 1.5.3. jport (integer) 1.5.4. jdomain (string) 1.5.5. aliases (string) 1.5.6. proxy (string) 1.5.7. registrar (string) 1.5.8. workers (integer) 1.5.9. max_jobs (integer) 1.5.10. cache_time (integer) 1.5.11. delay_time (integer) 1.5.12. sleep_time (integer) 1.5.13. check_time (integer) 1.6. Exported Functions 1.6.1. jab_send_message() 1.6.2. jab_send_message() 1.6.3. jab_join_jconf() 1.6.4. jab_exit_jconf() 1.6.5. jab_go_online() 1.6.6. jab_go_offline() 2. Developer's Guide 3. Frequently Asked Questions List of Examples 1-1. Set db_url parameter 1-2. Set jaddress parameter 1-3. Set jport parameter 1-4. Set jdomain parameter 1-5. Set jdomain parameter 1-6. Set proxy parameter 1-7. Set registrar parameter 1-8. Set workers parameter 1-9. Set max_jobs parameter 1-10. Set cache_time parameter 1-11. Set delay_time parameter 1-12. Set sleep_time parameter 1-13. Set check_time parameter 1-14. jab_send_message() usage 1-15. jab_send_message() usage 1-16. jab_join_jconf() usage 1-17. jab_exit_jconf() usage 1-18. jab_go_online() usage 1-19. jab_go_offline() usage _________________________________________________________ Chapter 1. User's Guide 1.1. Overview This is new version of Jabber module that integrates XODE XML parser for parsing Jabber messages. That introduces a new module dependency: expat library. Expat is a common XML library and is the fastest available for Linux/Unix, the second over all, after msxml library. It is integrated in most of well known Linux distributions. _________________________________________________________ 1.1.1. New Features * Presence support (see doc/xxjab.cfg for a sample cfg file) (January 2003). * SIP to Jabber conference support (December 2003). * Possibility to manage all kinds of Jabber messages (message/presence/iq) (December 2003). * Aliases -- Possibility to set host aliases for addresses (see parameter's desc.) (December 2003). * Send received SIP MESSAGE messages to different IM networks (Jabber, ICQ, MSN, AIM, Yahoo) using a Jabber server (December 2003). * Send incoming Jabber instant messages as SIP MESSAGE messages. * Gateways detection -- Ability to see whether an IM gateway is up or down. _________________________________________________________ 1.2. Admin's Guide Note A more complete guide about SIMPLE2Jabber gateway can be found at http://iptel.org/ser. The part below will be removed soon, only the manual from web will be updated. The Jabber server setup is not a subject of this guide. Check http://www.jabber.org for that. Useful scripts, for creating Jabber Gateway database, or for managing the Jabber accounts form web are located in 'doc' subdirectory of the module. Main steps of using the Jabber gateway: * Create the MySQL database. * Setup the local Jabber server. * Set the module parameter values in cfg file of SER, load the dependent modules, set up the routing rules for Jabber gateway. * Run SER. The administrator of SER/Jabber gateway must inform the users what are the aliases for Jabber/Other IM networks. Other IMs could be AIM, ICQ, MSN, Yahoo, and so on. These aliases depend on the server hostname where runs SER and how local Jabber server is setup. Next is presented a use case. Prologue: * SER is running on "server.org". * Local Jabber server is running on "jabsrv.server.org". * Jabber network alias (first part of "jdomain") is "jabber.server.org" The aliases for other IM networks must be the same as JID set in Jabber configuration file for each IM transport. The JIDs of Jabber transports must start with the name of the network. For AIM, JID must start with "aim.", for ICQ with "icq" (that because I use icqv7-t), for MSN with "msn." and for Yahoo with "yahoo.". The gateway needs these to find out what transport is working and which not. For our use case these could be like "aim.server.org", "icq.server.org", "msn.server.org", "yahoo.server.org". It is indicated to have these aliases in DNS, thus the client application can resolve the DNS name. Otherwise there must be set the outbound proxy to SER server. *** Routing rules for Jabber gateway First step is to configure SER to recognize messages for Jabber gateway. Look at "doc/xjab.cfg" to see a sample. The idea is to look in messages for destination address and if it contains Jabber alias or other IM alias, that means the message is for Jabber gateway. Next step is to find out what means that message for Jabber gateway. It could be a special message what triggers the gateway to take an action or is a simple message which should be delivered to Jabber network (using the method "jab_send_message"). The special messages are for: * Registering to Jabber server (go online in Jabber network)--here must be called "jab_go_online" method. * Leaving the Jabber network (go offline in Jabber network)--here must be called "jab_go_offline" method. * Joining a Jabber conference room--here must be called "jab_join_jconf". * Leaving a Jabber conference room--here must be called "jab_exit_jconf". The destination address must follow the following patterns: * For Jabber network: "usernamejabber_server@jabber_alias". * For Jabber conference: "nicknameroomconference_server@jabber_alias" . * For AIM network: "aim_username@aim_alias". * For ICQ network: "icq_number@icq_alias". * For MSN network: "msn_usernamemsn_server@msn_alias". msn_server can be "msn.com" or "hotmail.com". * For YAHOO network: "yahoo_username@yahoo_alias". Note "jabber_alias" is the first part of "jdomain". _________________________________________________________ 1.3. User's Guide The user must activate his Jabber account associated with his SIP id. For each other IM network on which he wants to send messages, he must set an account for that IM network. The gateway is not able to create new account in foreign networks, excepting local Jabber server. When you want to send a message to someone in other IM network, you must set the destination of the message according with the pattern corresponding to that IM network (see last part of "Admin guide" chapter). Sending a message to user@jabber.xxx.org which is in Jabber network, the destination must be: userjabber.xxx.org@jabber_alias. For someone who is in Yahoo network the destination must be: user@yahoo_alias Note The SER administrator have to set the Jabber transports for each IM network in order to be able to send messages to those networks. The alias of each IM network can be found out from SER admin. You cannot send messages from your SIP client to your associated Jabber account--is something like sending messages to yourself. _________________________________________________________ 1.4. Dependencies 1.4.1. SER Modules The following modules must be loaded before this module: * A database module. * pa (Optionally) - Presence Agent. * tm - Transaction Manager. _________________________________________________________ 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running SER with this module loaded: * Expat library. _________________________________________________________ 1.5. Exported Parameters 1.5.1. db_url (string) SQL URL of database. Default value is "mysql://root@127.0.0.1/sip_jab". Example 1-1. Set db_url parameter ... modparam("jabber", "db_url", "mysql://username:password@host/sip_jab") ... _________________________________________________________ 1.5.2. jaddress (string) IP or hostname of Jabber server -- it must be the same as the value from tag of Jabber server config file. Default value is "127.0.0.1". Example 1-2. Set jaddress parameter ... modparam("jabber", "jaddress", "1.2.3.4") ... _________________________________________________________ 1.5.3. jport (integer) Port number of Jabber server. Default value is "5222". Example 1-3. Set jport parameter ... modparam("jabber", "jport", 1234) ... _________________________________________________________ 1.5.4. jdomain (string) Format: jabber.sipserver.com=. If the destination is for Jabber network the URI should be like: usernamejabber_server@jdomain or nicknameroomnameconference_server@jdomain must be a un-reserved character. By default this character is * . The destination will be transformed to username@jabber_server or roomname@conference_server/nickname before the message is sent to Jabber server. Default value is none. Example 1-4. Set jdomain parameter ... modparam("jabber", "jdomain", "jabber.sipserver.com=*") ... _________________________________________________________ 1.5.5. aliases (string) Aliases for IM networks. Format: "N;alias1=;...;aliasN=;" Destinations like '*@aliasX' could have other format than those specified for Jabber network. All from user part of the destination address will be changed to if the destination address contains . (Ex: jdomain is 'jabber.x.com=*' and msn_alias is 'msn.x.com=%'. The destination address forM MSN Network, on SIP side, is like 'username*hotmail.com@msn.x.com'. The destination address will be transformed to 'username%hotmail.com@msn.x.com'. 'msn.x.com' must be the same as the JID associated with MSN transport in Jabber configuration file (usually is 'jabberd.xml')) Default value is none. Example 1-5. Set jdomain parameter ... modparam("jabber", "aliases", "1;msn.x.com=%") ... _________________________________________________________ 1.5.6. proxy (string) Outbound proxy address. Format: ip_address:port hostname:port All SIP messages generated by gateway will be sent to that address. If is missing, the message will be delivered to the hostname of the destination address Default value is none. Example 1-6. Set proxy parameter ... modparam("jabber", "proxy", "10.0.0.1:5060 sipserver.com:5060") ... _________________________________________________________ 1.5.7. registrar (string) The address in whose behalf the INFO and ERROR messages are sent. Default value is "jabber_gateway@127.0.0.1". Example 1-7. Set registrar parameter ... modparam("jabber", "registrar", "jabber_gateway@127.0.0.1") ... _________________________________________________________ 1.5.8. workers (integer) Number of workers. Default value is 2. Example 1-8. Set workers parameter ... modparam("jabber", "workers", 2) ... _________________________________________________________ 1.5.9. max_jobs (integer) Maximum jobs per worker. Default value is 10. Example 1-9. Set max_jobs parameter ... modparam("jabber", "max_jobs", 10) ... _________________________________________________________ 1.5.10. cache_time (integer) Cache time of a Jabber connection. Default value is 600. Example 1-10. Set cache_time parameter ... modparam("jabber", "cache_time", 600) ... _________________________________________________________ 1.5.11. delay_time (integer) Time to keep a SIP message (in seconds). Default value is 90 seconds. Example 1-11. Set delay_time parameter ... modparam("jabber", "delay_time", 90) ... _________________________________________________________ 1.5.12. sleep_time (integer) Time between expired Jabber connections checking (in seconds). Default value is 20 seconds. Example 1-12. Set sleep_time parameter ... modparam("jabber", "sleep_time", 20) ... _________________________________________________________ 1.5.13. check_time (integer) Time between checking the status of JabberGW workers (in seconds). Default value is 20 seconds. Example 1-13. Set check_time parameter ... modparam("jabber", "check_time", 20) ... _________________________________________________________ 1.6. Exported Functions 1.6.1. jab_send_message() Converts SIP MESSAGE message to a Jabber message and sends it to Jabber server. Example 1-14. jab_send_message() usage ... jab_send_message(); ... _________________________________________________________ 1.6.2. jab_send_message() Converts SIP MESSAGE message to a Jabber message and sends it to Jabber server. Example 1-15. jab_send_message() usage ... jab_send_message(); ... _________________________________________________________ 1.6.3. jab_join_jconf() Join a Jabber conference--the nickname, room name and conference server address should be included in To header as: nickname%roomname%conference_server@jdomain . If the nickname is missing, then the SIP username is used. Example 1-16. jab_join_jconf() usage ... jab_join_jconf(); ... _________________________________________________________ 1.6.4. jab_exit_jconf() Leave a Jabber conference--the nickname, room name and conference server address should be included in To header as: nickname%roomname%conference_server@jdomain . Example 1-17. jab_exit_jconf() usage ... jab_exit_jconf(); ... _________________________________________________________ 1.6.5. jab_go_online() Register to the Jabber server with associated Jabber ID of the SIP user. Example 1-18. jab_go_online() usage ... jab_go_online(); ... _________________________________________________________ 1.6.6. jab_go_offline() Log off from Jabber server the associated Jabber ID of the SIP user. Example 1-19. jab_go_offline() usage ... jab_go_offline(); ... _________________________________________________________ Chapter 2. Developer's Guide The module does not provide any sort of API to use in other SER modules. _________________________________________________________ Chapter 3. Frequently Asked Questions 3.1. Where can I find more about SER? 3.2. Where can I post a question about this module? 3.3. How can I report a bug? 3.1. Where can I find more about SER? Take a look at http://iptel.org/ser. 3.2. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * http://mail.iptel.org/mailman/listinfo/serusers * http://mail.iptel.org/mailman/listinfo/serdev E-mails regarding any stable version should be sent to and e-mail regarding development versions or CVS snapshots should be send to . 3.3. How can I report a bug? Please follow the guidelines provided at: http://iptel.org/ser/bugs kamailio-4.0.4/obsolete/jabber_s/xjab_base.c0000644000000000000000000000710712223032460017447 0ustar rootroot/* * $Id$ * * eXtended JABber module - Jabber connections pool * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../parser/parse_uri.h" #include "xjab_base.h" #include "mdefines.h" /** * get the hash code - based on Andrei's function * */ int xj_get_hash(str *x, str *y) { char* p; register unsigned v; register unsigned h; if(!x && !y) return 0; h=0; if(x) { for (p=x->s; p<=(x->s+x->len-4); p+=4) { v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; h+=v^(v>>3); } v=0; for (;p<(x->s+x->len); p++) { v<<=8; v+=*p; } h+=v^(v>>3); } if(y) { for (p=y->s; p<=(y->s+y->len-4); p+=4) { v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; h+=v^(v>>3); } v=0; for (;p<(y->s+y->len); p++) { v<<=8; v+=*p; } h+=v^(v>>3); } h=((h)+(h>>11))+((h>>13)+(h>>23)); return (h)?h:1; } /** * function used to compare two xj_jkey elements */ int xj_jkey_cmp(void *x, void *y) { int n; xj_jkey a, b; a = (xj_jkey)x; b = (xj_jkey)y; if(a == NULL || a->id == NULL || a->id->s == NULL) return -1; if(b == NULL || b->id == NULL || b->id->s == NULL) return 1; // DBG("JABBER: k_kmp: comparing <%.*s> / <%.*s>\n", ((str *)a)->len, // ((str *)a)->s, ((str *)b)->len, ((str *)b)->s); if(a->hash != b->hash) return (a->hash < b->hash)?-1:1; if(a->id->len != b->id->len) return (a->id->len < b->id->len)?-1:1; n=strncmp(a->id->s,b->id->s,a->id->len); if(n!=0) return (n<0)?-1:1; return 0; } /** * free the information from a jkey */ void xj_jkey_free_p(void *p) { if(p == NULL) return; if(((xj_jkey)p)->id != NULL) { if(((xj_jkey)p)->id->s != NULL) _M_SHM_FREE(((xj_jkey)p)->id->s); _M_SHM_FREE(((xj_jkey)p)->id); } _M_SHM_FREE(p); } /** * free a pointer to a t_jab_sipmsg structure * > element where points 'from' MUST be deliberated separated */ void xj_sipmsg_free(xj_sipmsg jsmsg) { if(jsmsg == NULL) return; if(jsmsg->to.s != NULL) _M_SHM_FREE(jsmsg->to.s); // the key is deallocated when the connection is closed // if(jsmsg->jkey->id->s != NULL) // _M_SHM_FREE(jsmsg->from->id->s); if(jsmsg->msg.s != NULL) _M_SHM_FREE(jsmsg->msg.s); _M_SHM_FREE(jsmsg); } int xj_extract_aor(str* u, int t) { struct sip_uri puri; if(!u) return -1; if (parse_uri(u->s, u->len, &puri) < 0) { LOG(L_ERR, "XJAB:extract_aor: Error while parsing URI\n"); return -1; } if(t == 1) u->s = puri.user.s; u->len = puri.host.s + puri.host.len - u->s; return 0; } kamailio-4.0.4/obsolete/jabber_s/xjab_jcon.c0000644000000000000000000004213012223032460017461 0ustar rootroot/* * $Id$ * * eXtended JABber module - functions used for SIP 2 JABBER communication * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../timer.h" #include "xjab_jcon.h" #include "xjab_util.h" #include "xode.h" #include "mdefines.h" #define JB_ID_BASE "SJ" #define JB_START_STREAM "" #define JB_START_STREAM_LEN 21 #define JB_CLIENT_OPEN_STREAM "" #define JB_IQ_ROSTER_GET "" #define XJ_MAX_JCONF 12 /** * init a JABBER connection */ xj_jcon xj_jcon_init(char *hostname, int port) { xj_jcon jbc = NULL; if(hostname==NULL || strlen(hostname)<=0) return NULL; jbc = (xj_jcon)_M_MALLOC(sizeof(struct _xj_jcon)); if(jbc == NULL) return NULL; jbc->sock=-1; jbc->port = port; jbc->juid = -1; jbc->seq_nr = 0; jbc->hostname = (char*)_M_MALLOC(strlen(hostname)+1); if(jbc->hostname == NULL) { _M_FREE(jbc); return NULL; } strcpy(jbc->hostname, hostname); jbc->allowed = jbc->ready = XJ_NET_NUL; jbc->jconf = NULL; jbc->nrjconf = 0; jbc->plist = xj_pres_list_init(); if(jbc->plist == NULL) { _M_FREE(jbc->hostname); _M_FREE(jbc); return NULL; } return jbc; } /** * connect to JABBER server */ int xj_jcon_connect(xj_jcon jbc) { struct sockaddr_in address; struct hostent *he; int sock; // open connection to server if((sock = socket(AF_INET, SOCK_STREAM, 0))<0) { DBG("XJAB:xj_jcon_connect: Error to create the socket\n"); return -1; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_connect: socket [%d]\n", sock); #endif he=gethostbyname(jbc->hostname); if(he == NULL) { DBG("XJAB:xj_jcon_connect: Error getting info about Jabber server" " address\n"); return -1; } // fill the fields of the address memcpy(&address.sin_addr, he->h_addr, he->h_length); address.sin_family=AF_INET; address.sin_port=htons(jbc->port); // try to connect with Jabber server if (connect(sock, (struct sockaddr *)&address, sizeof(address))<0) { DBG("XJAB:xj_jcon_connect: Error to connect with Jabber server\n"); return -1; } jbc->sock = sock; return 0; } /** * set the Jabber internal ID */ void xj_jcon_set_juid(xj_jcon jbc, int _juid) { if(jbc == NULL) return; jbc->juid = _juid; } /** * return the Jabber internal ID */ int xj_jcon_get_juid(xj_jcon jbc) { if(jbc == NULL) return -1; return jbc->juid; } /** * disconnect from JABBER server */ int xj_jcon_disconnect(xj_jcon jbc) { if(jbc == NULL || jbc->sock < 0) return -1; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_disconnect: -----START-----\n"); DBG("XJAB:xj_jcon_disconnect: socket [%d]\n", jbc->sock); #endif xj_jcon_send_presence(jbc, NULL, "unavailable", NULL, NULL); if(send(jbc->sock, "", 16, 0) < 16) DBG("XJAB:xj_jcon_disconnect: error closing stream\n"); if(close(jbc->sock) == -1) DBG("XJAB:xj_jcon_disconnect: error closing socket\n"); jbc->sock = -1; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_disconnect: -----END-----\n"); #endif return 0; } /** * authentication to the JABBER server */ int xj_jcon_user_auth(xj_jcon jbc, char *username, char *passwd, char *resource) { char msg_buff[4096]; int n, i, err; char *p0, *p1; xode x, y, z; /*** send open stream tag **/ sprintf(msg_buff, JB_CLIENT_OPEN_STREAM, jbc->hostname); if(send(jbc->sock, msg_buff, strlen(msg_buff), 0) != strlen(msg_buff)) goto error; n = recv(jbc->sock, msg_buff, 4096, 0); msg_buff[n] = 0; if(strncasecmp(msg_buff, JB_START_STREAM, JB_START_STREAM_LEN)) goto error; p0 = strstr(msg_buff + JB_START_STREAM_LEN, "id='"); if(p0 == NULL) goto error; p0 += 4; p1 = strchr(p0, '\''); if(p1 == NULL) goto error; jbc->stream_id = (char*)_M_MALLOC(p1-p0+1); strncpy(jbc->stream_id, p0, p1-p0); jbc->stream_id[p1-p0] = 0; sprintf(msg_buff, "%08X", jbc->seq_nr); x = xode_new_tag("iq"); if(!x) return -1; xode_put_attrib(x, "id", msg_buff); xode_put_attrib(x, "type", "get"); y = xode_insert_tag(x, "query"); xode_put_attrib(y, "xmlns", "jabber:iq:auth"); z = xode_insert_tag(y, "username"); xode_insert_cdata(z, username, -1); p0 = xode_to_str(x); n = strlen(p0); i = send(jbc->sock, p0, n, 0); if(i != n) goto errorx; xode_free(x); // receive response // try 10 times i = 10; while(i) { if((n = recv(jbc->sock, msg_buff, 4096, 0)) > 0) { msg_buff[n] = 0; break; } usleep(1000); i--; } if(!i) goto error; x = xode_from_strx(msg_buff, n, &err, &i); p0 = msg_buff; if(err) p0 += i; if(strncasecmp(xode_get_name(x), "iq", 2)) goto errorx; if((x = xode_get_tag(x, "query?xmlns=jabber:iq:auth")) == NULL) goto errorx; y = xode_new_tag("query"); xode_put_attrib(y, "xmlns", "jabber:iq:auth"); z = xode_insert_tag(y, "username"); xode_insert_cdata(z, username, -1); z = xode_insert_tag(y, "resource"); xode_insert_cdata(z, resource, -1); if(xode_get_tag(x, "digest") != NULL) { // digest authentication //sprintf(msg_buff, "%s%s", jbc->stream_id, passwd); strcpy(msg_buff, jbc->stream_id); strcat(msg_buff, passwd); //DBG("XJAB:xj_jcon_user_auth: [%s:%s]\n", jbc->stream_id, passwd); p1 = shahash(msg_buff); z = xode_insert_tag(y, "digest"); xode_insert_cdata(z, p1, -1); } else { // plaint text authentication z = xode_insert_tag(y, "password"); xode_insert_cdata(z, passwd, -1); } y = xode_wrap(y, "iq"); jbc->seq_nr++; sprintf(msg_buff, "%08X", jbc->seq_nr); xode_put_attrib(y, "id", msg_buff); xode_put_attrib(y, "type", "set"); p1 = xode_to_str(y); n = strlen(p1); i = send(jbc->sock, p1, n, 0); if(i != n) { xode_free(y); goto errorx; } xode_free(x); xode_free(y); // receive response // try 10 times i = 10; while(i) { if((n = recv(jbc->sock, msg_buff, 4096, 0)) > 0) { msg_buff[n] = 0; break; } usleep(1000); i--; } if(!i) goto error; x = xode_from_strx(msg_buff, n, &err, &i); p0 = msg_buff; if(err) p0 += i; if(strncasecmp(xode_get_name(x), "iq", 2) || strncasecmp(xode_get_attrib(x, "type"), "result", 6)) goto errorx; jbc->resource = (char*)_M_MALLOC(strlen(resource)+1); strcpy(jbc->resource, resource); jbc->allowed = XJ_NET_ALL; jbc->ready = XJ_NET_JAB; return 0; errorx: xode_free(x); error: return -1; } /** * receive the list of the roster */ int xj_jcon_get_roster(xj_jcon jbc) { int n = strlen(JB_IQ_ROSTER_GET); #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_get_roster\n"); #endif if(send(jbc->sock, JB_IQ_ROSTER_GET, n, 0) != n) return -1; return 0; } /** * add a new contact in user's roster */ int xj_jcon_set_roster(xj_jcon jbc, char* jid, char *type) { xode x; char *p; int n; char buff[16]; if(!jbc || !jid) return -1; x = xode_new_tag("item"); if(!x) return -1; xode_put_attrib(x, "jid", jid); if(type != NULL) xode_put_attrib(x, "subscription", type); x = xode_wrap(x, "query"); xode_put_attrib(x, "xmlns", "jabber:iq:roster"); x = xode_wrap(x, "iq"); xode_put_attrib(x, "type", "set"); jbc->seq_nr++; sprintf(buff, "%08X", jbc->seq_nr); xode_put_attrib(x, "id", buff); p = xode_to_str(x); n = strlen(p); if(send(jbc->sock, p, n, 0) != n) { DBG("XJAB:xj_jcon_set_roster: Error - item not sent\n"); goto error; } xode_free(x); return 0; error: xode_free(x); return -1; } /** * send a message through a JABBER connection * params are pairs (buffer, len) */ int xj_jcon_send_msg(xj_jcon jbc, char *to, int tol, char *msg, int msgl, int type) { char msg_buff[4096], *p; int n; xode x; if(jbc == NULL) return -1; x = xode_new_tag("body"); if(!x) return -1; xode_insert_cdata(x, msg, msgl); x = xode_wrap(x, "message"); strncpy(msg_buff, to, tol); msg_buff[tol] = 0; xode_put_attrib(x, "to", msg_buff); switch(type) { case XJ_JMSG_CHAT: xode_put_attrib(x, "type", "chat"); break; case XJ_JMSG_GROUPCHAT: xode_put_attrib(x, "type", "groupchat"); break; default: xode_put_attrib(x, "type", "normal"); } p = xode_to_str(x); n = strlen(p); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_send_msg: jabber msg:\n%s\n", p); #endif if(send(jbc->sock, p, n, 0) != n) { DBG("XJAB:xj_jcon_send_msg: error - message not sent\n"); goto error; } xode_free(x); return 0; error: xode_free(x); return -1; } /** * receive a message from a JABBER connection * NOT USED */ int xj_jcon_recv_msg(xj_jcon jbc, char *from, char *msg) { //char msg_buff[4096]; //sprintf(msg_buff, JB_MSG_NORMAL, to, msg); //send(jbc->sock, msg_buff, strlen(msg_buff), 0); return 0; } /** * send presence * type - "unavailable", "subscribe", "subscribed" .... * status - "online", "away", "unavailable" ... * priority - "0", "1", ... */ int xj_jcon_send_presence(xj_jcon jbc, char *sto, char *type, char *status, char *priority) { char *p; int n; xode x, y; if(jbc == NULL) return -1; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_send_presence: -----START-----\n"); #endif x = xode_new_tag("presence"); if(!x) return -1; if(sto != NULL) xode_put_attrib(x, "to", sto); if(type != NULL) xode_put_attrib(x, "type", type); if(status != NULL) { y = xode_insert_tag(x, "status"); xode_insert_cdata(y, status, strlen(status)); } if(priority != NULL) { y = xode_insert_tag(x, "priority"); xode_insert_cdata(y, priority, strlen(priority)); } p = xode_to_str(x); n = strlen(p); if(send(jbc->sock, p, n, 0) != n) { DBG("XJAB:xj_jcon_send_presence: Error - presence not sent\n"); goto error; } xode_free(x); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_send_presence: presence status was sent\n"); #endif return 0; error: xode_free(x); return -1; } /** * send subscribe for user's presence */ int xj_jcon_send_subscribe(xj_jcon jbc, char *to, char *from, char *type) { char *p; int n; xode x; if(!jbc || !to) return -1; x = xode_new_tag("presence"); if(!x) return -1; xode_put_attrib(x, "to", to); if(from != NULL) xode_put_attrib(x, "from", from); if(type != NULL) xode_put_attrib(x, "type", type); p = xode_to_str(x); n = strlen(p); if(send(jbc->sock, p, n, 0) != n) { DBG("XJAB:xj_jcon_send_subscribe: Error - subscribe not sent\n"); goto error; } xode_free(x); return 0; error: xode_free(x); return -1; } /** * free the allocated memory space of a JABBER connection */ int xj_jcon_free(xj_jcon jbc) { xj_jconf jcf; if(jbc == NULL) return -1; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_free: -----START-----\n"); #endif //if(jbc->sock != -1) // jb_disconnect(jbc); if(jbc->hostname != NULL) _M_FREE(jbc->hostname); if(jbc->stream_id != NULL) _M_FREE(jbc->stream_id); if(jbc->resource != NULL) _M_FREE(jbc->resource); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_free: %d conferences\n", jbc->nrjconf); #endif while(jbc->nrjconf > 0) { if((jcf=delpos234(jbc->jconf,0))!=NULL) xj_jconf_free(jcf); jbc->nrjconf--; } xj_pres_list_free(jbc->plist); _M_FREE(jbc); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_free: -----END-----\n"); #endif return 0; } /** * create a open connection to Jabber * - id : id of the connection * - jbc : pointer to Jabber connection * - cache_time : life time of the connection * - delay_time : time needed to became an active connection * #return : pointer to the structure or NULL on error */ int xj_jcon_set_attrs(xj_jcon jbc, xj_jkey jkey, int cache_time, int delay_time) { int t; if(jbc==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; jbc->jkey = jkey; t = get_ticks(); jbc->expire = t + cache_time; jbc->ready = t + delay_time; return 0; } /** * update the life time of the connection * - ojc : pointer to the open connection * - cache_time : number of seconds to keep the connection open * #return : 0 on success or <0 on error */ int xj_jcon_update(xj_jcon jbc, int cache_time) { if(jbc == NULL) return -1; #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_update [%.*s] %d\n", jbc->jkey->id->len, jbc->jkey->id->s, cache_time); #endif jbc->expire = get_ticks() + cache_time; return 0; } int xj_jcon_is_ready(xj_jcon jbc, char *to, int tol, char dl) { char *p; str sto; xj_jconf jcf = NULL; if(!jbc || !to || tol <= 0) return -1; sto.s = to; sto.len = tol; if(!xj_jconf_check_addr(&sto, dl)) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_is_ready: destination=conference\n"); #endif if((jcf=xj_jcon_get_jconf(jbc, &sto, dl))!=NULL) return (jcf->status & XJ_JCONF_READY)?0:3; #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_is_ready: conference does not exist\n"); #endif return -1; } p = to; while(p < to+tol && *p!='@') p++; if(p>=to+tol) return -1; p++; if(!strncasecmp(p, XJ_AIM_NAME, XJ_AIM_LEN)) return (jbc->ready & XJ_NET_AIM)?0:((jbc->allowed & XJ_NET_AIM)?1:2); if(!strncasecmp(p, XJ_ICQ_NAME, XJ_ICQ_LEN)) return (jbc->ready & XJ_NET_ICQ)?0:((jbc->allowed & XJ_NET_ICQ)?1:2); if(!strncasecmp(p, XJ_MSN_NAME, XJ_MSN_LEN)) return (jbc->ready & XJ_NET_MSN)?0:((jbc->allowed & XJ_NET_MSN)?1:2); if(!strncasecmp(p, XJ_YAH_NAME, XJ_YAH_LEN)) return (jbc->ready & XJ_NET_YAH)?0:((jbc->allowed & XJ_NET_YAH)?1:2); #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_is_ready: destination=jabber\n"); #endif return 0; } xj_jconf xj_jcon_get_jconf(xj_jcon jbc, str* sid, char dl) { xj_jconf jcf = NULL, p; if(!jbc || !sid || !sid->s || sid->len <= 0) return NULL; #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_get_jconf: looking for conference\n"); #endif if((jcf = xj_jconf_new(sid))==NULL) return NULL; if(xj_jconf_init_sip(jcf, jbc->jkey->id, dl)) goto clean; if(jbc->nrjconf && (p = find234(jbc->jconf, (void*)jcf, NULL)) != NULL) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_get_jconf: conference found\n"); #endif xj_jconf_free(jcf); return p; } if(jbc->nrjconf >= XJ_MAX_JCONF) goto clean; if(jbc->nrjconf==0) if(jbc->jconf==NULL) if((jbc->jconf = newtree234(xj_jconf_cmp)) == NULL) goto clean; if((p = add234(jbc->jconf, (void*)jcf)) != NULL) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_get_jconf: new conference created\n"); #endif jbc->nrjconf++; return p; } clean: DBG("XJAB: xj_jcon_get_jconf: error looking for conference\n"); xj_jconf_free(jcf); return NULL; } xj_jconf xj_jcon_check_jconf(xj_jcon jbc, char* id) { str sid; xj_jconf jcf = NULL, p = NULL; if(!jbc || !id || !jbc->nrjconf) return NULL; #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_get_jconf: looking for conference\n"); #endif sid.s = id; sid.len = strlen(id); if((jcf = xj_jconf_new(&sid))==NULL) return NULL; if(xj_jconf_init_jab(jcf)) goto clean; if((p = find234(jbc->jconf, (void*)jcf, NULL)) != NULL) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_get_jconf: conference found\n"); #endif xj_jconf_free(jcf); return p; } clean: #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_get_jconf: conference not found\n"); #endif xj_jconf_free(jcf); return NULL; } int xj_jcon_jconf_presence(xj_jcon jbc, xj_jconf jcf, char* type, char* status) { char buff[256]; strncpy(buff, jcf->room.s, jcf->room.len + jcf->server.len +1); buff[jcf->room.len + jcf->server.len +1] = '/'; buff[jcf->room.len + jcf->server.len +2] = 0; buff[jcf->room.len] = '@'; strncat(buff, jcf->nick.s, jcf->nick.len); return xj_jcon_send_presence(jbc,buff,type,status,NULL); } int xj_jcon_del_jconf(xj_jcon jbc, str *sid, char dl, int flag) { xj_jconf jcf = NULL, p = NULL; if(!jbc || !sid || !sid->s || sid->len <= 0) return -1; #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_del_jconf: deleting conference of <%.*s>\n", sid->len, sid->s); #endif if((jcf = xj_jconf_new(sid))==NULL) return -1; if(xj_jconf_init_sip(jcf, jbc->jkey->id, dl)) { xj_jconf_free(jcf); return -1; } p = del234(jbc->jconf, (void*)jcf); if(p != NULL) { if(flag == XJ_JCMD_UNSUBSCRIBE) xj_jcon_jconf_presence(jbc, jcf, "unavailable", NULL); jbc->nrjconf--; xj_jconf_free(p); #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_jcon_del_jconf: conference deleted\n"); #endif } xj_jconf_free(jcf); return 0; } /********** *********/ kamailio-4.0.4/obsolete/jabber_s/xjab_util.h0000644000000000000000000000435412223032460017520 0ustar rootroot/* * $Id$ * * eXtended JABber module * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _XJAB_UTIL_H_ #define _XJAB_UTIL_H_ #include "xjab_base.h" #include "xjab_jcon.h" #include "../../str.h" /********** ***/ typedef struct _xj_jmsg_queue { int len; // maximum size of the queue int size; // number of elements in the queue int cache; // cache time (seconds) int *expire; // expire time of the queued message xj_sipmsg *jsm; // pointer to the message xj_jcon *ojc; // pointer to the connection which will be used on sending } t_xj_jmsg_queue, *xj_jmsg_queue; /********** ***/ typedef struct _xj_jcon_pool { int len; // maximum len of the pool xj_jcon *ojc; // the connections to the Jabber t_xj_jmsg_queue jmqueue; // messages queue } t_xj_jcon_pool, *xj_jcon_pool; /********** LOOK AT IMPLEMENTATION OF FUNCTIONS FOR DESCRIPTION ***/ xj_jcon_pool xj_jcon_pool_init(int, int, int); int xj_jcon_pool_add(xj_jcon_pool, xj_jcon); xj_jcon xj_jcon_pool_get(xj_jcon_pool, xj_jkey); int xj_jcon_pool_del(xj_jcon_pool, xj_jkey); void xj_jccon_pool_free(xj_jcon_pool); void xj_jcon_pool_print(xj_jcon_pool); int xj_jcon_pool_add_jmsg(xj_jcon_pool, xj_sipmsg, xj_jcon); int xj_jcon_pool_del_jmsg(xj_jcon_pool, int); /********** ***/ #endif kamailio-4.0.4/obsolete/jabber_s/xjab_wlist.c0000644000000000000000000003664612223032460017711 0ustar rootroot/* * $Id$ * * eXtended JABber module - worker implementation * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * --- * * History * ------- * 2003-02-24 added 'xj_wlist_set_flag' function (dcm) * 2003-03-11 major locking changes - uses locking.h (andrei) * */ #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "xjab_worker.h" #include "mdefines.h" #define XJ_DEF_JDELIM '*' /** * init a workers list * - pipes : communication pipes * - size : size of list - number of workers * - max : maximum number of jobs per worker * #return : pointer to workers list or NULL on error */ xj_wlist xj_wlist_init(int **pipes, int size, int max, int cache_time, int sleep_time, int delay_time) { int i; xj_wlist jwl = NULL; if(pipes == NULL || size <= 0 || max <= 0) return NULL; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_init: -----START-----\n"); #endif jwl = (xj_wlist)_M_SHM_MALLOC(sizeof(t_xj_wlist)); if(jwl == NULL) return NULL; jwl->len = size; jwl->maxj = max; jwl->cachet = cache_time; jwl->delayt = delay_time; jwl->sleept = sleep_time; jwl->aliases = NULL; jwl->sems = NULL; i = 0; /* alloc locks*/ if((jwl->sems = lock_set_alloc(size)) == NULL){ LOG(L_CRIT, "jabber: failed to alloc lock set\n"); goto clean; }; /* init the locks*/ if (lock_set_init(jwl->sems)==0){ LOG(L_CRIT, "jabber: failed to initialize the locks\n"); goto clean; }; jwl->workers = (xj_worker)_M_SHM_MALLOC(size*sizeof(t_xj_worker)); if(jwl->workers == NULL){ lock_set_destroy(jwl->sems); goto clean; } for(i = 0; i < size; i++) { jwl->workers[i].nr = 0; jwl->workers[i].pid = 0; jwl->workers[i].wpipe = pipes[i][1]; jwl->workers[i].rpipe = pipes[i][0]; if((jwl->workers[i].sip_ids = newtree234(xj_jkey_cmp)) == NULL){ lock_set_destroy(jwl->sems); goto clean; } } return jwl; clean: DBG("XJAB:xj_wlist_init: error occurred -> cleaning\n"); if(jwl->sems != NULL) lock_set_dealloc(jwl->sems); if(jwl->workers != NULL) { while(i>=0) { if(jwl->workers[i].sip_ids == NULL) free2tree234(jwl->workers[i].sip_ids, xj_jkey_free_p); i--; } _M_SHM_FREE(jwl->workers); } _M_SHM_FREE(jwl); return NULL; } /** * set the p.id's of the workers * - jwl : pointer to the workers list * - pids : p.id's array * - size : number of pids * #return : 0 on success or <0 on error */ int xj_wlist_set_pid(xj_wlist jwl, int pid, int idx) { if(jwl == NULL || pid <= 0 || idx < 0 || idx >= jwl->len) return -1; lock_set_get(jwl->sems, idx); jwl->workers[idx].pid = pid; lock_set_release(jwl->sems, idx); return 0; } /** * free jab_wlist * - jwl : pointer to the workers list */ void xj_wlist_free(xj_wlist jwl) { int i; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_free: freeing 'xj_wlist' memory ...\n"); #endif if(jwl == NULL) return; if(jwl->workers != NULL) { for(i=0; ilen; i++) free2tree234(jwl->workers[i].sip_ids, xj_jkey_free_p); _M_SHM_FREE(jwl->workers); } if(jwl->aliases != NULL) { if(jwl->aliases->d) _M_SHM_FREE(jwl->aliases->d); if(jwl->aliases->jdm != NULL) { _M_SHM_FREE(jwl->aliases->jdm->s); _M_SHM_FREE(jwl->aliases->jdm); } if(jwl->aliases->proxy != NULL) { _M_SHM_FREE(jwl->aliases->proxy->s); _M_SHM_FREE(jwl->aliases->proxy); } if(jwl->aliases->size > 0) { for(i=0; ialiases->size; i++) _M_SHM_FREE(jwl->aliases->a[i].s); _M_SHM_FREE(jwl->aliases->a); } _M_SHM_FREE(jwl->aliases); jwl->aliases = NULL; } if(jwl->sems != NULL){ lock_set_destroy(jwl->sems); lock_set_dealloc(jwl->sems); } _M_SHM_FREE(jwl); } /** * return communication pipe with the worker that will process the message for * the id 'sid' only if it exists, or -1 if error * - jwl : pointer to the workers list * - sid : id of the entity (connection to Jabber - usually SHOULD be FROM * header of the incoming SIP message) * - p : will point to the SHM location of the 'sid' in jwl */ int xj_wlist_check(xj_wlist jwl, xj_jkey jkey, xj_jkey *p) { int i; if(jwl==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; i = 0; *p = NULL; while(i < jwl->len) { lock_set_get(jwl->sems, i); if(jwl->workers[i].pid <= 0) { lock_set_release(jwl->sems, i); i++; continue; } if((*p = find234(jwl->workers[i].sip_ids, (void*)jkey, NULL)) != NULL) { lock_set_release(jwl->sems, i); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_check: entry exists for <%.*s> in the" " pool of <%d> [%d]\n",jkey->id->len, jkey->id->s, jwl->workers[i].pid,i); #endif return jwl->workers[i].wpipe; } lock_set_release(jwl->sems, i); i++; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_check: entry does not exist for <%.*s>\n", jkey->id->len, jkey->id->s); #endif return -1; } /** * return communication pipe with the worker that will process the message for * the id 'sid', or -1 if error * - jwl : pointer to the workers list * - sid : id of the entity (connection to Jabber - usually SHOULD be FROM * header of the incoming SIP message) * - p : will point to the SHM location of the 'sid' in jwl */ int xj_wlist_get(xj_wlist jwl, xj_jkey jkey, xj_jkey *p) { int i = 0, pos = -1, min = 100000; xj_jkey msid = NULL; if(jwl==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; *p = NULL; while(i < jwl->len) { lock_set_get(jwl->sems, i); if(jwl->workers[i].pid <= 0) { lock_set_release(jwl->sems, i); i++; continue; } if((*p = find234(jwl->workers[i].sip_ids, (void*)jkey, NULL))!=NULL) { if(pos >= 0) lock_set_release(jwl->sems, pos); lock_set_release(jwl->sems, i); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_get: entry already exists for <%.*s> in the" " pool of <%d> [%d]\n",jkey->id->len, jkey->id->s, jwl->workers[i].pid,i); #endif return jwl->workers[i].wpipe; } if(min > jwl->workers[i].nr) { if(pos >= 0) lock_set_release(jwl->sems, pos); pos = i; min = jwl->workers[i].nr; } else lock_set_release(jwl->sems, i); i++; } if(pos >= 0 && jwl->workers[pos].nr < jwl->maxj) { jwl->workers[pos].nr++; msid = (xj_jkey)_M_SHM_MALLOC(sizeof(t_xj_jkey)); if(msid == NULL) goto error; msid->id = (str*)_M_SHM_MALLOC(sizeof(str)); if(msid->id == NULL) { _M_SHM_FREE(msid); goto error; } msid->id->s = (char*)_M_SHM_MALLOC(jkey->id->len); if(msid->id == NULL) { _M_SHM_FREE(msid->id); _M_SHM_FREE(msid); goto error; } if((*p = add234(jwl->workers[pos].sip_ids, msid)) != NULL) { msid->id->len = jkey->id->len; memcpy(msid->id->s, jkey->id->s, jkey->id->len); msid->hash = jkey->hash; msid->flag = XJ_FLAG_OPEN; lock_set_release(jwl->sems, pos); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_get: new entry for <%.*s> in the pool of" " <%d> - [%d]\n", jkey->id->len, jkey->id->s, jwl->workers[pos].pid, pos); #endif return jwl->workers[pos].wpipe; } _M_SHM_FREE(msid->id->s); _M_SHM_FREE(msid->id); _M_SHM_FREE(msid); } error: if(pos >= 0) lock_set_release(jwl->sems, pos); DBG("XJAB:xj_wlist_get: cannot create a new entry for <%.*s>\n", jkey->id->len, jkey->id->s); return -1; } /** * set the flag of the connection identified by 'jkey' * */ int xj_wlist_set_flag(xj_wlist jwl, xj_jkey jkey, int fl) { int i; xj_jkey p = NULL; if(jwl==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_set_flag: looking for <%.*s>" " having id=%d\n", jkey->id->len, jkey->id->s, jkey->hash); #endif i = 0; while(i < jwl->len) { lock_set_get(jwl->sems, i); if(jwl->workers[i].pid <= 0) { lock_set_release(jwl->sems, i); i++; continue; } if((p=find234(jwl->workers[i].sip_ids, (void*)jkey, NULL)) != NULL) { p->flag = fl; lock_set_release(jwl->sems, i); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_set_flag: the connection for <%.*s>" " marked with flag=%d", jkey->id->len, jkey->id->s, fl); #endif return jwl->workers[i].wpipe; } lock_set_release(jwl->sems, i); i++; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_set_flag: entry does not exist for <%.*s>\n", jkey->id->len, jkey->id->s); #endif return -1; } /** * set IM aliases, jdomain and outbound proxy * * #return 0 if OK */ int xj_wlist_set_aliases(xj_wlist jwl, char *als, char *jd, char *pa) { char *p, *p0, *p1; int i, n; if(jwl == NULL) return -1; if(!jd) // || !als || strlen(als)<2) return 0; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_set_aliases\n"); #endif if((jwl->aliases = (xj_jalias)_M_SHM_MALLOC(sizeof(t_xj_jalias)))==NULL) { DBG("XJAB:xj_wlist_set_aliases: not enough SHMemory.\n"); return -1; } jwl->aliases->jdm = NULL; jwl->aliases->proxy = NULL; jwl->aliases->dlm = XJ_DEF_JDELIM; // default user part delimiter jwl->aliases->size = 0; jwl->aliases->a = NULL; jwl->aliases->d = NULL; // set the jdomain if(jd != NULL && (n=strlen(jd))>2) { p = jd; while(p < jd+n && *p!='=') p++; if(paliases->dlm = *(p+1); n = p - jd; } if((jwl->aliases->jdm = (str*)_M_SHM_MALLOC(sizeof(str)))== NULL) { DBG("XJAB:xj_wlist_set_aliases: not enough SHMemory!?\n"); _M_SHM_FREE(jwl->aliases); jwl->aliases = NULL; return -1; } jwl->aliases->jdm->len = n; if((jwl->aliases->jdm->s=(char*)_M_SHM_MALLOC(jwl->aliases->jdm->len)) == NULL) { DBG("XJAB:xj_wlist_set_aliases: not enough SHMemory!?!\n"); _M_SHM_FREE(jwl->aliases->jdm); _M_SHM_FREE(jwl->aliases); jwl->aliases = NULL; } strncpy(jwl->aliases->jdm->s, jd, jwl->aliases->jdm->len); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_set_aliases: jdomain=%.*s delim=%c\n", jwl->aliases->jdm->len, jwl->aliases->jdm->s, jwl->aliases->dlm); #endif } // set the proxy address if(pa && strlen(pa)>0) { if((jwl->aliases->proxy = (str*)_M_SHM_MALLOC(sizeof(str)))==NULL) { DBG("XJAB:xj_wlist_set_aliases: not enough SHMemory!!\n"); goto clean3; } i = jwl->aliases->proxy->len = strlen(pa); // check if proxy address has sip: prefix if(i < 4 || pa[0]!='s' || pa[1]!='i' || pa[2]!='p' || pa[3]!=':') jwl->aliases->proxy->len += 4; if((jwl->aliases->proxy->s= (char*)_M_SHM_MALLOC(jwl->aliases->proxy->len)) == NULL) { DBG("XJAB:xj_wlist_set_aliases: not enough SHMemory!!!\n"); _M_SHM_FREE(jwl->aliases->proxy); goto clean3; } p0 = jwl->aliases->proxy->s; if(jwl->aliases->proxy->len != i) { strncpy(p0, "sip:", 4); p0 += 4; } strncpy(p0, pa, i); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_set_aliases: outbound proxy=[%.*s]\n", jwl->aliases->proxy->len, jwl->aliases->proxy->s); #endif } // set the IM aliases if(!als || strlen(als)<2) return 0; if((p = strchr(als, ';')) == NULL) { DBG("XJAB:xj_wlist_set_aliases: bad parameter value\n"); return -1; } if((jwl->aliases->size = atoi(als)) <= 0) { DBG("XJAB:xj_wlist_set_aliases: wrong number of aliases\n"); return 0; } jwl->aliases->d = (char*)_M_SHM_MALLOC(jwl->aliases->size*sizeof(char)); if(jwl->aliases->d == NULL) { DBG("XJAB:xj_wlist_set_aliases: not enough SHMemory..\n"); goto clean2; } memset(jwl->aliases->d, 0, jwl->aliases->size); jwl->aliases->a = (str*)_M_SHM_MALLOC(jwl->aliases->size*sizeof(str)); if(jwl->aliases->a == NULL) { DBG("XJAB:xj_wlist_set_aliases: not enough SHMemory..\n"); goto clean1; } p++; for(i=0; ialiases->size; i++) { if((p0 = strchr(p, ';'))==NULL) { DBG("XJAB:xj_wlist_set_aliases: bad parameter value format\n"); goto clean; } n = p0 - p; p1 = strchr(p, '='); if(p1 && p1aliases->d[i] = *(p1+1); n = p1 - p; } jwl->aliases->a[i].len = n; if((jwl->aliases->a[i].s = (char*)_M_SHM_MALLOC(jwl->aliases->a[i].len)) == NULL) { DBG("XJAB:xj_wlist_set_aliases: not enough SHMemory!\n"); goto clean; } strncpy(jwl->aliases->a[i].s, p, jwl->aliases->a[i].len); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_set_aliases: alias[%d/%d]=%.*s delim=%c\n", i+1, jwl->aliases->size, jwl->aliases->a[i].len, jwl->aliases->a[i].s, jwl->aliases->d[i]?jwl->aliases->d[i]:'X'); #endif p = p0 + 1; } return 0; clean: while(i>0) { _M_SHM_FREE(jwl->aliases->a[i-1].s); i--; } _M_SHM_FREE(jwl->aliases->a); clean1: if(jwl->aliases->d) _M_SHM_FREE(jwl->aliases->d); clean2: if(jwl->aliases->proxy) { _M_SHM_FREE(jwl->aliases->proxy->s); _M_SHM_FREE(jwl->aliases->proxy); } clean3: if(jwl->aliases->jdm) { _M_SHM_FREE(jwl->aliases->jdm->s); _M_SHM_FREE(jwl->aliases->jdm); } _M_SHM_FREE(jwl->aliases); jwl->aliases = NULL; return -1; } /** * check if the addr contains jdomain or an alias * - jwl : pointer to the workers list * - addr: the address to check against jdomain and aliases * #returns 0 - if contains or !=0 if not */ int xj_wlist_check_aliases(xj_wlist jwl, str *addr) { char *p, *p0; int ll, i; if(!jwl || !jwl->aliases || !addr || !addr->s || addr->len<=0) return -1; // find '@' p = addr->s; while(p < addr->s + addr->len && *p != '@') p++; if(p >= addr->s + addr->len) return -1; p++; ll = addr->s + addr->len - p; // check parameters p0 = p; while(p0 < p + ll && *p0 != ';') p0++; if(p0 < p + ll) ll = p0 - p; ll = addr->s + addr->len - p; if(jwl->aliases->jdm && jwl->aliases->jdm->len == ll && !strncasecmp(jwl->aliases->jdm->s, p, ll)) return 0; if(jwl->aliases->size <= 0) return 1; for(i = 0; i < jwl->aliases->size; i++) if(jwl->aliases->a[i].len == ll && !strncasecmp(p, jwl->aliases->a[i].s, ll)) return 0; return 1; } /** * delete an entity from working list of a worker * - jwl : pointer to the workers list * - sid : id of the entity (connection to Jabber - usually SHOULD be FROM * header of the incoming SIP message * - _pid : process id of the worker */ void xj_wlist_del(xj_wlist jwl, xj_jkey jkey, int _pid) { int i; void *p; if(jwl==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return; for(i=0; i < jwl->len; i++) if(jwl->workers[i].pid == _pid) break; if(i >= jwl->len) { DBG("XJAB:xj_wlist_del:%d: key <%.*s> not found in [%d]...\n", _pid, jkey->id->len, jkey->id->s, i); return; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_del:%d: trying to delete entry for <%.*s>...\n", _pid, jkey->id->len, jkey->id->s); #endif lock_set_get(jwl->sems, i); p = del234(jwl->workers[i].sip_ids, (void*)jkey); if(p != NULL) { jwl->workers[i].nr--; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_del:%d: sip id <%.*s> deleted\n", _pid, jkey->id->len, jkey->id->s); #endif xj_jkey_free_p(p); } lock_set_release(jwl->sems, i); } kamailio-4.0.4/obsolete/jabber_s/xjab_load.h0000644000000000000000000000312712223032460017457 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _XJAB_LOAD_H_ #define _XJAB_LOAD_H_ #include "../../sr_module.h" #include "../../str.h" #include "xjab_base.h" #define XJ_NO_SCRIPT_F 1 typedef int (*pa_register_watcher_f)(str*, str *, void*, void*); typedef int (*pa_unregister_watcher_f)(str*, str *, void*, void*); struct xjab_binds { pa_register_watcher_f register_watcher; pa_unregister_watcher_f unregister_watcher; }; typedef int(*load_xjab_f)(struct xjab_binds*); int load_xjab(struct xjab_binds*); void xj_register_watcher(str*, str *, void*, void*); void xj_unregister_watcher(str*, str *, void*, void*); #endif kamailio-4.0.4/obsolete/jabber_s/xjab_load.c0000644000000000000000000000304712223032460017453 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "xjab_load.h" #define LOAD_ERROR "ERROR:XJAB:xjab_bind: module function " int load_xjab(struct xjab_binds *xjb) { if(!( xjb->register_watcher=(pa_register_watcher_f) find_export("jab_register_watcher", XJ_NO_SCRIPT_F, 0)) ) { LOG(L_ERR, LOAD_ERROR "'jab_register_watcher' not found!\n"); return -1; } if(!( xjb->unregister_watcher=(pa_unregister_watcher_f) find_export("jab_unregister_watcher", XJ_NO_SCRIPT_F, 0)) ) { LOG(L_ERR, LOAD_ERROR "'jab_unregister_watcher' not found!\n"); return -1; } return 1; } kamailio-4.0.4/obsolete/jabber_s/xjab_worker.c0000644000000000000000000011163412223032460020047 0ustar rootroot/* * $Id$ * * eXtended JABber module - worker implementation * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*** * --- * * History * ------- * 2003-01-20 xj_worker_precess function cleaning - some part of it moved to * xj_worker_check_jcons function, (dcm) * 2003-02-28 send NOTIFYs even the connection is closed by user, (dcm) * 2003-03-11 major locking changes - uses locking.h, (andrei) * 2003-05-07 added new presence status - 'terminated' - when connection * with Jabber server is lost or closed, (dcm) * 2003-05-09 added new presence status - 'refused' - when the presence * subscription request is refused by target, (dcm) * 2003-05-09 new xj_worker_precess function cleaning - some part of it moved * to xj_worker_check_qmsg and xj_worker_check_watcher functions, * (dcm) * 2004-06-07 new DB api => xj_worker_process takes another parameter: dbf * (andrei) */ #include #include #include #include #include #include #include #include #include "../../dprint.h" #include "../../timer.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../cfg/cfg_struct.h" #include "../../modules/tm/tm_load.h" #include "xjab_worker.h" #include "xjab_util.h" #include "xjab_jcon.h" #include "xjab_dmsg.h" #include "xode.h" #include "xjab_presence.h" #include "mdefines.h" #define XJAB_RESOURCE "serXjab" #define XJ_ADDRTR_NUL 0 #define XJ_ADDRTR_S2J 1 #define XJ_ADDRTR_J2S 2 #define XJ_ADDRTR_CON 4 #define XJ_MSG_POOL_SIZE 10 // proxy address #define _PADDR(a) ((a)->aliases->proxy) /** TM bind */ extern struct tm_binds tmb; /** debug info */ int _xj_pid = 0; int main_loop = 1; /** **/ extern char *registrar; static str jab_gw_name = STR_STATIC_INIT("jabber_gateway@127.0.0.1"); /** * address correction * alias A~B: flag == 0 => A->B, otherwise B->A */ int xj_address_translation(str *src, str *dst, xj_jalias als, int flag) { char *p, *p0; int i, ll; if(!src || !dst || !src->s || !dst->s ) return -1; if(!als || !als->jdm || !als->jdm->s || als->jdm->len <= 0) goto done; dst->len = 0; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_address_translation:%d: - checking aliases\n", _xj_pid); #endif p = src->s; while(p<(src->s + src->len) && *p != '@') p++; if(*p != '@') goto done; p++; ll = src->s + src->len - p; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_address_translation:%d: - domain is [%.*s]\n",_xj_pid,ll,p); #endif /*** checking aliases */ if(als->size > 0) { for(i=0; isize; i++) if(als->a[i].len == ll && !strncasecmp(p, als->a[i].s, als->a[i].len)) { if(als->d[i]) { if(flag & XJ_ADDRTR_S2J) { strncpy(dst->s, src->s, src->len); p0 = dst->s; while(p0 < dst->s + (p-src->s)) { if(*p0 == als->dlm) *p0 = als->d[i]; p0++; } return 0; } if(flag & XJ_ADDRTR_J2S) { strncpy(dst->s, src->s, src->len); p0 = dst->s; while(p0 < dst->s + (p-src->s)) { if(*p0 == als->d[i]) *p0 = als->dlm; p0++; } return 0; } } goto done; } } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_address_translation:%d: - doing address correction\n", _xj_pid); #endif if(flag & XJ_ADDRTR_S2J) { if(als->jdm->len != ll || strncasecmp(p, als->jdm->s, als->jdm->len)) { DBG("XJA:xj_address_translation:%d: - wrong Jabber" " destination <%.*s>!\n", _xj_pid, src->len, src->s); return -1; } if(flag & XJ_ADDRTR_CON) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_address_translation:%d: - that is for" " Jabber conference\n", _xj_pid); #endif p0 = p-1; while(p0 > src->s && *p0 != als->dlm) p0--; if(p0 <= src->s) return -1; p0--; while(p0 > src->s && *p0 != als->dlm) p0--; if(*p0 != als->dlm) return -1; dst->len = p - p0 - 2; strncpy(dst->s, p0+1, dst->len); dst->s[dst->len]=0; p = dst->s; while(p < (dst->s + dst->len) && *p!=als->dlm) p++; if(*p==als->dlm) *p = '@'; return 0; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_address_translation:%d: - that is for" " Jabber network\n", _xj_pid); #endif dst->len = p - src->s - 1; strncpy(dst->s, src->s, dst->len); dst->s[dst->len]=0; if((p = strchr(dst->s, als->dlm)) != NULL) *p = '@'; else { DBG("XJA:xj_address_translation:%d: - wrong Jabber" " destination <%.*s>!!!\n", _xj_pid, src->len, src->s); return -1; } return 0; } if(flag & XJ_ADDRTR_J2S) { *(p-1) = als->dlm; p0 = src->s + src->len; while(p0 > p) { if(*p0 == '/') { src->len = p0 - src->s; *p0 = 0; } p0--; } strncpy(dst->s, src->s, src->len); dst->s[src->len] = '@'; dst->s[src->len+1] = 0; strncat(dst->s, als->jdm->s, als->jdm->len); dst->len = strlen(dst->s); return 0; } done: dst->s = src->s; dst->len = src->len; return 0; } /** * worker implementation * - jwl : pointer to the workers list * - jaddress : address of the jabber server * - jport : port of the jabber server * - rank : worker's rank * - db_con : connection to database * dbf: database module callbacks structure * #return : 0 on success or <0 on error */ int xj_worker_process(xj_wlist jwl, char* jaddress, int jport, int rank, db_cmd_t* cmd) { int pipe, ret, i, pos, maxfd, flag; xj_jcon_pool jcp; struct timeval tmv; fd_set set, mset; xj_sipmsg jsmsg; str sto; xj_jcon jbc = NULL; xj_jconf jcf = NULL; char *p, buff[1024], recv_buff[4096]; int flags, nr, ltime = 0; db_res_t* res = NULL; db_rec_t* rec; _xj_pid = getpid(); //signal(SIGTERM, xj_sig_handler); //signal(SIGINT, xj_sig_handler); //signal(SIGQUIT, xj_sig_handler); signal(SIGSEGV, xj_sig_handler); if(registrar) { jab_gw_name.s = registrar; jab_gw_name.len = strlen(registrar); if(registrar[0]== 's' && registrar[1]== 'i' && registrar[2]== 'p' && registrar[3]== ':') { jab_gw_name.s += 4; jab_gw_name.len -= 4; } } if(!jwl || !jwl->aliases || !jwl->aliases->jdm || !jaddress || rank >= jwl->len) { DBG("XJAB:xj_worker[%d]:%d: exiting - wrong parameters\n", rank, _xj_pid); return -1; } pipe = jwl->workers[rank].rpipe; DBG("XJAB:xj_worker[%d]:%d: started - pipe=<%d> : 1st message delay" " <%d>\n", rank, _xj_pid, pipe, jwl->delayt); if((jcp=xj_jcon_pool_init(jwl->maxj,XJ_MSG_POOL_SIZE,jwl->delayt))==NULL) { DBG("XJAB:xj_worker: cannot allocate the pool\n"); return -1; } maxfd = pipe; tmv.tv_sec = jwl->sleept; tmv.tv_usec = 0; FD_ZERO(&set); FD_SET(pipe, &set); while(main_loop) { mset = set; tmv.tv_sec = (jcp->jmqueue.size == 0)?jwl->sleept:1; #ifdef XJ_EXTRA_DEBUG //DBG("XJAB:xj_worker[%d]:%d: select waiting %ds - queue=%d\n",rank, // _xj_pid, (int)tmv.tv_sec, jcp->jmqueue.size); #endif tmv.tv_usec = 0; ret = select(maxfd+1, &mset, NULL, NULL, &tmv); /* update the local config */ cfg_update(); // check the msg queue xj_worker_check_qmsg(jwl, jcp); if(ret <= 0) goto step_x; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: something is coming\n", _xj_pid); #endif if(!FD_ISSET(pipe, &mset)) goto step_y; if(read(pipe, &jsmsg, sizeof(jsmsg)) < sizeof(jsmsg)) { DBG("XJAB:xj_worker:%d: BROKEN PIPE - exiting\n", _xj_pid); break; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: job <%p> from SER\n", _xj_pid, jsmsg); #endif if(jsmsg == NULL || jsmsg->jkey==NULL || jsmsg->jkey->id==NULL) goto step_w; strncpy(buff, jsmsg->jkey->id->s, jsmsg->jkey->id->len); buff[jsmsg->jkey->id->len] = 0; jbc = xj_jcon_pool_get(jcp, jsmsg->jkey); switch(jsmsg->type) { case XJ_SEND_MESSAGE: if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm) && (!jbc||!xj_jcon_get_jconf(jbc,&jsmsg->to,jwl->aliases->dlm))) { xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOTJCONF, NULL); goto step_w; } break; case XJ_REG_WATCHER: case XJ_JOIN_JCONF: case XJ_GO_ONLINE: break; case XJ_EXIT_JCONF: if(jbc == NULL) goto step_w; // close the conference session here if(jbc->nrjconf <= 0) goto step_w; if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm)) xj_jcon_del_jconf(jbc, &jsmsg->to, jwl->aliases->dlm, XJ_JCMD_UNSUBSCRIBE); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_INF_JCONFEXIT, NULL); goto step_w; case XJ_GO_OFFLINE: if(jbc != NULL) jbc->expire = ltime = -1; goto step_w; case XJ_DEL_WATCHER: default: goto step_w; } if(jbc != NULL) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: connection already exists" " for <%s> ...\n", _xj_pid, buff); #endif xj_jcon_update(jbc, jwl->cachet); goto step_z; } // NO OPEN CONNECTION FOR THIS SIP ID #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: new connection for <%s>.\n", _xj_pid, buff); #endif cmd->match[0].v.cstr = buff; cmd->match[1].v.int4 = 0; if (db_exec(&res, cmd) < 0 || !res || !(rec = db_first(res))) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: no database result when looking" " for associated Jabber account\n", _xj_pid); #endif xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_JGWFORB, NULL); goto step_v; } jbc = xj_jcon_init(jaddress, jport); if(xj_jcon_connect(jbc)) { DBG("XJAB:xj_worker:%d: Cannot connect" " to the Jabber server ...\n", _xj_pid); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOJSRV, NULL); goto step_v; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker: auth to jabber as: [%s] / [xxx]\n", rec->fld[0].v.cstr); // (char*)(ROW_VALUES(RES_ROWS(res))[1].val.string_val)); #endif if(xj_jcon_user_auth(jbc, rec->fld[0].v.cstr, rec->fld[1].v.cstr, XJAB_RESOURCE) < 0) { DBG("XJAB:xj_worker:%d: Authentication to the Jabber server" " failed ...\n", _xj_pid); xj_jcon_disconnect(jbc); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_JAUTH, NULL); xj_jcon_free(jbc); goto step_v; } if(xj_jcon_set_attrs(jbc, jsmsg->jkey, jwl->cachet, jwl->delayt) || xj_jcon_pool_add(jcp, jbc)) { DBG("XJAB:xj_worker:%d: Keeping connection to Jabber server" " failed! Not enough memory ...\n", _xj_pid); xj_jcon_disconnect(jbc); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_JGWFULL, NULL); xj_jcon_free(jbc); goto step_v; } /** add socket descriptor to select */ #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: add connection on <%d> \n", _xj_pid, jbc->sock); #endif if(jbc->sock > maxfd) maxfd = jbc->sock; FD_SET(jbc->sock, &set); xj_jcon_get_roster(jbc); xj_jcon_send_presence(jbc, NULL, NULL, "Online", "9"); /** wait for a while - the worker is tired */ //sleep(3); if (res) { db_res_free(res); res = NULL; } step_z: if(jsmsg->type == XJ_GO_ONLINE) goto step_w; if(jsmsg->type == XJ_REG_WATCHER) { // update or register a presence watcher xj_worker_check_watcher(jwl, jcp, jbc, jsmsg); goto step_w; } flag = 0; if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm)) { if((jcf = xj_jcon_get_jconf(jbc, &jsmsg->to, jwl->aliases->dlm)) != NULL) { if((jsmsg->type == XJ_JOIN_JCONF) && !(jcf->status & XJ_JCONF_READY || jcf->status & XJ_JCONF_WAITING)) { if(!xj_jcon_jconf_presence(jbc,jcf,NULL,"online")) jcf->status = XJ_JCONF_WAITING; else { // unable to join the conference // --- send back to SIP user a msg xj_send_sip_msgz(_PADDR(jwl),jsmsg->jkey->id,&jsmsg->to, XJ_DMSG_ERR_JOINJCONF, &jbc->jkey->flag); goto step_w; } } flag |= XJ_ADDRTR_CON; } else { // unable to get the conference // --- send back to SIP user a msg xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NEWJCONF, &jbc->jkey->flag); goto step_w; } } if(jsmsg->type != XJ_SEND_MESSAGE) goto step_w; // here will come only XJ_SEND_MESSAGE switch(xj_jcon_is_ready(jbc,jsmsg->to.s,jsmsg->to.len,jwl->aliases->dlm)) { case 0: #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: SENDING the message to Jabber" " network ...\n", _xj_pid); #endif /*** address correction ***/ sto.s = buff; sto.len = 0; flag |= XJ_ADDRTR_S2J; if(xj_address_translation(&jsmsg->to, &sto, jwl->aliases, flag) == 0) { if(xj_jcon_send_msg(jbc, sto.s, sto.len, jsmsg->msg.s, jsmsg->msg.len, (flag&XJ_ADDRTR_CON)?XJ_JMSG_GROUPCHAT:XJ_JMSG_CHAT)<0) xj_send_sip_msgz(_PADDR(jwl),jsmsg->jkey->id,&jsmsg->to, XJ_DMSG_ERR_SENDJMSG, &jbc->jkey->flag); } else DBG("XJAB:xj_worker:%d: ERROR SENDING as Jabber" " message ...\n", _xj_pid); goto step_w; case 1: #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d:SCHEDULING the message.\n", _xj_pid); #endif if(xj_jcon_pool_add_jmsg(jcp, jsmsg, jbc) < 0) { DBG("XJAB:xj_worker:%d: SCHEDULING the message FAILED." " Message was dropped.\n",_xj_pid); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_STOREJMSG, &jbc->jkey->flag); goto step_w; } else // skip freeing the SIP message - now is in queue goto step_y; case 2: xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOREGIM, &jbc->jkey->flag); goto step_w; case 3: // not joined to Jabber conference xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOTJCONF, &jbc->jkey->flag); goto step_w; default: xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_SENDJMSG, &jbc->jkey->flag); goto step_w; } step_v: // error connecting to Jabber server // cleaning jab_wlist xj_wlist_del(jwl, jsmsg->jkey, _xj_pid); // cleaning db_query if (res) db_res_free(res); res = NULL; step_w: if(jsmsg!=NULL) { xj_sipmsg_free(jsmsg); jsmsg = NULL; } step_y: // check for new message from ... JABBER for(i = 0; i < jcp->len && main_loop; i++) { if(jcp->ojc[i] == NULL) continue; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: checking socket <%d>" " ...\n", _xj_pid, jcp->ojc[i]->sock); #endif if(!FD_ISSET(jcp->ojc[i]->sock, &mset)) continue; pos = nr = 0; do { p = recv_buff; if(pos != 0) { while(pos < nr) { *p = recv_buff[pos]; pos++; p++; } *p = 0; /** * flush out the socket - set it to nonblocking */ flags = fcntl(jcp->ojc[i]->sock, F_GETFL, 0); if(flags!=-1 && !(flags & O_NONBLOCK)) { /* set NONBLOCK bit to enable non-blocking */ fcntl(jcp->ojc[i]->sock, F_SETFL, flags|O_NONBLOCK); } } if((nr = read(jcp->ojc[i]->sock, p, sizeof(recv_buff)-(p-recv_buff))) == 0 ||(nr < 0 && errno != EAGAIN)) { DBG("XJAB:xj_worker:%d: ERROR -" " connection to jabber lost on socket <%d> ...\n", _xj_pid, jcp->ojc[i]->sock); xj_send_sip_msgz(_PADDR(jwl), jcp->ojc[i]->jkey->id, &jab_gw_name,XJ_DMSG_ERR_DISCONNECTED,&jbc->jkey->flag); // make sure that will ckeck expired connections ltime = jcp->ojc[i]->expire = -1; FD_CLR(jcp->ojc[i]->sock, &set); goto step_xx; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: received: %dbytes Err:%d/EA:%d\n", _xj_pid, nr, errno, EAGAIN); #endif xj_jcon_update(jcp->ojc[i], jwl->cachet); if(nr>0) p[nr] = 0; nr = strlen(recv_buff); pos = 0; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker: JMSG START ----------\n%.*s\n" " JABBER: JMSGL:%d END ----------\n", nr, recv_buff, nr); #endif } while(xj_manage_jab(recv_buff, nr, &pos, jwl->aliases, jcp->ojc[i]) == 9 && main_loop); /** * flush out the socket - set it back to blocking */ flags = fcntl(jcp->ojc[i]->sock, F_GETFL, 0); if(flags!=-1 && (flags & O_NONBLOCK)) { /* reset NONBLOCK bit to enable blocking */ fcntl(jcp->ojc[i]->sock, F_SETFL, flags & ~O_NONBLOCK); } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: msgs from socket <%d> parsed" " ...\n", _xj_pid, jcp->ojc[i]->sock); #endif } // end FOR(i = 0; i < jcp->len; i++) step_x: if(ret < 0) { DBG("XJAB:xj_worker:%d: SIGNAL received!!!!!!!!\n", _xj_pid); maxfd = pipe; FD_ZERO(&set); FD_SET(pipe, &set); for(i = 0; i < jcp->len; i++) { if(jcp->ojc[i] != NULL) { FD_SET(jcp->ojc[i]->sock, &set); if( jcp->ojc[i]->sock > maxfd ) maxfd = jcp->ojc[i]->sock; } } } step_xx: if(ltime < 0 || ltime + jwl->sleept <= get_ticks()) { ltime = get_ticks(); #ifdef XJ_EXTRA_DEBUG //DBG("XJAB:xj_worker:%d: scanning for expired connection\n", // _xj_pid); #endif xj_worker_check_jcons(jwl, jcp, ltime, &set); } } // END while DBG("XJAB:xj_worker:%d: cleaning procedure\n", _xj_pid); return 0; } // end xj_worker_process /** * parse incoming message from Jabber server */ int xj_manage_jab(char *buf, int len, int *pos, xj_jalias als, xj_jcon jbc) { int j, err=0; char *p, *to, *from, *msg, *type, *emsg, *ecode, lbuf[4096], fbuf[128]; xj_jconf jcf = NULL; str ts, tf; xode x, y, z; str *sid; xj_pres_cell prc = NULL; if(!jbc) return -1; sid = jbc->jkey->id; x = xode_from_strx(buf, len, &err, &j); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_parse_jab: XODE ret:%d pos:%d\n", err, j); #endif if(err && pos != NULL) *pos= j; if(x == NULL) return -1; lbuf[0] = 0; ecode = NULL; /******************** XMPP 'MESSAGE' HANDLING **********************/ if(!strncasecmp(xode_get_name(x), "message", 7)) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: jabber [message] received\n"); #endif if((to = xode_get_attrib(x, "to")) == NULL) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: missing 'to' attribute\n"); #endif err = -1; goto ready; } if((from = xode_get_attrib(x, "from")) == NULL) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: missing 'from' attribute\n"); #endif err = -1; goto ready; } if((y = xode_get_tag(x, "body")) == NULL || (msg = xode_get_data(y)) == NULL) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: missing 'body' of message\n"); #endif err = -1; goto ready; } type = xode_get_attrib(x, "type"); if(type != NULL && !strncasecmp(type, "error", 5)) { if((y = xode_get_tag(x, "error")) == NULL || (emsg = xode_get_data(y)) == NULL) strcpy(lbuf, "{Error sending following message} - "); else { ecode = xode_get_attrib(y, "code"); strcpy(lbuf, "{Error ("); if(ecode != NULL) { strcat(lbuf, ecode); strcat(lbuf, " - "); } strcat(lbuf, emsg); strcat(lbuf, ") when trying to send following message}"); } } // is from a conference?!?! if((jcf=xj_jcon_check_jconf(jbc, from))!=NULL) { if(lbuf[0] == 0) { p = from + strlen(from); while(p>from && *p != '/') p--; if(*p == '/') { if(jcf->nick.len>0 && strlen(p+1) == jcf->nick.len && !strncasecmp(p+1, jcf->nick.s, jcf->nick.len)) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: message sent by myself\n"); #endif goto ready; } lbuf[0] = '['; lbuf[1] = 0; strcat(lbuf, p+1); strcat(lbuf, "] "); } } else { jcf->status = XJ_JCONF_NULL; xj_jcon_jconf_presence(jbc,jcf,NULL,"online"); } strcat(lbuf, msg); ts.s = lbuf; ts.len = strlen(lbuf); if(xj_send_sip_msg(als->proxy, sid, &jcf->uri, &ts, &jbc->jkey->flag)<0) DBG("XJAB:xj_manage_jab: ERROR SIP MESSAGE was not sent!\n"); #ifdef XJ_EXTRA_DEBUG else DBG("XJAB:xj_manage_jab: SIP MESSAGE was sent!\n"); #endif goto ready; } strcat(lbuf, msg); ts.s = from; ts.len = strlen(from); tf.s = fbuf; tf.len = 0; if(xj_address_translation(&ts, &tf, als, XJ_ADDRTR_J2S) == 0) { ts.s = lbuf; ts.len = strlen(lbuf); if(xj_send_sip_msg(als->proxy, sid, &tf, &ts, &jbc->jkey->flag)<0) DBG("XJAB:xj_manage_jab: ERROR SIP MESSAGE was not sent ...\n"); #ifdef XJ_EXTRA_DEBUG else DBG("XJAB:xj_manage_jab: SIP MESSAGE was sent.\n"); #endif } goto ready; } /*------------------- END 'MESSAGE' HANDLING ----------------------*/ /******************** XMPP 'PRESENCE' HANDLING *********************/ if(!strncasecmp(xode_get_name(x), "presence", 8)) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: jabber [presence] received\n"); #endif type = xode_get_attrib(x, "type"); from = xode_get_attrib(x, "from"); if(from == NULL) goto ready; ts.s = from; p = from; while(pready |= XJ_NET_AIM; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: AIM network ready\n"); #endif } else if(!strncasecmp(from, XJ_ICQ_NAME, XJ_ICQ_LEN)) { jbc->ready |= XJ_NET_ICQ; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: ICQ network ready\n"); #endif } else if(!strncasecmp(from, XJ_MSN_NAME, XJ_MSN_LEN)) { jbc->ready |= XJ_NET_MSN; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: MSN network ready\n"); #endif } else if(!strncasecmp(from, XJ_YAH_NAME, XJ_YAH_LEN)) { jbc->ready |= XJ_NET_YAH; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: YAHOO network ready\n"); #endif } } else if((jcf=xj_jcon_check_jconf(jbc, from))!=NULL) { jcf->status = XJ_JCONF_READY; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: %s conference ready\n", from); #endif } else { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: user <%.*s> is online\n",ts.len,ts.s); #endif prc = xj_pres_list_check(jbc->plist, &ts); if(prc) { if(prc->state != XJ_PS_ONLINE) { prc->state = XJ_PS_ONLINE; goto call_pa_cbf; } } else { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: user state received - creating" " presence cell for [%.*s]\n", ts.len, ts.s); #endif prc = xj_pres_cell_new(); if(prc == NULL) { DBG("XJAB:xj_manage_jab: cannot create presence" " cell for [%s]\n", from); goto ready; } if(xj_pres_cell_init(prc, &ts, NULL, NULL)<0) { DBG("XJAB:xj_manage_jab: cannot init presence" " cell for [%s]\n", from); xj_pres_cell_free(prc); goto ready; } prc = xj_pres_list_add(jbc->plist, prc); if(prc) { prc->state = XJ_PS_ONLINE; goto call_pa_cbf; } } } goto ready; } if(strchr(from, '@') == NULL) goto ready; if(!strncasecmp(type, "error", 5)) { if((jcf=xj_jcon_check_jconf(jbc, from))!=NULL) { tf.s = from; tf.len = strlen(from); if((y = xode_get_tag(x, "error")) == NULL) goto ready; if ((p = xode_get_attrib(y, "code")) != NULL && atoi(p) == 409) { xj_send_sip_msgz(als->proxy, sid, &tf, XJ_DMSG_ERR_JCONFNICK, &jbc->jkey->flag); goto ready; } xj_send_sip_msgz(als->proxy,sid,&tf,XJ_DMSG_ERR_JCONFREFUSED, &jbc->jkey->flag); } goto ready; } if(type!=NULL && !strncasecmp(type, "subscribe", 9)) { xj_jcon_send_presence(jbc, from, "subscribed", NULL, NULL); goto ready; } prc = xj_pres_list_check(jbc->plist, &ts); if(!prc) goto ready; if(!strncasecmp(type, "unavailable", 11)) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: user <%s> is offline\n", from); #endif if(prc->state != XJ_PS_OFFLINE) { prc->state = XJ_PS_OFFLINE; goto call_pa_cbf; } goto ready; } if(!strncasecmp(type, "unsubscribed", 12)) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: user <%s> does not allow to see his" " presence status\n", from); #endif if(prc->state != XJ_PS_REFUSED) { prc->state = XJ_PS_REFUSED; goto call_pa_cbf; } } // ignoring unknown types goto ready; } /*------------------- END XMPP 'PRESENCE' HANDLING ----------------*/ /******************** XMPP 'IQ' HANDLING ***************************/ if(!strncasecmp(xode_get_name(x), "iq", 2)) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: jabber [iq] received\n"); #endif if(!strncasecmp(xode_get_attrib(x, "type"), "result", 6)) { if((y = xode_get_tag(x, "query?xmlns=jabber:iq:roster")) == NULL) goto ready; z = xode_get_firstchild(y); while(z) { if(!strncasecmp(xode_get_name(z), "item", 5) && (from = xode_get_attrib(z, "jid")) != NULL) { if(strchr(from, '@') == NULL) { // transports if(!strncasecmp(from, XJ_AIM_NAME, XJ_AIM_LEN)) { jbc->allowed |= XJ_NET_AIM; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab:AIM network available\n"); #endif } else if(!strncasecmp(from, XJ_ICQ_NAME, XJ_ICQ_LEN)) { jbc->allowed |= XJ_NET_ICQ; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab:ICQ network available\n"); #endif } else if(!strncasecmp(from, XJ_MSN_NAME, XJ_MSN_LEN)) { jbc->allowed |= XJ_NET_MSN; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab:MSN network available\n"); #endif } else if(!strncasecmp(from, XJ_YAH_NAME, XJ_YAH_LEN)) { jbc->allowed |= XJ_NET_YAH; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab:YAHOO network available\n"); #endif } goto next_sibling; } } next_sibling: z = xode_get_nextsibling(z); } } goto ready; } /*------------------- END XMPP 'IQ' HANDLING ----------------------*/ call_pa_cbf: if(prc && prc->cbf) { // call the PA callback function tf.s = fbuf; tf.len = 0; if(xj_address_translation(&ts,&tf,als,XJ_ADDRTR_J2S)==0) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_manage_jab: calling CBF(%.*s,%d)\n", tf.len, tf.s, prc->state); #endif (*(prc->cbf))(&tf, &tf, prc->state, prc->cbp); } } ready: xode_free(x); return err; } /** * */ void xj_sig_handler(int s) { //signal(SIGTERM, xj_sig_handler); //signal(SIGINT, xj_sig_handler); //signal(SIGQUIT, xj_sig_handler); signal(SIGSEGV, xj_sig_handler); main_loop = 0; DBG("XJAB:xj_worker:%d: SIGNAL received=%d\n **************", _xj_pid, s); } /***************************** ****************************************/ /** * send a SIP MESSAGE message * - to : destination * - from : origin * - contact : contact header * - msg : body of the message * #return : 0 on success or <0 on error */ int xj_send_sip_msg(str *proxy, str *to, str *from, str *msg, int *cbp) { str msg_type = STR_STATIC_INIT("MESSAGE"); char buf[512]; str tfrom; str str_hdr; char buf1[1024]; uac_req_t uac_r; if( !to || !to->s || to->len <= 0 || !from || !from->s || from->len <= 0 || !msg || !msg->s || msg->len <= 0 || (cbp && *cbp!=0) ) return -1; // from correction tfrom.len = 0; strncpy(buf+tfrom.len, "s, from->len); tfrom.len += from->len; buf[tfrom.len++] = '>'; tfrom.s = buf; // building Contact and Content-Type strcpy(buf1,"Content-Type: text/plain"CRLF"Contact: "); str_hdr.len = 24 + CRLF_LEN + 9; strncat(buf1,tfrom.s,tfrom.len); str_hdr.len += tfrom.len; strcat(buf1, CRLF); str_hdr.len += CRLF_LEN; str_hdr.s = buf1; if(cbp) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_send_sip_msg: uac callback parameter [%p==%d]\n", cbp, *cbp); #endif set_uac_req(&uac_r, &msg_type, &str_hdr, msg, 0, TMCB_LOCAL_COMPLETED, xj_tuac_callback, (void*)cbp ); } else { set_uac_req(&uac_r, &msg_type, &str_hdr, msg, 0, 0, 0, 0 ); } return tmb.t_request(&uac_r, 0, to, &tfrom, 0); } /** * send a SIP MESSAGE message * - to : destination * - from : origin * - contact : contact header * - msg : body of the message, string terminated by zero * #return : 0 on success or <0 on error */ int xj_send_sip_msgz(str *proxy, str *to, str *from, char *msg, int *cbp) { str tstr; int n; if(!to || !from || !msg || (cbp && *cbp!=0)) return -1; tstr.s = msg; tstr.len = strlen(msg); if((n = xj_send_sip_msg(proxy, to, from, &tstr, cbp)) < 0) DBG("XJAB: jab_send_sip_msgz: ERROR SIP MESSAGE wasn't sent to" " [%.*s]...\n", to->len, to->s); #ifdef XJ_EXTRA_DEBUG else DBG("XJAB: jab_send_sip_msgz: SIP MESSAGE was sent to [%.*s]...\n", to->len, to->s); #endif return n; } /** * send disconnected info to all SIP users associated with worker idx * and clean the entries from wlist */ int xj_wlist_clean_jobs(xj_wlist jwl, int idx, int fl) { xj_jkey p; if(jwl==NULL || idx < 0 || idx >= jwl->len || !jwl->workers[idx].sip_ids) return -1; lock_set_get(jwl->sems, idx); while((p=(xj_jkey)delpos234(jwl->workers[idx].sip_ids, 0))!=NULL) { if(fl) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_wlist_send_info: sending disconnect message" " to <%.*s>\n", p->id->len, p->id->s); #endif xj_send_sip_msgz(_PADDR(jwl), p->id, &jab_gw_name, XJ_DMSG_INF_DISCONNECTED, NULL); } jwl->workers[idx].nr--; xj_jkey_free_p(p); } lock_set_release(jwl->sems, idx); return 0; } /** * callback function for TM */ void xj_tuac_callback( struct cell *t, int type, struct tmcb_params *ps) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_tuac_callback: completed with status %d\n", ps->code); #endif if(!ps->param) { DBG("XJAB: m_tuac_callback: parameter not received\n"); return; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_tuac_callback: parameter [%p : ex-value=%d]\n", ps->param, *((int*)ps->param) ); #endif if(ps->code < 200 || ps->code >= 300) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB: xj_tuac_callback: no 2XX return code - connection set" " as expired \n"); #endif *((int*)ps->param) = XJ_FLAG_CLOSE; } } /** * check for expired connections */ void xj_worker_check_jcons(xj_wlist jwl, xj_jcon_pool jcp, int ltime, fd_set *pset) { int i; xj_jconf jcf; for(i = 0; i < jcp->len && main_loop; i++) { if(jcp->ojc[i] == NULL) continue; if(jcp->ojc[i]->jkey->flag==XJ_FLAG_OPEN && jcp->ojc[i]->expire > ltime) continue; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: connection expired for <%.*s> \n", _xj_pid, jcp->ojc[i]->jkey->id->len, jcp->ojc[i]->jkey->id->s); #endif xj_send_sip_msgz(_PADDR(jwl), jcp->ojc[i]->jkey->id, &jab_gw_name, XJ_DMSG_INF_JOFFLINE, NULL); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: connection's close flag =%d\n", _xj_pid, jcp->ojc[i]->jkey->flag); #endif // CLEAN JAB_WLIST xj_wlist_del(jwl, jcp->ojc[i]->jkey, _xj_pid); // looking for open conference rooms #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: having %d open conferences\n", _xj_pid, jcp->ojc[i]->nrjconf); #endif while(jcp->ojc[i]->nrjconf > 0) { if((jcf=delpos234(jcp->ojc[i]->jconf,0))!=NULL) { // get out of room xj_jcon_jconf_presence(jcp->ojc[i],jcf, "unavailable", NULL); xj_jconf_free(jcf); } jcp->ojc[i]->nrjconf--; } // send offline presence to all subscribers if(jcp->ojc[i]->plist) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker:%d: sending 'terminated' status to SIP" " subscriber\n", _xj_pid); #endif xj_pres_list_notifyall(jcp->ojc[i]->plist, XJ_PS_TERMINATED); } FD_CLR(jcp->ojc[i]->sock, pset); xj_jcon_disconnect(jcp->ojc[i]); xj_jcon_free(jcp->ojc[i]); jcp->ojc[i] = NULL; } } /** * check if there are msg to send or delete from queue */ void xj_worker_check_qmsg(xj_wlist jwl, xj_jcon_pool jcp) { int i, flag; str sto; char buff[1024]; if(!jwl || !jcp) return; /** check the msg queue AND if the target connection is ready */ for(i = 0; ijmqueue.size && main_loop; i++) { if(jcp->jmqueue.jsm[i]==NULL || jcp->jmqueue.ojc[i]==NULL) { if(jcp->jmqueue.jsm[i]!=NULL) { xj_sipmsg_free(jcp->jmqueue.jsm[i]); jcp->jmqueue.jsm[i] = NULL; xj_jcon_pool_del_jmsg(jcp, i); } if(jcp->jmqueue.ojc[i]!=NULL) xj_jcon_pool_del_jmsg(jcp, i); continue; } if(jcp->jmqueue.expire[i] < get_ticks()) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker_check_qmsg:%d: message to %.*s is expired\n", _xj_pid, jcp->jmqueue.jsm[i]->to.len, jcp->jmqueue.jsm[i]->to.s); #endif xj_send_sip_msgz(_PADDR(jwl), jcp->jmqueue.jsm[i]->jkey->id, &jcp->jmqueue.jsm[i]->to, XJ_DMSG_ERR_SENDIM, &jcp->jmqueue.ojc[i]->jkey->flag); if(jcp->jmqueue.jsm[i]!=NULL) { xj_sipmsg_free(jcp->jmqueue.jsm[i]); jcp->jmqueue.jsm[i] = NULL; } /** delete message from queue */ xj_jcon_pool_del_jmsg(jcp, i); continue; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker_check_qmsg:%d:%d: QUEUE: message[%d] from [%.*s]" "/to [%.*s]/body[%.*s] expires at %d\n", _xj_pid, get_ticks(), i, jcp->jmqueue.jsm[i]->jkey->id->len, jcp->jmqueue.jsm[i]->jkey->id->s, jcp->jmqueue.jsm[i]->to.len,jcp->jmqueue.jsm[i]->to.s, jcp->jmqueue.jsm[i]->msg.len,jcp->jmqueue.jsm[i]->msg.s, jcp->jmqueue.expire[i]); #endif if(xj_jcon_is_ready(jcp->jmqueue.ojc[i], jcp->jmqueue.jsm[i]->to.s, jcp->jmqueue.jsm[i]->to.len, jwl->aliases->dlm)) continue; /*** address correction ***/ flag = XJ_ADDRTR_S2J; if(!xj_jconf_check_addr(&jcp->jmqueue.jsm[i]->to,jwl->aliases->dlm)) flag |= XJ_ADDRTR_CON; sto.s = buff; sto.len = 0; if(xj_address_translation(&jcp->jmqueue.jsm[i]->to, &sto, jwl->aliases, flag) == 0) { /** send message from queue */ #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker_check_qmsg:%d: SENDING the message from" " local queue to Jabber network ...\n", _xj_pid); #endif xj_jcon_send_msg(jcp->jmqueue.ojc[i], sto.s, sto.len, jcp->jmqueue.jsm[i]->msg.s, jcp->jmqueue.jsm[i]->msg.len, (flag&XJ_ADDRTR_CON)?XJ_JMSG_GROUPCHAT:XJ_JMSG_CHAT); } else DBG("XJAB:xj_worker_check_qmsg:%d: ERROR SENDING the message from" " local queue to Jabber network ...\n", _xj_pid); if(jcp->jmqueue.jsm[i]!=NULL) { xj_sipmsg_free(jcp->jmqueue.jsm[i]); jcp->jmqueue.jsm[i] = NULL; } /** delete message from queue */ xj_jcon_pool_del_jmsg(jcp, i); } } /** * update or register a presence watcher */ void xj_worker_check_watcher(xj_wlist jwl, xj_jcon_pool jcp, xj_jcon jbc, xj_sipmsg jsmsg) { str sto; char buff[1024]; xj_pres_cell prc = NULL; if(!jwl || !jcp || !jbc || !jsmsg) return; if(!jsmsg->cbf) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker_check_watcher:%d: NULL PA callback" " function\n", _xj_pid); #endif return; } if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm)) { // is for a conference - ignore?!?! #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker_check_watcher:%d: presence request for a" " conference.\n", _xj_pid); #endif // set as offline (*(jsmsg->cbf))(&jsmsg->to, &jsmsg->to, XJ_PS_OFFLINE, jsmsg->p); return; } sto.s = buff; sto.len = 0; if(xj_address_translation(&jsmsg->to, &sto, jwl->aliases, XJ_ADDRTR_S2J) == 0) { prc = xj_pres_list_check(jbc->plist, &sto); if(!prc) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker_check_watcher:%d: NEW presence" " cell for %.*s.\n", _xj_pid, sto.len, sto.s); #endif prc = xj_pres_cell_new(); if(!prc) { DBG("XJAB:xj_worker_check_watcher:%d: cannot create a presence" " cell for %.*s.\n", _xj_pid, sto.len, sto.s); return; } if(xj_pres_cell_init(prc, &sto, jsmsg->cbf, jsmsg->p)<0) { DBG("XJAB:xj_worker_check_watcher:%d: cannot init the presence" " cell for %.*s.\n", _xj_pid, sto.len, sto.s); xj_pres_cell_free(prc); return; } if((prc = xj_pres_list_add(jbc->plist, prc))==NULL) { DBG("XJAB:xj_worker_check_watcher:%d: cannot add the presence" " cell for %.*s.\n", _xj_pid, sto.len, sto.s); return; } sto.s[sto.len] = 0; if(!xj_jcon_send_subscribe(jbc, sto.s, NULL, "subscribe")) prc->status = XJ_PRES_STATUS_WAIT; } else { xj_pres_cell_update(prc, jsmsg->cbf, jsmsg->p); #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_worker_check_watcher:%d: calling CBF(%.*s,%d)\n", _xj_pid, jsmsg->to.len, jsmsg->to.s, prc->state); #endif // send presence info to SIP subscriber (*(prc->cbf))(&jsmsg->to, &jsmsg->to, prc->state, prc->cbp); } } } /***************************** ****************************************/ kamailio-4.0.4/obsolete/jabber_s/xjab_presence.c0000644000000000000000000001223412223032460020336 0ustar rootroot/* * $Id$ * * XJAB module * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "xjab_presence.h" /** * create a presence cell */ xj_pres_cell xj_pres_cell_new() { xj_pres_cell prc = NULL; prc = (xj_pres_cell)pkg_malloc(sizeof(t_xj_pres_cell)); if(prc == NULL) return NULL; prc->key = 0; prc->userid.s = NULL; prc->userid.len = 0; prc->state = XJ_PS_OFFLINE; prc->status = XJ_PRES_STATUS_NULL; prc->cbf = NULL; prc->cbp = NULL; prc->prev = NULL; prc->next = NULL; return prc; } /** * free the presence cell */ void xj_pres_cell_free(xj_pres_cell prc) { if(!prc) return; if(prc->userid.s) pkg_free(prc->userid.s); pkg_free(prc); prc = NULL; } /** * free all presence cell linked to */ void xj_pres_cell_free_all(xj_pres_cell prc) { xj_pres_cell p, p0; if(!prc) return; p = prc; while(p) { p0 = p->next; xj_pres_cell_free(p); p = p0; } } /** * init a presence cell */ int xj_pres_cell_init(xj_pres_cell prc, str* uid, pa_callback_f f, void* p) { if(!prc || !uid || !uid->s || uid->len<=0) return -1; prc->userid.s = (char*)pkg_malloc(uid->len*sizeof(char)); if(prc->userid.s == NULL) return -1; strncpy(prc->userid.s, uid->s, uid->len); prc->userid.len = uid->len; prc->key = xj_get_hash(uid, NULL); prc->cbf = f; prc->cbp = p; return 0; } /** * update attributes for a presence cell */ int xj_pres_cell_update(xj_pres_cell prc, pa_callback_f f, void *p) { if(!prc) return -1; prc->cbf = f; prc->cbp = p; return 0; } /** * init a presence list */ xj_pres_list xj_pres_list_init() { xj_pres_list prl = NULL; prl = (xj_pres_list)pkg_malloc(sizeof(t_xj_pres_list)); if(!prl) return NULL; prl->nr = 0; prl->clist = NULL; return prl; } /** * free the presence list */ void xj_pres_list_free(xj_pres_list prl) { if(!prl) return; xj_pres_cell_free_all(prl->clist); pkg_free(prl); prl = NULL; } /** * add, if does not exist, an user in present list */ xj_pres_cell xj_pres_list_add(xj_pres_list prl, xj_pres_cell prc) { xj_pres_cell p, p0; if(!prc) return NULL; if(!prl) { xj_pres_cell_free(prc); return NULL; } // presence list empty if(prl->clist==NULL) { prl->nr++; prl->clist = prc; return prc; } p0 = p = prl->clist; while(p && p->key <= prc->key) { if(p->key == prc->key && p->userid.len == prc->userid.len && !strncasecmp(p->userid.s, prc->userid.s, p->userid.len)) { // cell already exist // update cbf and cbp p->cbf = prc->cbf; p->cbp = prc->cbp; xj_pres_cell_free(prc); return p; } p0 = p; p = p->next; } // add a the cell in list prc->next = p0->next; prc->prev = p0; if(p0->next) p0->next->prev = prc; p0->next = prc; prl->nr++; return prc; } /** * delete a user from presence list */ int xj_pres_list_del(xj_pres_list prl, str *uid) { xj_pres_cell p; int lkey; if(!prl || !uid || !uid->s || uid->len<=0) return -1; if(prl->nr<=0 || prl->clist==NULL) return 0; lkey = xj_get_hash(uid, NULL); p = prl->clist; while(p && p->key <= lkey) { if(p->key == lkey && p->userid.len == uid->len && !strncasecmp(p->userid.s, uid->s, uid->len)) { prl->nr--; if(p->next) p->next->prev = p->prev; if(p->prev == NULL) prl->clist = p->next; else p->prev->next = p->next; xj_pres_cell_free(p); return 0; } p = p->next; } return 0; } /** * Check if a user is already in presence list */ xj_pres_cell xj_pres_list_check(xj_pres_list prl, str* uid) { xj_pres_cell p; int lkey; if(!prl || !uid || !uid->s || uid->len<=0 || prl->nr<=0 || prl->clist==NULL) return NULL; lkey = xj_get_hash(uid, NULL); p = prl->clist; while(p && p->key <= lkey) { if(p->key == lkey && p->userid.len == uid->len && !strncasecmp(p->userid.s, uid->s, uid->len)) return p; p = p->next; } return NULL; } /** * Notify all users from list */ void xj_pres_list_notifyall(xj_pres_list prl, int s) { xj_pres_cell p; if(!prl || prl->nr<=0 || prl->clist==NULL) return; p = prl->clist; while(p) { if(p->cbf) (*(p->cbf))(&(p->userid),&(p->userid), (s==XJ_PS_CHECK)?p->state:s, p->cbp); p = p->next; } } kamailio-4.0.4/obsolete/jabber_s/jabber.c0000644000000000000000000006201412223032460016754 0ustar rootroot/* * $Id$ * * XJAB module * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * --- * * History * ------- * 2003-02-28 connection management with ihttp implemented (dcm) * 2003-02-24 first version of callback functions for ihttp (dcm) * 2003-02-13 lot of comments enclosed in #ifdef XJ_EXTRA_DEBUG (dcm) * 2003-03-11 New module interface (janakj) * 2003-03-16 flags export parameter added (janakj) * 2003-04-06 rank 0 changed to 1 in child_init (janakj) * 2003-06-19 fixed too many Jabber workers bug (mostly on RH9.0) (dcm) * 2003-08-05 adapted to the new parse_content_type_hdr function (bogdan) * 2004-06-07 db API update (andrei) */ #include #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../error.h" #include "../../ut.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../globals.h" #include "../../timer.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_content.h" #include "../../parser/parse_from.h" #include "../../lib/srdb2/db.h" #include "../../cfg/cfg_struct.h" #include "../../modules/tm/tm_load.h" #ifdef HAVE_IHTTP #include "../ihttp/ih_load.h" #endif #include "xjab_load.h" #include "xjab_worker.h" #include "xjab_util.h" MODULE_VERSION /** TM bind */ struct tm_binds tmb; #ifdef HAVE_IHTTP /** iHTTP bind */ struct ih_binds ihb; /** iHTTP callback functions */ int xjab_mod_info(ih_req_p _irp, void *_p, char *_bb, int *_bl, char *_hb, int *_hl); int xjab_connections(ih_req_p _irp, void *_p, char *_bb, int *_bl, char *_hb, int *_hl); #endif /** workers list */ xj_wlist jwl = NULL; /** Structure that represents database connection */ db_ctx_t* ctx = NULL; db_cmd_t* cmd = NULL; db_fld_t db_params[] = { {.name = "sip_id", .type = DB_CSTR}, {.name = "type", .type = DB_INT}, {.name = 0} }; db_fld_t db_cols[] = { {.name = "jab_id", .type = DB_CSTR}, {.name = "jab_passwd", .type = DB_CSTR}, {.name = 0} }; /** parameters */ static char *db_url = "mysql://root@127.0.0.1/sip_jab"; char *db_table = "jusers"; char *registrar=NULL; //"sip:registrar@iptel.org"; int nrw = 2; int max_jobs = 10; char *jaddress = "127.0.0.1"; int jport = 5222; char *jaliases = NULL; char *jdomain = NULL; char *proxy = NULL; int delay_time = 90; int sleep_time = 20; int cache_time = 600; int check_time = 20; int **pipes = NULL; static int mod_init(void); static int child_init(int rank); int xjab_manage_sipmsg(struct sip_msg *msg, int type); void xjab_check_workers(int mpid); static int xj_send_message(struct sip_msg*, char*, char*); static int xj_join_jconf(struct sip_msg*, char*, char*); static int xj_exit_jconf(struct sip_msg*, char*, char*); static int xj_go_online(struct sip_msg*, char*, char*); static int xj_go_offline(struct sip_msg*, char*, char*); static void destroy(void); /* * Exported functions */ static cmd_export_t cmds[] = { {"jab_send_message", xj_send_message, 0, 0, REQUEST_ROUTE}, {"jab_join_jconf", xj_join_jconf, 0, 0, REQUEST_ROUTE}, {"jab_exit_jconf", xj_exit_jconf, 0, 0, REQUEST_ROUTE}, {"jab_go_online", xj_go_online, 0, 0, REQUEST_ROUTE}, {"jab_go_offline", xj_go_offline, 0, 0, REQUEST_ROUTE}, {"jab_register_watcher", (cmd_function)xj_register_watcher, XJ_NO_SCRIPT_F, 0, 0 }, {"jab_unregister_watcher", (cmd_function)xj_unregister_watcher, XJ_NO_SCRIPT_F, 0, 0 }, {"load_xjab", (cmd_function)load_xjab, XJ_NO_SCRIPT_F, 0, 0 }, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"db_url", PARAM_STRING, &db_url }, {"jaddress", PARAM_STRING, &jaddress }, {"aliases", PARAM_STRING, &jaliases }, {"proxy", PARAM_STRING, &proxy }, {"jdomain", PARAM_STRING, &jdomain }, {"registrar", PARAM_STRING, ®istrar }, {"jport", PARAM_INT, &jport }, {"workers", PARAM_INT, &nrw }, {"max_jobs", PARAM_INT, &max_jobs }, {"cache_time", PARAM_INT, &cache_time}, {"delay_time", PARAM_INT, &delay_time}, {"sleep_time", PARAM_INT, &sleep_time}, {"check_time", PARAM_INT, &check_time}, {0, 0, 0} }; struct module_exports exports= { "jabber", cmds, /* Exported functions */ 0, /* RPC methods */ params, /* Exported parameters */ mod_init, /* module initialization function */ (response_function) 0, (destroy_function) destroy, 0, child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { load_tm_f load_tm; #ifdef HAVE_IHTTP load_ih_f load_ih; #endif int i; DBG("XJAB:mod_init: initializing ...\n"); if(!jdomain) { LOG(L_ERR, "XJAB:mod_init: ERROR jdomain is NULL\n"); return -1; } /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) { LOG(L_ERR, "ERROR: xjab:mod_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) return -1; #ifdef HAVE_IHTTP /* import the iHTTP auto-loading function */ if ( !(load_ih=(load_ih_f)find_export("load_ih", IH_NO_SCRIPT_F, 0))) { LOG(L_ERR, "ERROR:xjab:mod_init: can't import load_ih\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_ih( &ihb )==-1) return -1; #endif pipes = (int**)pkg_malloc(nrw*sizeof(int*)); if (pipes == NULL) { LOG(L_ERR, "XJAB:mod_init:Error while allocating pipes\n"); return -1; } for(i=0; i-<%d>\n", i, pipes[i][0], pipes[i][1]); } if((jwl = xj_wlist_init(pipes,nrw,max_jobs,cache_time,sleep_time, delay_time)) == NULL) { LOG(L_ERR, "XJAB:mod_init: error initializing workers list\n"); return -1; } if(xj_wlist_set_aliases(jwl, jaliases, jdomain, proxy) < 0) { LOG(L_ERR, "XJAB:mod_init: error setting aliases and outbound proxy\n"); return -1; } /* register nrw + 1 number of children that will keep * updating their local configuration */ cfg_register_child(nrw + 1); DBG("XJAB:mod_init: initialized ...\n"); return 0; } /* * Initialize children */ static int child_init(int rank) { int i, j, mpid, cpid; DBG("XJAB:child_init: initializing child <%d>\n", rank); /* Rank 0 is main process now - 1 is the first child (janakj) */ if(rank == 1) { #ifdef HAVE_IHTTP /** register iHTTP callbacks -- go forward in any case*/ ihb.reg_f("xjab", "XMPP Gateway", IH_MENU_YES, xjab_mod_info, NULL); ihb.reg_f("xjabc", "XMPP connections", IH_MENU_YES, xjab_connections, NULL); #endif if((mpid=fork())<0 ) { LOG(L_ERR, "XJAB:child_init:error - cannot launch worker's" " manager\n"); return -1; } if(mpid == 0) { /** launching the workers */ for(i=0;icontent_length) { LOG(L_ERR,"XJAB:xjab_manage_sipmsg: ERROR no Content-Length" " header found!\n"); goto error; } body.len = get_content_length(msg); /* parse the content-type header */ if((mime=parse_content_type_hdr(msg))<1) { LOG(L_ERR,"XJAB:xjab_manage_sipmsg: ERROR cannot parse" " Content-Type header\n"); goto error; } /* check the content-type value */ if(mime!=(TYPE_TEXT<<16)+SUBTYPE_PLAIN && mime!=(TYPE_MESSAGE<<16)+SUBTYPE_CPIM) { LOG(L_ERR,"XJAB:xjab_manage_sipmsg: ERROR invalid content-type for" " a message request! type found=%d\n", mime); goto error; } } // check for TO and FROM headers - if is not SIP MESSAGE if(parse_headers( msg, HDR_TO_F|HDR_FROM_F, 0)==-1 || !msg->to || !msg->from) { LOG(L_ERR,"XJAB:xjab_manage_sipmsg: cannot find TO or FROM HEADERS!\n"); goto error; } /* parsing from header */ if ( parse_from_header( msg )==-1 || msg->from->parsed==NULL) { DBG("ERROR:xjab_manage_sipmsg: cannot get FROM header\n"); goto error; } from_uri.s = ((struct to_body*)msg->from->parsed)->uri.s; from_uri.len = ((struct to_body*)msg->from->parsed)->uri.len; if(xj_extract_aor(&from_uri, 0)) { DBG("ERROR:xjab_manage_sipmsg: cannot get AoR from FROM header\n"); goto error; } jkey.hash = xj_get_hash(&from_uri, NULL); jkey.id = &from_uri; // get the communication pipe with the worker switch(type) { case XJ_SEND_MESSAGE: case XJ_JOIN_JCONF: case XJ_GO_ONLINE: if((pipe = xj_wlist_get(jwl, &jkey, &p)) < 0) { DBG("XJAB:xjab_manage_sipmsg: cannot find pipe of the worker!\n"); goto error; } break; case XJ_EXIT_JCONF: case XJ_GO_OFFLINE: if((pipe = xj_wlist_check(jwl, &jkey, &p)) < 0) { DBG("XJAB:xjab_manage_sipmsg: no open Jabber session for" " <%.*s>!\n", from_uri.len, from_uri.s); goto error; } break; default: DBG("XJAB:xjab_manage_sipmsg: ERROR:strange SIP msg type!\n"); goto error; } // if is for going ONLINE/OFFLINE we do not need the destination if(type==XJ_GO_ONLINE || type==XJ_GO_OFFLINE) goto prepare_job; // determination of destination // - try to get it from new_uri, r-uri or to hdr, but check it against // jdomain and aliases dst.len = 0; if( msg->new_uri.len > 0) { dst.s = msg->new_uri.s; dst.len = msg->new_uri.len; if(xj_wlist_check_aliases(jwl, &dst)) dst.len = 0; #ifdef XJ_EXTRA_DEBUG else DBG("XJAB:xjab_manage_sipmsg: using NEW URI for destination\n"); #endif } if (dst.len == 0 && msg->first_line.u.request.uri.s != NULL && msg->first_line.u.request.uri.len > 0 ) { dst.s = msg->first_line.u.request.uri.s; dst.len = msg->first_line.u.request.uri.len; if(xj_wlist_check_aliases(jwl, &dst)) dst.len = 0; #ifdef XJ_EXTRA_DEBUG else DBG("XJAB:xjab_manage_sipmsg: using R-URI for destination\n"); #endif } if(dst.len == 0 && msg->to->parsed) { dst.s = ((struct to_body*)msg->to->parsed)->uri.s; dst.len = ((struct to_body*)msg->to->parsed)->uri.len; if(dst.s == NULL || xj_wlist_check_aliases(jwl, &dst)) dst.len = 0; #ifdef XJ_EXTRA_DEBUG else DBG("XJAB:xjab_manage_sipmsg: using TO-URI for destination\n"); #endif } if(dst.len == 0) { DBG("XJAB:xjab_manage_sipmsg: destination not found in SIP message\n"); goto error; } /** skip 'sip:' and parameters in destination address */ if(xj_extract_aor(&dst, 1)) { DBG("ERROR:xjab_manage_sipmsg: cannot get AoR for destination\n"); goto error; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xjab_manage_sipmsg: DESTINATION after correction [%.*s].\n", dst.len, dst.s); #endif prepare_job: //putting the SIP message parts in share memory to be accessible by workers jsmsg = (xj_sipmsg)shm_malloc(sizeof(t_xj_sipmsg)); memset(jsmsg, 0, sizeof(t_xj_sipmsg)); if(jsmsg == NULL) return -1; switch(type) { case XJ_SEND_MESSAGE: jsmsg->msg.len = body.len; if((jsmsg->msg.s = (char*)shm_malloc(jsmsg->msg.len+1)) == NULL) { shm_free(jsmsg); goto error; } strncpy(jsmsg->msg.s, body.s, jsmsg->msg.len); break; case XJ_GO_ONLINE: case XJ_GO_OFFLINE: dst.len = 0; dst.s = 0; case XJ_JOIN_JCONF: case XJ_EXIT_JCONF: jsmsg->msg.len = 0; jsmsg->msg.s = NULL; break; default: DBG("XJAB:xjab_manage_sipmsg: this SHOULD NOT appear\n"); shm_free(jsmsg); goto error; } if(dst.len>0) { jsmsg->to.len = dst.len; if((jsmsg->to.s = (char*)shm_malloc(jsmsg->to.len+1))==NULL) { if(type == XJ_SEND_MESSAGE) shm_free(jsmsg->msg.s); shm_free(jsmsg); goto error; } strncpy(jsmsg->to.s, dst.s, jsmsg->to.len); } else { jsmsg->to.len = 0; jsmsg->to.s = 0; } jsmsg->jkey = p; jsmsg->type = type; //jsmsg->jkey->hash = jkey.hash; DBG("XJAB:xjab_manage_sipmsg:%d: sending <%p> to worker through <%d>\n", getpid(), jsmsg, pipe); // sending the SHM pointer of SIP message to the worker fl = write(pipe, &jsmsg, sizeof(jsmsg)); if(fl != sizeof(jsmsg)) { DBG("XJAB:xjab_manage_sipmsg: error when writing to worker pipe!\n"); if(type == XJ_SEND_MESSAGE) shm_free(jsmsg->msg.s); shm_free(jsmsg->to.s); shm_free(jsmsg); goto error; } return 1; error: return -1; } /** * destroy function of module */ static void destroy(void) { int i; #ifdef XJ_EXTRA_DEBUG DBG("XJAB: Unloading module ...\n"); #endif if(pipes) { // close the pipes for(i = 0; i < nrw; i++) { if(pipes[i]) { close(pipes[i][0]); close(pipes[i][1]); } pkg_free(pipes[i]); } pkg_free(pipes); } if (ctx) db_ctx_free(ctx); ctx = NULL; xj_wlist_free(jwl); DBG("XJAB: Unloaded ...\n"); } /** * register a watcher function for a Jabber user' presence */ void xj_register_watcher(str *from, str *to, void *cbf, void *pp) { xj_sipmsg jsmsg = NULL; t_xj_jkey jkey, *jp; int pipe, fl; str from_uri, to_uri; if(!to || !from || !cbf) return; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_register_watcher: from=[%.*s] to=[%.*s]\n", from->len, from->s, to->len, to->s); #endif from_uri.s = from->s; from_uri.len = from->len; if(xj_extract_aor(&from_uri, 0)) { DBG("ERROR:xjab_manage_sipmsg: cannot get AoR from FROM header\n"); goto error; } jkey.hash = xj_get_hash(&from_uri, NULL); jkey.id = &from_uri; if((pipe = xj_wlist_get(jwl, &jkey, &jp)) < 0) { DBG("XJAB:xj_register_watcher: cannot find pipe of the worker!\n"); goto error; } //putting the SIP message parts in share memory to be accessible by workers jsmsg = (xj_sipmsg)shm_malloc(sizeof(t_xj_sipmsg)); memset(jsmsg, 0, sizeof(t_xj_sipmsg)); if(jsmsg == NULL) goto error; jsmsg->msg.len = 0; jsmsg->msg.s = NULL; to_uri.s = to->s; to_uri.len = to->len; /** skip 'sip:' and parameters in destination address */ if(xj_extract_aor(&to_uri, 1)) { DBG("ERROR:xjab_manage_sipmsg: cannot get AoR for destination\n"); goto error; } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_register_watcher: DESTINATION after correction [%.*s].\n", to_uri.len, to_uri.s); #endif jsmsg->to.len = to_uri.len; if((jsmsg->to.s = (char*)shm_malloc(jsmsg->to.len+1)) == NULL) { if(jsmsg->msg.s) shm_free(jsmsg->msg.s); shm_free(jsmsg); goto error; } strncpy(jsmsg->to.s, to_uri.s, jsmsg->to.len); jsmsg->to.s[jsmsg->to.len] = '\0'; jsmsg->jkey = jp; jsmsg->type = XJ_REG_WATCHER; //jsmsg->jkey->hash = jkey.hash; jsmsg->cbf = (pa_callback_f)cbf; jsmsg->p = pp; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_register_watcher:%d: sending <%p> to worker through <%d>\n", getpid(), jsmsg, pipe); #endif // sending the SHM pointer of SIP message to the worker fl = write(pipe, &jsmsg, sizeof(jsmsg)); if(fl != sizeof(jsmsg)) { DBG("XJAB:xj_register_watcher: error when writing to worker pipe!\n"); if(jsmsg->msg.s) shm_free(jsmsg->msg.s); shm_free(jsmsg->to.s); shm_free(jsmsg); goto error; } error: return; } /** * unregister a watcher for a Jabber user' presence */ void xj_unregister_watcher(str *from, str *to, void *cbf, void *pp) { if(!to || !from) return; } /** * check if all SER2Jab workers are still alive * - if not, try to launch new ones */ void xjab_check_workers(int mpid) { int i, n, stat; //DBG("XJAB:%d:xjab_check_workers: time=%d\n", mpid, get_ticks()); if(!jwl || jwl->len <= 0) return; for(i=0; i < jwl->len; i++) { if(jwl->workers[i].pid > 0) { stat = 0; n = waitpid(jwl->workers[i].pid, &stat, WNOHANG); if(n == 0 || n!=jwl->workers[i].pid) continue; LOG(L_ERR,"XJAB:xjab_check_workers: worker[%d][pid=%d] has exited" " - status=%d err=%d errno=%d\n", i, jwl->workers[i].pid, stat, n, errno); xj_wlist_clean_jobs(jwl, i, 1); xj_wlist_set_pid(jwl, -1, i); } #ifdef XJ_EXTRA_DEBUG DBG("XJAB:%d:xjab_check_workers: create a new worker[%d]\n", mpid, i); #endif if ( (stat=fork())<0 ) { #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xjab_check_workers: error - cannot launch new" " worker[%d]\n", i); #endif LOG(L_ERR, "XJAB:xjab_check_workers: error - worker[%d] lost" " forever \n", i); return; } if (stat == 0) { if(xj_wlist_set_pid(jwl, getpid(), i) < 0) { LOG(L_ERR, "XJAB:xjab_check_workers: error setting new" " worker's pid - w[%d]\n", i); return; } /* initialize the config framework * The child process was not registered under * the framework during mod_init, therefore the * late version needs to be called. (Miklos) */ if (cfg_late_child_init()) return; ctx = db_ctx("jabber"); if (ctx == NULL) goto dberror; if (db_add_db(ctx, db_url) < 0) goto dberror; if (db_connect(ctx) < 0) goto dberror; cmd = db_cmd(DB_GET, ctx, db_table, db_cols, db_params, NULL); if (!cmd) goto dberror; xj_worker_process(jwl,jaddress,jport,i, cmd); db_cmd_free(cmd); db_ctx_free(ctx); ctx = NULL; /* destroy the local config */ cfg_child_destroy(); exit(0); } } dberror: if (cmd) db_cmd_free(cmd); cmd = NULL; if (ctx) db_ctx_free(ctx); ctx = NULL; } #ifdef HAVE_IHTTP /** * Module's information retrieval - function to use with iHttp module * */ int xjab_mod_info(ih_req_p _irp, void *_p, char *_bb, int *_bl, char *_hb, int *_hl) { if(!_irp || !_bb || !_bl || *_bl <= 0 || !_hb || !_hl || *_hl <= 0) return -1; *_hl = 0; *_hb = 0; strcpy(_bb, "

SER2Jabber Gateway

"); strcat(_bb, "
Module parameters:
"); strcat(_bb, "
-- db table = "); strcat(_bb, db_table); strcat(_bb, "
-- workers = "); strcat(_bb, int2str(nrw, NULL)); strcat(_bb, "
-- max jobs per worker = "); strcat(_bb, int2str(max_jobs, NULL)); strcat(_bb, "
-- jabber server address = "); strcat(_bb, jaddress); strcat(_bb, "
-- jabber server port = "); strcat(_bb, int2str(jport, NULL)); strcat(_bb, "
-- aliases = "); strcat(_bb, (jaliases)?jaliases:"NULL"); strcat(_bb, "
-- jabber domain = "); strcat(_bb, (jdomain)?jdomain:"NULL"); strcat(_bb, "
-- proxy address = "); strcat(_bb, (proxy)?proxy:"NULL"); strcat(_bb, "
-- delay time = "); strcat(_bb, int2str(delay_time, NULL)); strcat(_bb, "
-- sleep time = "); strcat(_bb, int2str(sleep_time, NULL)); strcat(_bb, "
-- cache time = "); strcat(_bb, int2str(cache_time, NULL)); strcat(_bb, "
-- check time = "); strcat(_bb, int2str(check_time, NULL)); *_bl = strlen(_bb); return 0; } /** * SER2Jab connection management - function to use with iHttp module * - be aware of who is able to use the ihttp because he can close any * open connection between SER and Jabber server */ int xjab_connections(ih_req_p _irp, void *_p, char *_bb, int *_bl, char *_hb, int *_hl) { t_xj_jkey jkey, *p; str _u; ih_param_p _ipp = NULL; int idx, i, maxcount; char *cp; if(!_irp || !_bb || !_bl || *_bl <= 0 || !_hb || !_hl || *_hl <= 0) return -1; *_hl = 0; *_hb = 0; idx = -1; strcpy(_bb, "

Active XMPP connections

"); if(_irp->params) { strcat(_bb, "
Close action is alpha release!
"); _ipp = _irp->params; i = 0; while(_ipp) { switch(_ipp->name[0]) { case 'w': idx = 0; cp = _ipp->value; while(*cp && *cp>='0' && *cp<='9') { idx = idx*10 + *cp-'0'; cp++; } i++; break; case 'u': _u.s = _ipp->value; _u.len = strlen(_ipp->value); jkey.id = &_u; i++; break; case 'i': jkey.hash = 0; cp = _ipp->value; while(*cp && *cp>='0' && *cp<='9') { jkey.hash = jkey.hash*10 + *cp-'0'; cp++; } i++; break; } _ipp = _ipp->next; } if(i!=3 || idx < 0 || idx >= jwl->len) { strcat(_bb, "
Bad parameters!\n"); } else { strcat(_bb, "
The connection of ["); strcat(_bb, _u.s); if(xj_wlist_set_flag(jwl, &jkey, XJ_FLAG_CLOSE) < 0) strcat(_bb, "] does not exist!\n"); else strcat(_bb, "] was scheduled for closing!
\n"); } *_bl = strlen(_bb); return 0; } if(jwl!=NULL && jwl->len > 0 && jwl->workers!=NULL) { for(idx=0; idxlen; idx++) { strcat(_bb, "
Worker["); strcat(_bb, int2str(idx, NULL)); strcat(_bb, "]   pid="); strcat(_bb, int2str(jwl->workers[idx].pid, NULL)); strcat(_bb, "   nr of jobs="); strcat(_bb, int2str(jwl->workers[idx].nr, NULL)); if(!jwl->workers[idx].sip_ids) continue; lock_set_get(jwl->sems, idx); maxcount = count234(jwl->workers[idx].sip_ids); for (i = 0; i < maxcount; i++) { p = (xj_jkey)index234(jwl->workers[idx].sip_ids, i); if(p == NULL) continue; strcat(_bb, "
   "); strcat(_bb, int2str(i, NULL)); strcat(_bb, ".   "); strcat(_bb, "close"); strcat(_bb, "   "); strcat(_bb, int2str(p->hash, NULL)); strcat(_bb, "   "); strncat(_bb, p->id->s, p->id->len); } lock_set_release(jwl->sems, idx); } } *_bl = strlen(_bb); return 0; } #endif // HAVE_IHTTP kamailio-4.0.4/obsolete/jabber_s/tree234.h0000644000000000000000000001332212223032460016722 0ustar rootroot/* * $Id$ * * tree234.h: header defining functions in tree234.c. * * This file is copyright 1999-2001 Simon Tatham. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #ifndef TREE234_H #define TREE234_H /* * This typedef is opaque outside tree234.c itself. */ typedef struct tree234_Tag tree234; typedef int (*cmpfn234)(void *, void *); /** * function for deallocation of a element pointer from a node */ typedef void (*freefn)(void *); /* * Create a 2-3-4 tree. If `cmp' is NULL, the tree is unsorted, and * lookups by key will fail: you can only look things up by numeric * index, and you have to use addpos234() and delpos234(). */ tree234 *newtree234(cmpfn234 cmp); /* * Free a 2-3-4 tree (not including freeing the elements). */ void freetree234(tree234 *t); /* * Free a 2-3-4 tree (including freeing the elements with 'fn' function). */ void free2tree234(tree234 *t, freefn fn); /* * Add an element e to a sorted 2-3-4 tree t. Returns e on success, * or if an existing element compares equal, returns that. */ void *add234(tree234 *t, void *e); /* * Add an element e to an unsorted 2-3-4 tree t. Returns e on * success, NULL on failure. (Failure should only occur if the * index is out of range or the tree is sorted.) * * Index range can be from 0 to the tree's current element count, * inclusive. */ void *addpos234(tree234 *t, void *e, int index); /* * Look up the element at a given numeric index in a 2-3-4 tree. * Returns NULL if the index is out of range. * * One obvious use for this function is in iterating over the whole * of a tree (sorted or unsorted): * * for (i = 0; (p = index234(tree, i)) != NULL; i++) consume(p); * * or * * int maxcount = count234(tree); * for (i = 0; i < maxcount; i++) { * p = index234(tree, i); * assert(p != NULL); * consume(p); * } */ void *index234(tree234 *t, int index); /* * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not * found. e is always passed as the first argument to cmp, so cmp * can be an asymmetric function if desired. cmp can also be passed * as NULL, in which case the compare function from the tree proper * will be used. * * Three of these functions are special cases of findrelpos234. The * non-`pos' variants lack the `index' parameter: if the parameter * is present and non-NULL, it must point to an integer variable * which will be filled with the numeric index of the returned * element. * * The non-`rel' variants lack the `relation' parameter. This * parameter allows you to specify what relation the element you * provide has to the element you're looking for. This parameter * can be: * * REL234_EQ - find only an element that compares equal to e * REL234_LT - find the greatest element that compares < e * REL234_LE - find the greatest element that compares <= e * REL234_GT - find the smallest element that compares > e * REL234_GE - find the smallest element that compares >= e * * Non-`rel' variants assume REL234_EQ. * * If `rel' is REL234_GT or REL234_LT, the `e' parameter may be * NULL. In this case, REL234_GT will return the smallest element * in the tree, and REL234_LT will return the greatest. This gives * an alternative means of iterating over a sorted tree, instead of * using index234: * * // to loop forwards * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_GT)) != NULL ;) * consume(p); * * // to loop backwards * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_LT)) != NULL ;) * consume(p); */ enum { REL234_EQ, REL234_LT, REL234_LE, REL234_GT, REL234_GE }; void *find234(tree234 *t, void *e, cmpfn234 cmp); void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation); void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index); void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation, int *index); /* * Delete an element e in a 2-3-4 tree. Does not free the element, * merely removes all links to it from the tree nodes. * * delpos234 deletes the element at a particular tree index: it * works on both sorted and unsorted trees. * * del234 deletes the element passed to it, so it only works on * sorted trees. (It's equivalent to using findpos234 to determine * the index of an element, and then passing that index to * delpos234.) * * Both functions return a pointer to the element they delete, for * the user to free or pass on elsewhere or whatever. If the index * is out of range (delpos234) or the element is already not in the * tree (del234) then they return NULL. */ void *del234(tree234 *t, void *e); void *delpos234(tree234 *t, int index); /* * Return the total element count of a tree234. */ int count234(tree234 *t); #endif /* TREE234_H */ kamailio-4.0.4/obsolete/jabber_s/xode_str.c0000644000000000000000000001262312223032460017357 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include "xode.h" xode_pool xode_spool_getpool(const xode_spool s) { if(s == NULL) return NULL; return s->p; } int xode_spool_getlen(const xode_spool s) { if(s == NULL) return 0; return s->len; } void xode_spool_free(xode_spool s) { xode_pool_free(xode_spool_getpool(s)); } xode_spool xode_spool_newfrompool(xode_pool p) { xode_spool s; s = xode_pool_malloc(p, sizeof(struct xode_spool_struct)); s->p = p; s->len = 0; s->last = NULL; s->first = NULL; return s; } xode_spool xode_spool_new(void) { return xode_spool_newfrompool(xode_pool_heap(512)); } void xode_spool_add(xode_spool s, char *str) { struct xode_spool_node *sn; int len; if(str == NULL) return; len = strlen(str); if(len == 0) return; sn = xode_pool_malloc(s->p, sizeof(struct xode_spool_node)); sn->c = xode_pool_strdup(s->p, str); sn->next = NULL; s->len += len; if(s->last != NULL) s->last->next = sn; s->last = sn; if(s->first == NULL) s->first = sn; } void xode_spooler(xode_spool s, ...) { va_list ap; char *arg = NULL; if(s == NULL) return; va_start(ap, s); /* loop till we hit our end flag, the first arg */ while(1) { arg = va_arg(ap,char *); if((void*)arg == (void*)s || arg == NULL) break; else xode_spool_add(s, arg); } va_end(ap); } char *xode_spool_tostr(xode_spool s) { char *ret,*tmp; struct xode_spool_node *next; if(s == NULL || s->len == 0 || s->first == NULL) return NULL; ret = xode_pool_malloc(s->p, s->len + 1); *ret = '\0'; next = s->first; tmp = ret; while(next != NULL) { tmp = strcat(tmp,next->c); next = next->next; } return ret; } /* convenience :) */ char *xode_spool_str(xode_pool p, ...) { va_list ap; xode_spool s; char *arg = NULL; if(p == NULL) return NULL; s = xode_spool_newfrompool(p); va_start(ap, p); /* loop till we hit our end flag, the first arg */ while(1) { arg = va_arg(ap,char *); if((void*)arg == (void*)p) break; else xode_spool_add(s, arg); } va_end(ap); return xode_spool_tostr(s); } char *xode_strunescape(xode_pool p, char *buf) { int i,j=0; char *temp; if (p == NULL || buf == NULL) return(NULL); if (strchr(buf,'&') == NULL) return(buf); temp = xode_pool_malloc(p,strlen(buf)+1); if (temp == NULL) return(NULL); for(i=0;i': newlen+=4; break; } } if(oldlen == newlen) return buf; temp = xode_pool_malloc(p,newlen+1); if (temp==NULL) return(NULL); for(i=j=0;i': memcpy(&temp[j],">",4); j += 4; break; default: temp[j++] = buf[i]; } } temp[j] = '\0'; return temp; } kamailio-4.0.4/obsolete/jabber_s/xpool.c0000644000000000000000000001457212223032460016676 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ * * 2/27/00:3am, random plans by jer * * ok based on gprof, we really need some innovation here... my thoughs are this: * * most things are strings, so have a string-based true-blue garbage collector * one big global hash containing all the strings created by any pstrdup, returning const char * * a refcount on each string block * when a pool is freed, it moves down the refcount * garbage collector collects pools on the free stack, and runs through the hash for unused strings * j_strcmp can check for == (if they are both from a pstrdup) * * let's see... this would change: * pstrdup: do a hash lookup, success=return, fail=pmalloc & hash put * pool_free: * * * * * */ #include "xode.h" //#include "config.h" #define _xode_pool__malloc malloc #define _xode_pool__free free /* xode_pfree - a linked list node which stores an allocation chunk, plus a callback */ struct xode_pool_free { xode_pool_cleaner f; void *arg; struct xode_pool_heap *heap; struct xode_pool_free *next; }; /* make an empty pool */ xode_pool _xode_pool_new(void) { xode_pool p; while((p = _xode_pool__malloc(sizeof(_xode_pool))) == NULL) sleep(1); p->cleanup = NULL; p->heap = NULL; p->size = 0; return p; } /* free a heap */ void _xode_pool_heapfree(void *arg) { struct xode_pool_heap *h = (struct xode_pool_heap *)arg; _xode_pool__free(h->block); _xode_pool__free(h); } /* mem should always be freed last */ void _xode_pool_cleanup_append(xode_pool p, struct xode_pool_free *pf) { struct xode_pool_free *cur; if(p->cleanup == NULL) { p->cleanup = pf; return; } /* fast forward to end of list */ for(cur = p->cleanup; cur->next != NULL; cur = cur->next); cur->next = pf; } /* create a cleanup tracker */ struct xode_pool_free *_xode_pool_free(xode_pool p, xode_pool_cleaner f, void *arg) { struct xode_pool_free *ret; /* make the storage for the tracker */ while((ret = _xode_pool__malloc(sizeof(struct xode_pool_free))) == NULL) sleep(1); ret->f = f; ret->arg = arg; ret->next = NULL; return ret; } /* create a heap and make sure it get's cleaned up */ struct xode_pool_heap *_xode_pool_heap(xode_pool p, int size) { struct xode_pool_heap *ret; struct xode_pool_free *clean; /* make the return heap */ while((ret = _xode_pool__malloc(sizeof(struct xode_pool_heap))) == NULL) sleep(1); while((ret->block = _xode_pool__malloc(size)) == NULL) sleep(1); ret->size = size; p->size += size; ret->used = 0; /* append to the cleanup list */ clean = _xode_pool_free(p, _xode_pool_heapfree, (void *)ret); clean->heap = ret; /* for future use in finding used mem for pstrdup */ _xode_pool_cleanup_append(p, clean); return ret; } xode_pool _xode_pool_newheap(int bytes) { xode_pool p; p = _xode_pool_new(); p->heap = _xode_pool_heap(p,bytes); return p; } void *xode_pool_malloc(xode_pool p, int size) { void *block; if(p == NULL) { fprintf(stderr,"Memory Leak! xode_pmalloc received NULL pool, unable to track allocation, exiting]\n"); abort(); } /* if there is no heap for this pool or it's a big request, just raw, I like how we clean this :) */ if(p->heap == NULL || size > (p->heap->size / 2)) { while((block = _xode_pool__malloc(size)) == NULL) sleep(1); p->size += size; _xode_pool_cleanup_append(p, _xode_pool_free(p, _xode_pool__free, block)); return block; } /* we have to preserve boundaries, long story :) */ if(size >= 4) while(p->heap->used&7) p->heap->used++; /* if we don't fit in the old heap, replace it */ if(size > (p->heap->size - p->heap->used)) p->heap = _xode_pool_heap(p, p->heap->size); /* the current heap has room */ block = (char *)p->heap->block + p->heap->used; p->heap->used += size; return block; } void *xode_pool_mallocx(xode_pool p, int size, char c) { void* result = xode_pool_malloc(p, size); if (result != NULL) memset(result, c, size); return result; } /* easy safety utility (for creating blank mem for structs, etc) */ void *xode_pool_malloco(xode_pool p, int size) { void *block = xode_pool_malloc(p, size); memset(block, 0, size); return block; } /* XXX efficient: move this to const char * and then loop through the existing heaps to see if src is within a block in this pool */ char *xode_pool_strdup(xode_pool p, const char *src) { char *ret; if(src == NULL) return NULL; ret = xode_pool_malloc(p,strlen(src) + 1); strcpy(ret,src); return ret; } /* when move above, this one would actually return a new block */ char *xode_pool_strdupx(xode_pool p, const char *src) { return xode_pool_strdup(p, src); } int xode_pool_size(xode_pool p) { if(p == NULL) return 0; return p->size; } void xode_pool_free(xode_pool p) { struct xode_pool_free *cur, *stub; if(p == NULL) return; cur = p->cleanup; while(cur != NULL) { (*cur->f)(cur->arg); stub = cur->next; _xode_pool__free(cur); cur = stub; } _xode_pool__free(p); } /* public cleanup utils, insert in a way that they are run FIFO, before mem frees */ void xode_pool_cleanup(xode_pool p, xode_pool_cleaner f, void *arg) { struct xode_pool_free *clean; clean = _xode_pool_free(p, f, arg); clean->next = p->cleanup; p->cleanup = clean; } xode_pool xode_pool_new(void) { return _xode_pool_new(); } xode_pool xode_pool_heap(const int bytes) { return _xode_pool_newheap(bytes); } kamailio-4.0.4/obsolete/jabber_s/xode_from.c0000644000000000000000000001301412223032460017505 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include "xode.h" #include #include #include static void _xode_put_expatattribs(xode current, const char **atts) { int i = 0; if (atts == NULL) return; while (atts[i] != '\0') { xode_put_attrib(current, atts[i], atts[i+1]); i += 2; } } static void _xode_expat_startElement(void* userdata, const char* name, const char** atts) { /* get the xmlnode pointed to by the userdata */ xode *x = userdata; xode current = *x; if (current == NULL) { /* allocate a base node */ current = xode_new(name); _xode_put_expatattribs(current, atts); *x = current; } else { *x = xode_insert_tag(current, name); _xode_put_expatattribs(*x, atts); } } static void _xode_expat_endElement(void* userdata, const char* name) { xode *x = userdata; xode current = *x; current->complete = 1; current = xode_get_parent(current); /* if it's NULL we've hit the top folks, otherwise back up a level */ if(current != NULL) *x = current; } static void _xode_expat_charData(void* userdata, const char* s, int len) { xode *x = userdata; xode current = *x; xode_insert_cdata(current, s, len); } xode xode_from_str(char *str, int len) { XML_Parser p; xode *x, node; /* pointer to an xmlnode */ if(NULL == str) return NULL; if(len == -1) len = strlen(str); x = malloc(sizeof(void *)); *x = NULL; /* pointer to NULL */ p = XML_ParserCreate(NULL); XML_SetUserData(p, x); XML_SetElementHandler(p, _xode_expat_startElement, _xode_expat_endElement); XML_SetCharacterDataHandler(p, _xode_expat_charData); if(!XML_Parse(p, str, len, 1)) { /* jdebug(ZONE,"xmlnode_str_error: %s",(char *)XML_ErrorString(XML_GetErrorCode(p)));*/ xode_free(*x); *x = NULL; } node = *x; free(x); XML_ParserFree(p); return node; /* return the xmlnode x points to */ } xode xode_from_strx(char *str, int len, int *err, int *pos) { XML_Parser p; xode *x, node; /* pointer to an xmlnode */ if(NULL == str) return NULL; if(len == -1) len = strlen(str); x = malloc(sizeof(void *)); *x = NULL; /* pointer to NULL */ p = XML_ParserCreate(NULL); XML_SetUserData(p, x); XML_SetElementHandler(p, _xode_expat_startElement, _xode_expat_endElement); XML_SetCharacterDataHandler(p, _xode_expat_charData); XML_Parse(p, str, len, 0); if(err != NULL) *err = XML_GetErrorCode(p); if(pos != NULL) *pos = XML_GetCurrentByteIndex(p); node = *x; free(x); XML_ParserFree(p); return node; /* return the xmlnode x points to */ } xode xode_from_file(char *file) { XML_Parser p; xode *x, node; /* pointer to an xmlnode */ char buf[BUFSIZ]; int done, fd, len; char _file[1000]; if(NULL == file) return NULL; /* perform tilde expansion */ if(*file == '~') { char *env = getenv("HOME"); if(env != NULL) snprintf((char*)_file, 1000, "%s%s", env, file + 1); else snprintf((char*)_file, 1000, "%s", file); } else { snprintf((char*)_file, 1000, "%s", file); } fd = open((char*)&_file,O_RDONLY); if(fd < 0) return NULL; x = malloc(sizeof(void *)); *x = NULL; /* pointer to NULL */ p = XML_ParserCreate(NULL); XML_SetUserData(p, x); XML_SetElementHandler(p, _xode_expat_startElement, _xode_expat_endElement); XML_SetCharacterDataHandler(p, _xode_expat_charData); do{ len = read(fd, buf, BUFSIZ); done = len < BUFSIZ; if(!XML_Parse(p, buf, len, done)) { /* jdebug(ZONE,"xmlnode_file_parseerror: %s",(char *)XML_ErrorString(XML_GetErrorCode(p)));*/ xode_free(*x); *x = NULL; done = 1; } }while(!done); node = *x; XML_ParserFree(p); free(x); close(fd); return node; /* return the xmlnode x points to */ } int xode_to_file(char *file, xode node) { char *doc; int fd, i; char _file[1000]; if(file == NULL || node == NULL) return -1; /* perform tilde expansion */ if(*file == '~') { char *env = getenv("HOME"); if(env != NULL) snprintf((char*)_file, 1000, "%s%s", env, file + 1); else snprintf((char*)_file, 1000, "%s", file); } else { snprintf((char*)_file, 1000, "%s", file); } fd = open((char*)&_file, O_CREAT | O_WRONLY | O_TRUNC, 0600); if(fd < 0) return -1; doc = xode_to_str(node); i = write(fd,doc,strlen(doc)); if(i < 0) return -1; close(fd); return 1; } kamailio-4.0.4/obsolete/jabber_s/xjab_dmsg.h0000644000000000000000000000601412223032460017470 0ustar rootroot/* * $Id$ * * eXtended JABber module * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*** error and info messages ***/ #ifndef _XJAB_DMSG_H_ #define _XJAB_DMSG_H_ #define XJ_DMSG_INF_DISCONNECTED "INFO: Connection to Jabber server lost. You have to login to Jabber server again (join again the conferences that you were participating, too)." #define XJ_DMSG_ERR_SENDIM "ERROR: Your message was not sent. Connection to IM network failed." #define XJ_DMSG_ERR_NOTJCONF "ERROR: Your message was not sent. You are not joined in the conference. Please join the room before sending messages." #define XJ_DMSG_INF_JCONFEXIT "INFO: You have just left the conference." #define XJ_DMSG_ERR_JGWFORB "ERROR: Your message was not sent. You do not have permission to use the gateway." #define XJ_DMSG_ERR_NOJSRV "ERROR: Your message was not sent. Cannot connect to Jabber server." #define XJ_DMSG_ERR_JAUTH "ERROR: Your message was not sent. Authentication to Jabber server failed." #define XJ_DMSG_ERR_JGWFULL "ERROR: Your message was not sent. SIP-2-JABBER gateway is full." #define XJ_DMSG_ERR_JOINJCONF "ERROR: Cannot join the conference room." #define XJ_DMSG_ERR_NEWJCONF "ERROR:Cannot create a new conference session." #define XJ_DMSG_ERR_SENDJMSG "ERROR: Your message was not sent. Something wrong during transmitting to Jabber network." #define XJ_DMSG_ERR_STOREJMSG "ERROR: Your message was not sent. Something wrong while trying to transmit it to Jabber network." #define XJ_DMSG_ERR_NOREGIM "ERROR: Your message was not sent. You are not registered with this IM gateway." #define XJ_DMSG_ERR_DISCONNECTED "ERROR: Connection to Jabber server lost. You have to login to Jabber server again (join again the conferences that you were participating, too)." #define XJ_DMSG_INF_JOFFLINE "INFO: Your are now offline in Jabber network. Thank you for using SIP-Jabber gateway." #define XJ_DMSG_ERR_JCONFNICK "ERROR: Your nickname already exists in the conference room. Please choose a new one." #define XJ_DMSG_ERR_JCONFREFUSED "ERROR: Your participation to the conference room was refused." #endif kamailio-4.0.4/obsolete/jabber_s/xsnprintf.c0000644000000000000000000006455212223032460017573 0ustar rootroot/* ==================================================================== * $Id$ * * Copyright (c) 1995-1998 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * 4. The names "Apache Server" and "Apache Group" must not be used to * endorse or promote products derived from this software without * prior written permission. * * 5. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Group and was originally based * on public domain software written at the National Center for * Supercomputing Applications, University of Illinois, Urbana-Champaign. * For more information on the Apache Group and the Apache HTTP server * project, please see . * * This code is based on, and used with the permission of, the * SIO stdio-replacement strx_* functions by Panos Tsirigotis * for xinetd. */ #include "xode.h" #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) #include #include #include #include #include #include #include #ifdef HAVE_GCVT #define ap_ecvt ecvt #define ap_fcvt fcvt #define ap_gcvt gcvt #else /* * cvt.c - IEEE floating point formatting routines for FreeBSD * from GNU libc-4.6.27 */ /* * ap_ecvt converts to decimal * the number of digits is specified by ndigit * decpt is set to the position of the decimal point * sign is set to 0 for positive, 1 for negative */ #define NDIG 80 static char * ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag) { register int r2; double fi, fj; register char *p, *p1; static char buf[NDIG]; if (ndigits >= NDIG - 1) ndigits = NDIG - 2; r2 = 0; *sign = 0; p = &buf[0]; if (arg < 0) { *sign = 1; arg = -arg; } arg = modf(arg, &fi); p1 = &buf[NDIG]; /* * Do integer part */ if (fi != 0) { p1 = &buf[NDIG]; while (fi != 0) { fj = modf(fi / 10, &fi); *--p1 = (int) ((fj + .03) * 10) + '0'; r2++; } while (p1 < &buf[NDIG]) *p++ = *p1++; } else if (arg > 0) { while ((fj = arg * 10) < 1) { arg = fj; r2--; } } p1 = &buf[ndigits]; if (eflag == 0) p1 += r2; *decpt = r2; if (p1 < &buf[0]) { buf[0] = '\0'; return (buf); } while (p <= p1 && p < &buf[NDIG]) { arg *= 10; arg = modf(arg, &fj); *p++ = (int) fj + '0'; } if (p1 >= &buf[NDIG]) { buf[NDIG - 1] = '\0'; return (buf); } p = p1; *p1 += 5; while (*p1 > '9') { *p1 = '0'; if (p1 > buf) ++ * --p1; else { *p1 = '1'; (*decpt)++; if (eflag == 0) { if (p > buf) *p = '0'; p++; } } } *p = '\0'; return (buf); } static char * ap_ecvt(double arg, int ndigits, int *decpt, int *sign) { return (ap_cvt(arg, ndigits, decpt, sign, 1)); } static char * ap_fcvt(double arg, int ndigits, int *decpt, int *sign) { return (ap_cvt(arg, ndigits, decpt, sign, 0)); } /* * ap_gcvt - Floating output conversion to * minimal length string */ static char * ap_gcvt(double number, int ndigit, char *buf) { int sign, decpt; register char *p1, *p2; int i; p1 = ap_ecvt(number, ndigit, &decpt, &sign); p2 = buf; if (sign) *p2++ = '-'; for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--) ndigit--; if ((decpt >= 0 && decpt - ndigit > 4) || (decpt < 0 && decpt < -3)) { /* use E-style */ decpt--; *p2++ = *p1++; *p2++ = '.'; for (i = 1; i < ndigit; i++) *p2++ = *p1++; *p2++ = 'e'; if (decpt < 0) { decpt = -decpt; *p2++ = '-'; } else *p2++ = '+'; if (decpt / 100 > 0) *p2++ = decpt / 100 + '0'; if (decpt / 10 > 0) *p2++ = (decpt % 100) / 10 + '0'; *p2++ = decpt % 10 + '0'; } else { if (decpt <= 0) { if (*p1 != '0') *p2++ = '.'; while (decpt < 0) { decpt++; *p2++ = '0'; } } for (i = 1; i <= ndigit; i++) { *p2++ = *p1++; if (i == decpt) *p2++ = '.'; } if (ndigit < decpt) { while (ndigit++ < decpt) *p2++ = '0'; *p2++ = '.'; } } if (p2[-1] == '.') p2--; *p2 = '\0'; return (buf); } #endif /* HAVE_CVT */ typedef enum { NO = 0, YES = 1 } boolean_e; #define FALSE 0 #define TRUE 1 #define NUL '\0' #define INT_NULL ((int *)0) #define WIDE_INT long typedef WIDE_INT wide_int; typedef unsigned WIDE_INT u_wide_int; typedef int bool_int; #define S_NULL "(null)" #define S_NULL_LEN 6 #define FLOAT_DIGITS 6 #define EXPONENT_LENGTH 10 /* * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions * * XXX: this is a magic number; do not decrease it */ #define NUM_BUF_SIZE 512 /* * Descriptor for buffer area */ struct buf_area { char *buf_end; char *nextb; /* pointer to next byte to read/write */ }; typedef struct buf_area buffy; /* * The INS_CHAR macro inserts a character in the buffer and writes * the buffer back to disk if necessary * It uses the char pointers sp and bep: * sp points to the next available character in the buffer * bep points to the end-of-buffer+1 * While using this macro, note that the nextb pointer is NOT updated. * * NOTE: Evaluation of the c argument should not have any side-effects */ #define INS_CHAR( c, sp, bep, cc ) \ { \ if ( sp < bep ) \ { \ *sp++ = c ; \ cc++ ; \ } \ } #define NUM( c ) ( c - '0' ) #define STR_TO_DEC( str, num ) \ num = NUM( *str++ ) ; \ while ( isdigit((int)*str ) ) \ { \ num *= 10 ; \ num += NUM( *str++ ) ; \ } /* * This macro does zero padding so that the precision * requirement is satisfied. The padding is done by * adding '0's to the left of the string that is going * to be printed. */ #define FIX_PRECISION( adjust, precision, s, s_len ) \ if ( adjust ) \ while ( s_len < precision ) \ { \ *--s = '0' ; \ s_len++ ; \ } /* * Macro that does padding. The padding is done by printing * the character ch. */ #define PAD( width, len, ch ) do \ { \ INS_CHAR( ch, sp, bep, cc ) ; \ width-- ; \ } \ while ( width > len ) /* * Prefix the character ch to the string str * Increase length * Set the has_prefix flag */ #define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES /* * Convert num to its decimal format. * Return value: * - a pointer to a string containing the number (no sign) * - len contains the length of the string * - is_negative is set to TRUE or FALSE depending on the sign * of the number (always set to FALSE if is_unsigned is TRUE) * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) */ static char * conv_10(register wide_int num, register bool_int is_unsigned, register bool_int * is_negative, char *buf_end, register int *len) { register char *p = buf_end; register u_wide_int magnitude; if (is_unsigned) { magnitude = (u_wide_int) num; *is_negative = FALSE; } else { *is_negative = (num < 0); /* * On a 2's complement machine, negating the most negative integer * results in a number that cannot be represented as a signed integer. * Here is what we do to obtain the number's magnitude: * a. add 1 to the number * b. negate it (becomes positive) * c. convert it to unsigned * d. add 1 */ if (*is_negative) { wide_int t = num + 1; magnitude = ((u_wide_int) - t) + 1; } else magnitude = (u_wide_int) num; } /* * We use a do-while loop so that we write at least 1 digit */ do { register u_wide_int new_magnitude = magnitude / 10; *--p = magnitude - new_magnitude * 10 + '0'; magnitude = new_magnitude; } while (magnitude); *len = buf_end - p; return (p); } /* * Convert a floating point number to a string formats 'f', 'e' or 'E'. * The result is placed in buf, and len denotes the length of the string * The sign is returned in the is_negative argument (and is not placed * in buf). */ static char * conv_fp(register char format, register double num, boolean_e add_dp, int precision, bool_int * is_negative, char *buf, int *len) { register char *s = buf; register char *p; int decimal_point; if (format == 'f') p = ap_fcvt(num, precision, &decimal_point, is_negative); else /* either e or E format */ p = ap_ecvt(num, precision + 1, &decimal_point, is_negative); /* * Check for Infinity and NaN */ if (isalpha((int)*p)) { *len = strlen(strcpy(buf, p)); *is_negative = FALSE; return (buf); } if (format == 'f') { if (decimal_point <= 0) { *s++ = '0'; if (precision > 0) { *s++ = '.'; while (decimal_point++ < 0) *s++ = '0'; } else if (add_dp) { *s++ = '.'; } } else { while (decimal_point-- > 0) { *s++ = *p++; } if (precision > 0 || add_dp) { *s++ = '.'; } } } else { *s++ = *p++; if (precision > 0 || add_dp) *s++ = '.'; } /* * copy the rest of p, the NUL is NOT copied */ while (*p) *s++ = *p++; if (format != 'f') { char temp[EXPONENT_LENGTH]; /* for exponent conversion */ int t_len; bool_int exponent_is_negative; *s++ = format; /* either e or E */ decimal_point--; if (decimal_point != 0) { p = conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative, &temp[EXPONENT_LENGTH], &t_len); *s++ = exponent_is_negative ? '-' : '+'; /* * Make sure the exponent has at least 2 digits */ if (t_len == 1) *s++ = '0'; while (t_len--) *s++ = *p++; } else { *s++ = '+'; *s++ = '0'; *s++ = '0'; } } *len = s - buf; return (buf); } /* * Convert num to a base X number where X is a power of 2. nbits determines X. * For example, if nbits is 3, we do base 8 conversion * Return value: * a pointer to a string containing the number * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) */ static char * conv_p2(register u_wide_int num, register int nbits, char format, char *buf_end, register int *len) { register int mask = (1 << nbits) - 1; register char *p = buf_end; static char low_digits[] = "0123456789abcdef"; static char upper_digits[] = "0123456789ABCDEF"; register char *digits = (format == 'X') ? upper_digits : low_digits; do { *--p = digits[num & mask]; num >>= nbits; } while (num); *len = buf_end - p; return (p); } /* * Do format conversion placing the output in buffer */ static int format_converter(register buffy * odp, const char *fmt, va_list ap) { register char *sp; register char *bep; register int cc = 0; register int i; register char *s = NULL; char *q; int s_len; register int min_width = 0; int precision = 0; enum { LEFT, RIGHT } adjust; char pad_char; char prefix_char; double fp_num; wide_int i_num = (wide_int) 0; u_wide_int ui_num; char num_buf[NUM_BUF_SIZE]; char char_buf[2]; /* for printing %% and % */ /* * Flag variables */ boolean_e is_long; boolean_e alternate_form; boolean_e print_sign; boolean_e print_blank; boolean_e adjust_precision; boolean_e adjust_width; bool_int is_negative; s_len=0; /* warning fix */ sp = odp->nextb; bep = odp->buf_end; while (*fmt) { if (*fmt != '%') { INS_CHAR(*fmt, sp, bep, cc); } else { /* * Default variable settings */ adjust = RIGHT; alternate_form = print_sign = print_blank = NO; pad_char = ' '; prefix_char = NUL; fmt++; /* * Try to avoid checking for flags, width or precision */ if (isascii((int)*fmt) && !islower((int)*fmt)) { /* * Recognize flags: -, #, BLANK, + */ for (;; fmt++) { if (*fmt == '-') adjust = LEFT; else if (*fmt == '+') print_sign = YES; else if (*fmt == '#') alternate_form = YES; else if (*fmt == ' ') print_blank = YES; else if (*fmt == '0') pad_char = '0'; else break; } /* * Check if a width was specified */ if (isdigit((int)*fmt)) { STR_TO_DEC(fmt, min_width); adjust_width = YES; } else if (*fmt == '*') { min_width = va_arg(ap, int); fmt++; adjust_width = YES; if (min_width < 0) { adjust = LEFT; min_width = -min_width; } } else adjust_width = NO; /* * Check if a precision was specified * * XXX: an unreasonable amount of precision may be specified * resulting in overflow of num_buf. Currently we * ignore this possibility. */ if (*fmt == '.') { adjust_precision = YES; fmt++; if (isdigit((int)*fmt)) { STR_TO_DEC(fmt, precision); } else if (*fmt == '*') { precision = va_arg(ap, int); fmt++; if (precision < 0) precision = 0; } else precision = 0; } else adjust_precision = NO; } else adjust_precision = adjust_width = NO; /* * Modifier check */ if (*fmt == 'l') { is_long = YES; fmt++; } else is_long = NO; /* * Argument extraction and printing. * First we determine the argument type. * Then, we convert the argument to a string. * On exit from the switch, s points to the string that * must be printed, s_len has the length of the string * The precision requirements, if any, are reflected in s_len. * * NOTE: pad_char may be set to '0' because of the 0 flag. * It is reset to ' ' by non-numeric formats */ switch (*fmt) { case 'u': if (is_long) i_num = va_arg(ap, u_wide_int); else i_num = (wide_int) va_arg(ap, unsigned int); /* * The rest also applies to other integer formats, so fall * into that case. */ case 'd': case 'i': /* * Get the arg if we haven't already. */ if ((*fmt) != 'u') { if (is_long) i_num = va_arg(ap, wide_int); else i_num = (wide_int) va_arg(ap, int); }; s = conv_10(i_num, (*fmt) == 'u', &is_negative, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); if (*fmt != 'u') { if (is_negative) prefix_char = '-'; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; } break; case 'o': if (is_long) ui_num = va_arg(ap, u_wide_int); else ui_num = (u_wide_int) va_arg(ap, unsigned int); s = conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); if (alternate_form && *s != '0') { *--s = '0'; s_len++; } break; case 'x': case 'X': if (is_long) ui_num = (u_wide_int) va_arg(ap, u_wide_int); else ui_num = (u_wide_int) va_arg(ap, unsigned int); s = conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); if (alternate_form && i_num != 0) { *--s = *fmt; /* 'x' or 'X' */ *--s = '0'; s_len += 2; } break; case 's': s = va_arg(ap, char *); if (s != NULL) { s_len = strlen(s); if (adjust_precision && precision < s_len) s_len = precision; } else { s = S_NULL; s_len = S_NULL_LEN; } pad_char = ' '; break; case 'f': case 'e': case 'E': fp_num = va_arg(ap, double); s = conv_fp(*fmt, fp_num, alternate_form, (adjust_precision == NO) ? FLOAT_DIGITS : precision, &is_negative, &num_buf[1], &s_len); if (is_negative) prefix_char = '-'; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; break; case 'g': case 'G': if (adjust_precision == NO) precision = FLOAT_DIGITS; else if (precision == 0) precision = 1; /* * * We use &num_buf[ 1 ], so that we have room for the sign */ s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1]); if (*s == '-') prefix_char = *s++; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; s_len = strlen(s); if (alternate_form && (q = strchr(s, '.')) == NULL) s[s_len++] = '.'; if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL) *q = 'E'; break; case 'c': char_buf[0] = (char) (va_arg(ap, int)); s = &char_buf[0]; s_len = 1; pad_char = ' '; break; case '%': char_buf[0] = '%'; s = &char_buf[0]; s_len = 1; pad_char = ' '; break; case 'n': *(va_arg(ap, int *)) = cc; break; /* * Always extract the argument as a "char *" pointer. We * should be using "void *" but there are still machines * that don't understand it. * If the pointer size is equal to the size of an unsigned * integer we convert the pointer to a hex number, otherwise * we print "%p" to indicate that we don't handle "%p". */ case 'p': ui_num = (u_wide_int) va_arg(ap, char *); if (sizeof(char *) <= sizeof(u_wide_int)) s = conv_p2(ui_num, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len); else { s = "%p"; s_len = 2; } pad_char = ' '; break; case NUL: /* * The last character of the format string was %. * We ignore it. */ continue; /* * The default case is for unrecognized %'s. * We print % to help the user identify what * option is not understood. * This is also useful in case the user wants to pass * the output of format_converter to another function * that understands some other % (like syslog). * Note that we can't point s inside fmt because the * unknown could be preceded by width etc. */ default: char_buf[0] = '%'; char_buf[1] = *fmt; s = char_buf; s_len = 2; pad_char = ' '; break; } if (prefix_char != NUL) { *--s = prefix_char; s_len++; } if (adjust_width && adjust == RIGHT && min_width > s_len) { if (pad_char == '0' && prefix_char != NUL) { INS_CHAR(*s, sp, bep, cc) s++; s_len--; min_width--; } PAD(min_width, s_len, pad_char); } /* * Print the string s. */ for (i = s_len; i != 0; i--) { INS_CHAR(*s, sp, bep, cc); s++; } if (adjust_width && adjust == LEFT && min_width > s_len) PAD(min_width, s_len, pad_char); } fmt++; } odp->nextb = sp; return (cc); } /* * This is the general purpose conversion function. */ static void strx_printv(int *ccp, char *buf, size_t len, const char *format, va_list ap) { buffy od; int cc; /* * First initialize the descriptor * Notice that if no length is given, we initialize buf_end to the * highest possible address. */ od.buf_end = len ? &buf[len] : (char *) ~0; od.nextb = buf; /* * Do the conversion */ cc = format_converter(&od, format, ap); if (len == 0 || od.nextb <= od.buf_end) *(od.nextb) = '\0'; if (ccp) *ccp = cc; } int ap_snprintf(char *buf, size_t len, const char *format,...) { int cc; va_list ap; va_start(ap, format); strx_printv(&cc, buf, (len - 1), format, ap); va_end(ap); return (cc); } int ap_vsnprintf(char *buf, size_t len, const char *format, va_list ap) { int cc; strx_printv(&cc, buf, (len - 1), format, ap); return (cc); } #endif /* HAVE_SNPRINTF */ kamailio-4.0.4/obsolete/jabber_s/xjab_util.c0000644000000000000000000001457112223032460017515 0ustar rootroot/* * $Id$ * * eXtended JABber module - Jabber connections pool * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../timer.h" #include "xjab_util.h" #include "xjab_jcon.h" #include "mdefines.h" /** * init a jc_pool structure * - size : maximum number of the open connection to Jabber * - jlen : maximum size of messages queue * #return : pointer to the structure or NULL on error */ xj_jcon_pool xj_jcon_pool_init(int size, int jlen, int ch) { xj_jcon_pool jcp = (xj_jcon_pool)_M_MALLOC(sizeof(t_xj_jcon_pool)); if(jcp == NULL) return NULL; jcp->len = size; jcp->ojc = (xj_jcon*)_M_MALLOC(size*sizeof(xj_jcon)); if(jcp->ojc == NULL) { _M_FREE(jcp); return NULL; } memset( jcp->ojc , 0, size*sizeof(xj_jcon) ); jcp->jmqueue.len = jlen; jcp->jmqueue.size = 0; jcp->jmqueue.cache = (ch>0)?ch:90; jcp->jmqueue.expire = (int*)_M_MALLOC(jlen*sizeof(int)); if(jcp->jmqueue.expire == NULL) { _M_FREE(jcp->ojc); _M_FREE(jcp); return NULL; } jcp->jmqueue.jsm=(xj_sipmsg*)_M_MALLOC(jlen*sizeof(xj_sipmsg)); if(jcp->jmqueue.jsm == NULL) { _M_FREE(jcp->jmqueue.expire); _M_FREE(jcp->ojc); _M_FREE(jcp); return NULL; } jcp->jmqueue.ojc = (xj_jcon*)_M_MALLOC(jlen*sizeof(xj_jcon)); if(jcp->jmqueue.ojc == NULL) { _M_FREE(jcp->jmqueue.expire); _M_FREE(jcp->jmqueue.jsm); _M_FREE(jcp->ojc); _M_FREE(jcp); return NULL; } memset( jcp->jmqueue.expire , 0, jlen*sizeof(int) ); memset( jcp->jmqueue.jsm , 0, jlen*sizeof(xj_sipmsg) ); memset( jcp->jmqueue.ojc , 0, jlen*sizeof(xj_jcon) ); return jcp; } /** * add a new element in messages queue * - jcp : pointer to the Jabber connections pool structure * - _jsm : pointer to the message * - _ojc : pointer to the Jabber connection that will be used for this message * #return : 0 on success or <0 on error */ int xj_jcon_pool_add_jmsg(xj_jcon_pool jcp, xj_sipmsg _jsm, xj_jcon _ojc) { int i; if(jcp == NULL) return -1; if(jcp->jmqueue.size == jcp->jmqueue.len) return -2; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_pool_add_jmsg: add msg into the pool\n"); #endif for(i = 0; ijmqueue.len; i++) { if(jcp->jmqueue.jsm[i] == NULL || jcp->jmqueue.ojc[i] == NULL) { jcp->jmqueue.size++; jcp->jmqueue.expire[i] = get_ticks() + jcp->jmqueue.cache; jcp->jmqueue.jsm[i] = _jsm; jcp->jmqueue.ojc[i] = _ojc; return 0; } } return -2; } /** * delete first element from messages queue * - jcp : pointer to the Jabber connections pool structure * #return : 0 on success or <0 on error */ int xj_jcon_pool_del_jmsg(xj_jcon_pool jcp, int idx) { if(jcp == NULL) return -1; if(jcp->jmqueue.size <= 0) return -2; jcp->jmqueue.size--; jcp->jmqueue.jsm[idx] = NULL; jcp->jmqueue.ojc[idx] = NULL; return 0; } /** * add a new connection in pool * - jcp : pointer to the Jabber connections pool structure * #return : 0 on success or <0 on error */ int xj_jcon_pool_add(xj_jcon_pool jcp, xj_jcon jc) { int i = 0; if(jcp == NULL) return -1; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_pool_add: add connection into the pool\n"); #endif while(i < jcp->len && jcp->ojc[i] != NULL) i++; if(i >= jcp->len) return -1; jcp->ojc[i] = jc; return 0; } /** * get the jabber connection associated with 'id' * - jcp : pointer to the Jabber connections pool structure * - id : id of the Jabber connection * #return : pointer to the open connection to Jabber structure or NULL on error */ xj_jcon xj_jcon_pool_get(xj_jcon_pool jcp, xj_jkey jkey) { int i = 0; xj_jcon _ojc; if(jcp==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return NULL; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_pool_get: looking for the connection of <%.*s>" " into the pool\n", jkey->id->len, jkey->id->s); #endif while(i < jcp->len) { if((jcp->ojc[i]!=NULL) && jcp->ojc[i]->jkey->hash==jkey->hash && (!strncmp(jcp->ojc[i]->jkey->id->s, jkey->id->s, jkey->id->len))) { _ojc = jcp->ojc[i]; //jcp->ojc[i] = NULL; return _ojc; } i++; } return NULL; } /** * remove the connection associated with 'id' from pool * - jcp : pointer to the Jabber connections pool structure * - id : id of the Jabber connection * #return : 0 on success or <0 on error */ int xj_jcon_pool_del(xj_jcon_pool jcp, xj_jkey jkey) { int i = 0; if(jcp==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_pool_del: removing a connection from the pool\n"); #endif while(i < jcp->len) { if((jcp->ojc[i]!=NULL) && jcp->ojc[i]->jkey->hash==jkey->hash && (!strncmp(jcp->ojc[i]->jkey->id->s,jkey->id->s,jkey->id->len))) { xj_jcon_free(jcp->ojc[i]); jcp->ojc[i] = NULL; break; } i++; } return 0; } /** * free a Jabber connections pool structure * - jcp : pointer to the Jabber connections pool structure */ void xj_jcon_pool_free(xj_jcon_pool jcp) { int i; if(jcp == NULL) return; #ifdef XJ_EXTRA_DEBUG DBG("XJAB:xj_jcon_pool_free: -----START-----\n"); #endif if(jcp->ojc != NULL) { for(i=0; ilen; i++) { if(jcp->ojc[i] != NULL) xj_jcon_free(jcp->ojc[i]); } _M_FREE(jcp->ojc); } if(jcp->jmqueue.jsm != NULL) _M_FREE(jcp->jmqueue.jsm); if(jcp->jmqueue.ojc != NULL) _M_FREE(jcp->jmqueue.ojc); if(jcp->jmqueue.expire != NULL) _M_FREE(jcp->jmqueue.expire); _M_FREE(jcp); } kamailio-4.0.4/obsolete/jabber_s/Makefile0000644000000000000000000000065612223032460017027 0ustar rootroot# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=jabber.so # extra debug messages DEFS+=-DXJ_EXTRA_DEBUG # -DHAVE_IHTTP # expat.h location DEFS+=-I$(LOCALBASE)/include LIBS=-L$(LOCALBASE)/lib -lexpat DEFS+=-DSER_MOD_INTERFACE SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/jabber_s/mdefines.h0000644000000000000000000000323312223032460017324 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*************************************************************************** mdefines.h - description ------------------- author : Daniel-Constantin MIERLA email : mierla@fokus.fhg.de organization : FhI FOKUS, BERLIN ***************************************************************************/ #ifndef _mdefines_h_ #define _mdefines_h_ #define _M_PRINTF printf #define _M_CALLOC calloc #define _M_REALLOC realloc #define _M_MALLOC pkg_malloc #define _M_FREE pkg_free #define _M_SHM_MALLOC shm_malloc #define _M_SHM_FREE shm_free #endif kamailio-4.0.4/obsolete/uri_radius/0000755000000000000000000000000012223032460015757 5ustar rootrootkamailio-4.0.4/obsolete/uri_radius/checks.c0000644000000000000000000001010512223032460017360 0ustar rootroot/* checks.c v 0.1 2003/1/20 * * Radius based checks * * Copyright (C) 2002-2003 Juha Heinanen * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-03-11: Code cleanup (janakj) */ #include #include "../../mem/mem.h" #include "../../parser/parse_uri.h" #include "../../dprint.h" #include "../../usr_avp.h" #include "../../ut.h" #include "../../rad_dict.h" #include "checks.h" #include "urirad_mod.h" #ifdef RADIUSCLIENT_NG_4 # include #else # include #endif /* * Split name:value into string name and string value */ static void attr_name_value(VALUE_PAIR* vp, str* name, str* value) { int i; for (i = 0; i < vp->lvalue; i++) { if (vp->strvalue[i] == ':') { name->s = vp->strvalue; name->len = i; if (i == (vp->lvalue - 1)) { value->s = (char*)0; value->len = 0; } else { value->s = vp->strvalue + i + 1; value->len = vp->lvalue - i - 1; } return; } } name->len = value->len = 0; name->s = value->s = (char*)0; } /* * Generate AVPs from the database result */ static int generate_avps(VALUE_PAIR* received) { int_str name, val; VALUE_PAIR *vp; vp = received; while ((vp = rc_avpair_get(vp, attrs[A_SER_ATTR].v, 0))) { attr_name_value(vp, &name.s, &val.s); if (add_avp(AVP_NAME_STR | AVP_VAL_STR, name, val) < 0) { LOG(L_ERR, "generate_avps: Unable to create a new AVP\n"); } else { DBG("generate_avps: AVP '%.*s'='%.*s' has been added\n", name.s.len, ZSW(name.s.s), val.s.len, ZSW(val.s.s)); } vp = vp->next; } return 0; } /* * Check from Radius if request URI belongs to a local user. * User-Name is user@host of request Uri and Service-Type is Call-Check. */ int radius_does_uri_exist(struct sip_msg* _m, char* _s1, char* _s2) { static char msg[4096]; VALUE_PAIR *send, *received; UINT4 service; char* at, *uri; send = received = 0; if (parse_sip_msg_uri(_m) < 0) { LOG(L_ERR, "radius_does_uri_exist(): Error while parsing URI\n"); return -1; } uri = (char*)pkg_malloc(_m->parsed_uri.user.len + _m->parsed_uri.host.len + 2); if (!uri) { LOG(L_ERR, "radius_does_uri_exist(): No memory left\n"); return -2; } at = uri; memcpy(at, _m->parsed_uri.user.s, _m->parsed_uri.user.len); at += _m->parsed_uri.user.len; *at = '@'; at++; memcpy(at , _m->parsed_uri.host.s, _m->parsed_uri.host.len); at += _m->parsed_uri.host.len; *at = '\0'; if (!rc_avpair_add(rh, &send, attrs[A_USER_NAME].v, uri, -1, 0)) { LOG(L_ERR, "radius_does_uri_exist(): Error adding User-Name\n"); rc_avpair_free(send); pkg_free(uri); return -3; } service = vals[V_CALL_CHECK].v; if (!rc_avpair_add(rh, &send, attrs[A_SERVICE_TYPE].v, &service, -1, 0)) { LOG(L_ERR, "radius_does_uri_exist(): Error adding service type\n"); rc_avpair_free(send); pkg_free(uri); return -4; } if (rc_auth(rh, 0, send, &received, msg) == OK_RC) { DBG("radius_does_uri_exist(): Success\n"); rc_avpair_free(send); generate_avps(received); rc_avpair_free(received); pkg_free(uri); return 1; } else { DBG("radius_does_uri_exist(): Failure\n"); rc_avpair_free(send); rc_avpair_free(received); pkg_free(uri); return -5; } } kamailio-4.0.4/obsolete/uri_radius/doc/0000755000000000000000000000000012223032460016524 5ustar rootrootkamailio-4.0.4/obsolete/uri_radius/doc/functions.xml0000644000000000000000000000160712223032460021262 0ustar rootroot
Functions
<function>radius_does_uri_exist()</function> Checks from Radius if user@host in Request-URI is a local user. Can be used to decide if 404 or 480 should be returned after lookup has failed. Adds SIP-AVP reply items, that must have a string value of form "name:value", as AVPs. <function>radius_does_uri_exist</function> usage ... if (radius_does_uri_exist()) { ... }; ...
kamailio-4.0.4/obsolete/uri_radius/doc/uri_radius.xml0000644000000000000000000000224112223032460021413 0ustar rootroot
Juha Heinanen Song Networks
jh@tutpro.com
2003 Juha Heinanen
Uri_radius Module
Overview URI check using Radius server.
Dependencies The following libraries or applications must be installed before running SER with this module loaded: radius client library.
kamailio-4.0.4/obsolete/uri_radius/doc/Makefile0000644000000000000000000000013312223032460020161 0ustar rootrootdocs = uri_radius.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/uri_radius/doc/params.xml0000644000000000000000000000233712223032460020536 0ustar rootroot
Parameters
<varname>radius_config</varname> (string) Radiusclient configuration file. Default value is "/usr/local/etc/radiusclient/radiusclient.conf". Set <varname>param_name</varname> parameter ... modparam("uri_radius", "radius_config", "/etc/radiusclient.conf") ...
<varname>service_type</varname> (integer) Radius service type used in radius_does_uri_exist check. Default value is 10 (Call-Check). Set <varname>param_name</varname> parameter ... modparam("uri_radius", "service_type", 11) ...
kamailio-4.0.4/obsolete/uri_radius/README0000644000000000000000000000264012223032460016641 0ustar rootroot1. Uri_radius Module Juha Heinanen Song Networks Copyright © 2003 Juha Heinanen __________________________________________________________________ 1.1. Overview 1.2. Dependencies 1.3. Parameters 1.3.1. radius_config (string) 1.3.2. service_type (integer) 1.4. Functions 1.4.1. radius_does_uri_exist() 1.1. Overview URI check using Radius server. 1.2. Dependencies The following libraries or applications must be installed before running SER with this module loaded: * radius client library. 1.3. Parameters 1.3.1. radius_config (string) Radiusclient configuration file. Default value is "/usr/local/etc/radiusclient/radiusclient.conf". Example 1. Set param_name parameter ... modparam("uri_radius", "radius_config", "/etc/radiusclient.conf") ... 1.3.2. service_type (integer) Radius service type used in radius_does_uri_exist check. Default value is 10 (Call-Check). Example 2. Set param_name parameter ... modparam("uri_radius", "service_type", 11) ... 1.4. Functions 1.4.1. radius_does_uri_exist() Checks from Radius if user@host in Request-URI is a local user. Can be used to decide if 404 or 480 should be returned after lookup has failed. Adds SIP-AVP reply items, that must have a string value of form "name:value", as AVPs. Example 3. radius_does_uri_exist usage ... if (radius_does_uri_exist()) { ... }; ... kamailio-4.0.4/obsolete/uri_radius/checks.h0000644000000000000000000000245112223032460017372 0ustar rootroot/* domain.h v 0.1 2003/1/20 * * Header file for radius based checks * * Copyright (C) 2002-2003 Juha Heinanen * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CHECKS_H #define CHECKS_H #include "../../parser/msg_parser.h" /* * Check from radius if Request URI exists */ int radius_does_uri_exist(struct sip_msg* _msg, char* _str1, char* _str2); #endif /* CHECKS_H */ kamailio-4.0.4/obsolete/uri_radius/urirad_mod.c0000644000000000000000000000606012223032460020252 0ustar rootroot/* * $Id$ * * URI checks using Radius * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2003-03-15 - created by janakj * 2003-03-16 - flags export parameter added (janakj) */ #include "../../dprint.h" #include "../../sr_module.h" #include "urirad_mod.h" #include "checks.h" #include "../../rad_dict.h" #ifdef RADIUSCLIENT_NG_4 # include #else # include #endif MODULE_VERSION struct attr attrs[A_MAX]; struct val vals[V_MAX]; void *rh; static int mod_init(void); /* Module initialization function */ /* * Module parameter variables */ static char* radius_config = "/usr/local/etc/radiusclient/radiusclient.conf"; static int service_type = -1; /* * Exported functions */ static cmd_export_t cmds[] = { {"radius_does_uri_exist", radius_does_uri_exist, 0, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"radius_config", PARAM_STRING, &radius_config}, {"service_type", PARAM_INT, &service_type}, {0, 0, 0} }; /* * Module interface */ struct module_exports exports = { "uri_radius", cmds, /* Exported functions */ 0, /* RPC methods */ params, /* Exported parameters */ mod_init, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0, /* oncancel function */ 0 /* child initialization function */ }; static int mod_init(void) { DBG("uri_radius - initializing\n"); memset(attrs, 0, sizeof(attrs)); memset(vals, 0, sizeof(vals)); attrs[A_USER_NAME].n = "User-Name"; attrs[A_SERVICE_TYPE].n = "Service-Type"; attrs[A_SER_ATTR].n = "SER-Attrs"; vals[V_CALL_CHECK].n = "Call-Check"; if ((rh = rc_read_config(radius_config)) == NULL) { LOG(L_ERR, "uri_radius: Error opening configuration file \n"); return -1; } if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary")) != 0) { LOG(L_ERR, "uri_radius: Error opening dictionary file \n"); return -2; } INIT_AV(rh, attrs, vals, "uri_radius", -3, -4); if (service_type != -1) vals[V_CALL_CHECK].v = service_type; return 0; } kamailio-4.0.4/obsolete/uri_radius/urirad_mod.h0000644000000000000000000000243612223032460020262 0ustar rootroot/* * $Id$ * * URI checks using Radius * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2003-03-15 - Created by janakj */ #ifndef URIRAD_MOD_H #define URIRAD_MOD_H #include "../../rad_dict.h" /* attr & val */ extern struct attr attrs[]; extern struct val vals[]; extern void *rh; #endif /* URIRAD_MOD_H */ kamailio-4.0.4/obsolete/uri_radius/Makefile0000644000000000000000000000041112223032460017413 0ustar rootroot# $Id$ # # URI checks using Radius # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs include ../../Makefile.radius auto_gen= NAME=uri_radius.so DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/dialog/0000755000000000000000000000000012223032460015050 5ustar rootrootkamailio-4.0.4/obsolete/dialog/dlg_mod.c0000644000000000000000000000636712223032460016635 0ustar rootroot/* * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "dlg_mod.h" #include "db_dlg.h" #include "serialize_dlg.h" #include "../../sr_module.h" #include "../../modules/tm/tm_load.h" #include #include "dlg_utils.h" #include "dlg_request.h" #include "../../locking.h" MODULE_VERSION /* "public" data members */ static int db_mode = 0; static str db_url = STR_NULL; /* internal data members */ /* data members for pregenerated tags - taken from TM */ char dialog_tags[TOTAG_VALUE_LEN]; char *dialog_tag_suffix = NULL; struct tm_binds tmb; static int dlg_mod_init(void); static void dlg_mod_destroy(void); static int dlg_mod_child_init(int _rank); /* * Exported functions */ static cmd_export_t cmds[]={ {"bind_dlg_mod", (cmd_function)bind_dlg_mod, -1, 0, 0}, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[]={ {"db_mode", PARAM_INT, &db_mode }, {"db_url", PARAM_STR, &db_url }, {0, 0, 0} }; struct module_exports exports = { "dialog", cmds, /* Exported functions */ 0, /* RPC methods */ params, /* Exported parameters */ dlg_mod_init, /* module initialization function */ 0, /* response function*/ dlg_mod_destroy, /* destroy function */ 0, /* oncancel function */ dlg_mod_child_init/* per-child init function */ }; static void init_dialog_tags() { /* taken from tm, might be useful */ init_tags(dialog_tags, &dialog_tag_suffix, "SER-DIALOG/tags", '-'); } #include gen_lock_t *dlg_mutex = NULL; static int init_dialog_mutex() { dlg_mutex = (gen_lock_t*)shm_malloc(sizeof(*dlg_mutex)); if (!dlg_mutex) return -1; lock_init(dlg_mutex); return 0; } static void destroy_dialog_mutex() { if (dlg_mutex) { lock_destroy(dlg_mutex); shm_free((void*)dlg_mutex); } } static int dlg_mod_init(void) { load_tm_f load_tm; if (init_dialog_mutex() < 0) { ERR("can't initialize mutex\n"); return -1; } init_dialog_tags(); load_tm = (load_tm_f)find_export("load_tm", NO_SCRIPT, 0); if (!load_tm) { LOG(L_ERR, "dlg_mod_init(): Can't import tm\n"); return -1; } if (load_tm(&tmb) < 0) { LOG(L_ERR, "dlg_mod_init(): Can't import tm functions\n"); return -1; } return 0; } static int dlg_mod_child_init(int _rank) { return 0; } static void dlg_mod_destroy(void) { destroy_dialog_mutex(); } kamailio-4.0.4/obsolete/dialog/db_dlg.h0000644000000000000000000000237112223032460016437 0ustar rootroot/* * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __DB_DLG_H #define __DB_DLG_H #include "../../modules/tm/dlg.h" #include "../../lib/srdb2/db.h" #include int db_store_dlg(db_con_t* conn, dlg_t *dlg, str *dst_id); int db_load_dlg(db_con_t* conn, str *id, dlg_t **dst_dlg); #endif kamailio-4.0.4/obsolete/dialog/serialize_dlg.h0000644000000000000000000000236712223032460020046 0ustar rootroot/* * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __SERIALIZE_DLG_H #define __SERIALIZE_DLG_H #include "../../modules/tm/dlg.h" #include int serialize_dlg(sstream_t *ss, dlg_t *dlg); int dlg2str(dlg_t *dlg, str *dst_str); int str2dlg(const str *s, dlg_t *dst_dlg); #endif kamailio-4.0.4/obsolete/dialog/dlg_utils.c0000644000000000000000000000416212223032460017205 0ustar rootroot#include "../../parser/parse_rr.h" #include "dlg_mod_internal.h" #include "dlg_utils.h" #include "dlg_request.h" #include "serialize_dlg.h" #include /* #include */ int preset_dialog_route(dlg_t* dialog, str *route) { rr_t *old_r, *r = NULL; int res; /* check parameters */ if ((!dialog) || (is_str_empty(route))) { ERR("bad parameters\n"); return -1; } if (dialog->state != DLG_NEW) { ERR("Dialog is not in DLG_NEW state\n"); return -1; } if (parse_rr_body(route->s, route->len, &r) < 0) { ERR("can't parse given route\n"); return -1; } if (!r) { ERR("empty route\n"); return -1; } old_r = dialog->route_set; dialog->route_set = NULL; res = shm_duplicate_rr(&dialog->route_set, r); if (r) free_rr(&r); if (res < 0) { /* return old routeset to its place */ dialog->route_set = old_r; ERR("can't duplicate route\n"); return -1; } /* free old route */ if (old_r) shm_free_rr(&old_r); res = tmb.calculate_hooks(dialog); if (res < 0) { ERR("Error while calculating hooks\n"); return -2; } return 0; } int bind_dlg_mod(dlg_func_t *dst) { if (!dst) return -1; /* dst->db_store = db_store_dlg; dst->db_load = db_load_dlg;*/ memset(dst, 0, sizeof(*dst)); dst->serialize = serialize_dlg; dst->dlg2str = dlg2str; dst->str2dlg = str2dlg; dst->preset_dialog_route = preset_dialog_route; dst->request_outside = request_outside; dst->request_inside = request_inside; dst->hash_dlg_id = hash_dlg_id; dst->cmp_dlg_ids = cmp_dlg_ids; return 0; } int cmp_dlg_ids(dlg_id_t *a, dlg_id_t *b) { if (!a) { if (!b) return -1; else return 0; } if (!b) return 1; if (str_case_equals(&a->call_id, &b->call_id) != 0) return 1; if (str_case_equals(&a->rem_tag, &b->rem_tag) != 0) return 1; /* case sensitive ? */ if (str_case_equals(&a->loc_tag, &b->loc_tag) != 0) return 1; /* case sensitive ? */ return 0; } unsigned int hash_dlg_id(dlg_id_t *id) { char tmp[512]; int len; if (!id) return 0; len = snprintf(tmp, sizeof(tmp), "%.*s%.*s%.*s", FMT_STR(id->call_id), FMT_STR(id->rem_tag), FMT_STR(id->loc_tag)); return rshash(tmp, len); } kamailio-4.0.4/obsolete/dialog/dlg_utils.h0000644000000000000000000000036512223032460017213 0ustar rootroot#ifndef __DLG_UTILS_H #define __DLG_UTILS_H #include "dlg_mod.h" int preset_dialog_route(dlg_t* dialog, str *route); int bind_dlg_mod(dlg_func_t *dst); int cmp_dlg_ids(dlg_id_t *a, dlg_id_t *b); unsigned int hash_dlg_id(dlg_id_t *id); #endif kamailio-4.0.4/obsolete/dialog/dlg_mod_internal.h0000644000000000000000000000261312223032460020524 0ustar rootroot/* * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __DLG_MOD_INTERNAL_H #define __DLG_MOD_INTERNAL_H #include "../../modules/tm/tm_load.h" #include "dlg_mod.h" extern struct tm_binds tmb; /* data members for pregenerated tags - taken from TM */ extern char *dialog_tags; extern char *dialog_tag_suffix; extern gen_lock_t *dlg_mutex; #define lock_dialog_mod() lock_get(dlg_mutex) #define unlock_dialog_mod() lock_release(dlg_mutex) #endif kamailio-4.0.4/obsolete/dialog/dlg_request.h0000644000000000000000000000052512223032460017541 0ustar rootroot#ifndef __DIALOG_REQUEST_H #define __DIALOG_REQUEST_H #include "dlg_mod.h" #include "../../modules/tm/t_hooks.h" int request_outside(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb cb, void* cbp); int request_inside(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb completion_cb, void* cbp); #endif kamailio-4.0.4/obsolete/dialog/db_dlg.c0000644000000000000000000000351312223032460016431 0ustar rootroot/* * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "dlg_mod_internal.h" #include "db_dlg.h" #include #include /*****************************************************************/ /* static void trace_dlg(const char *s, dlg_t *d) { rr_t *r; TRACE_LOG("%s: callid = %.*s \nrem tag = %.*s \nloc tag = %.*s\n" "loc uri = %.*s\n rem uri = %.*s\n rem target = %.*s\n", s, FMT_STR(d->id.call_id), FMT_STR(d->id.rem_tag), FMT_STR(d->id.loc_tag), FMT_STR(d->loc_uri), FMT_STR(d->rem_uri), FMT_STR(d->rem_target) ); r = d->route_set; while (r) { TRACE_LOG(" ... name = %.*s\n uri = %.*s", FMT_STR(r->nameaddr.name), FMT_STR(r->nameaddr.uri)); r = r->next; } }*/ int db_store_dlg(db_con_t* conn, dlg_t *dlg, str *dst_id) { return -1; } int db_load_dlg(db_con_t* conn, str *id, dlg_t **dst_dialog) { return -1; } kamailio-4.0.4/obsolete/dialog/dlg_request.c0000644000000000000000000000360112223032460017532 0ustar rootroot#include "dlg_request.h" #include "dlg_mod_internal.h" /* * Send an initial request that will start a dialog with given route header * the dialog must be created before!!! */ int request_outside(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb cb, void* cbp) { uac_req_t uac_r; /* check parameters */ if ((!dialog) || (!method)) goto err; if ((method->len < 0) || (!method->s)) goto err; if (dialog->state != DLG_NEW) { LOG(L_ERR, "req_within: Dialog is not in DLG_NEW state\n"); goto err; } if (!dialog->hooks.next_hop) { /* FIXME: this is only experimental - hooks are calculated only when * next hop is not known */ if (tmb.calculate_hooks(dialog) < 0) { LOG(L_ERR, "Error while calculating hooks\n"); return -2; } } set_uac_req(&uac_r, method, headers, body, dialog, TMCB_LOCAL_COMPLETED, cb, cbp); return tmb.t_uac(&uac_r); err: /* if (cbp) shm_free(cbp);*/ /* !!! never do this automaticaly??? !!! */ /* call the callback? Probably not because we can be in locked section * and the callback can try to lock it too. */ return -1; } /* * Send a message within a dialog */ int request_inside(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb completion_cb, void* cbp) { uac_req_t uac_r; if (!method || !dialog) { LOG(L_ERR, "req_within: Invalid parameter value\n"); goto err; } if (dialog->state != DLG_CONFIRMED) { LOG(L_ERR, "req_within: Dialog is not confirmed yet\n"); goto err; } if ((method->len == 3) && (!memcmp("ACK", method->s, 3))) goto send; if ((method->len == 6) && (!memcmp("CANCEL", method->s, 6))) goto send; dialog->loc_seq.value++; /* Increment CSeq */ send: set_uac_req(&uac_r, method, headers, body, dialog, TMCB_LOCAL_COMPLETED, completion_cb, cbp); return tmb.t_uac(&uac_r); err: /* if (cbp) shm_free(cbp); */ /* !!! never !!! */ return -1; } kamailio-4.0.4/obsolete/dialog/serialize_dlg.c0000644000000000000000000002204312223032460020032 0ustar rootroot/* * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "dlg_mod_internal.h" #include "serialize_dlg.h" #include #include #include static int serialize_dlg_state(sstream_t *ss, dlg_state_t *state) { int i = -1; if (is_input_sstream(ss)) { /* read state */ if (serialize_int(ss, &i) != 0) return -1; switch (i) { case 0: *state = DLG_NEW; break; case 1: *state = DLG_EARLY; break; case 2: *state = DLG_CONFIRMED; break; case 3: *state = DLG_DESTROYED; break; default: ERROR_LOG("deserializing unknow dialog state (%d)!\n", i); return -1; /* unknown dialog state */ } } else { /* store state */ switch (*state) { case DLG_NEW: i = 0; break; case DLG_EARLY: i = 1; break; case DLG_CONFIRMED: i = 2; break; case DLG_DESTROYED: i = 3; break; } if (i == -1) { WARN_LOG("serializing unknow dialog state (probably unloadable!)\n"); } serialize_int(ss, &i); } return 0; } #if 0 static int serialize_ptype(sstream_t *ss, ptype_t *type) { int i = -1; if (is_input_sstream(ss)) { /* read ptype */ if (serialize_int(ss, &i) != 0) return -1; switch (i) { case 0: *type = P_OTHER; break; case 1: *type = P_Q; break; case 2: *type = P_EXPIRES; break; case 3: *type = P_METHOD; break; case 4: *type = P_RECEIVED; break; case 5: *type = P_TRANSPORT; break; case 6: *type = P_LR; break; case 7: *type = P_R2; break; case 8: *type = P_MADDR; break; case 9: *type = P_TTL; break; case 10: *type = P_DSTIP; break; case 11: *type = P_DSTPORT; break; case 12: *type = P_INSTANCE; break; default: ERROR_LOG("deserializing unknow ptype (%d)!\n", i); return -1; } } else { /* store ptype */ switch (*type) { case P_OTHER: i = 0; break; case P_Q: i = 1; break; case P_EXPIRES: i = 2; break; case P_METHOD: i = 3; break; case P_RECEIVED: i = 4; break; case P_TRANSPORT: i = 5; break; case P_LR: i = 6; break; case P_R2: i = 7; break; case P_MADDR: i = 8; break; case P_TTL: i = 9; break; case P_DSTIP: i = 10; break; case P_DSTPORT: i = 11; break; case P_INSTANCE: i = 12; break; } if (i == -1) { WARN_LOG("serializing unknow ptype (probably unloadable!)\n"); } serialize_int(ss, &i); } return 0; } /*static int serialize_param(sstream_t *ss, rr_t **_r) { int do_it = 0; int res = 0; if (is_input_sstream(ss)) { / * read route * / if (serialize_int(ss, &do_it) != 0) return -1; if (!do_it) *_r = NULL; else { *_r = (rr_t*)cds_malloc(sizeof(rr_t)); if (!*_r) { ERROR_LOG("serialize_route(): can't allocate memory\n"); return -1; } (*_r)->r2 = NULL; (*_r)->params = NULL; (*_r)->next = NULL; } } else { / * store route * / if (*_r) do_it = 1; else do_it = 0; if (serialize_int(ss, &do_it) != 0) return -1; } if (do_it) { rr_t *r = *_r; res = serialize_str(ss, &r->nameaddr.name) | res; res = serialize_str(ss, &r->nameaddr.uri) | res; res = serialize_int(ss, &r->nameaddr.len) | res; res = serialize_params(ss, &r->nameaddr.params) | res; res = serialize_int(ss, &r->nameaddr.len) | res; } return res; }*/ static int serialize_param(sstream_t *ss, param_t *p) { int res = 0; res = serialize_ptype(ss, &p->type) | res; res = serialize_str(ss, &p->name) | res; res = serialize_str(ss, &p->body) | res; res = serialize_int(ss, &p->len) | res; return res; } static int serialize_params(sstream_t *ss, param_t **params) { if (is_input_sstream(ss)) { /* read */ int i = 0; param_t *p, *last = NULL; *params = NULL; if (serialize_int(ss, &i) != 0) return -1; /* can't read "terminator" */ while (i) { p = (param_t *)cds_malloc(sizeof(param_t)); if (!p) { ERROR_LOG("serialize_params(): can't allocate memory\n"); return -1; } p->next = NULL; if (last) last->next = p; else *params = p; last = p; if (serialize_param(ss, p) != 0) return -1; if (serialize_int(ss, &i) != 0) return -1; /* can't read "terminator" */ } } else { /* store */ param_t *p = *params; int i = 1; while (p) { if (serialize_int(ss, &i) != 0) return -1; /* params terminator ! */ if (serialize_param(ss, p) != 0) return -1; p = p->next; } i = 0; if (serialize_int(ss, &i) != 0) return -1; /* params terminator ! */ } return 0; } static int serialize_route_ex(sstream_t *ss, rr_t **_r) { int do_it = 0; int res = 0; if (is_input_sstream(ss)) { /* read route */ if (serialize_int(ss, &do_it) != 0) return -1; if (!do_it) *_r = NULL; else { *_r = (rr_t*)cds_malloc(sizeof(rr_t)); if (!*_r) { ERROR_LOG("serialize_route(): can't allocate memory\n"); return -1; } (*_r)->r2 = NULL; (*_r)->params = NULL; (*_r)->next = NULL; } } else { /* store route */ if (*_r) do_it = 1; else do_it = 0; if (serialize_int(ss, &do_it) != 0) return -1; } if (do_it) { rr_t *r = *_r; str s = { r->nameaddr.name.s, r->len }; int delta = r->nameaddr.uri.s - r->nameaddr.name.s; res = serialize_str(ss, &s) | res; res = serialize_int(ss, &r->nameaddr.name.len) | res; res = serialize_int(ss, &r->nameaddr.uri.len) | res; res = serialize_int(ss, &r->nameaddr.len) | res; res = serialize_int(ss, &delta) | res; if (is_input_sstream(ss)) { r->nameaddr.name.s = s.s; r->nameaddr.uri.s = s.s + delta; } /* !!! optimalized strings - use carefuly !!! */ /*res = serialize_str(ss, &r->nameaddr.name) | res; res = serialize_str(ss, &r->nameaddr.uri) | res; res = serialize_int(ss, &r->nameaddr.len) | res;*/ /* ??? res = serialize_r2(ss, &r->nameaddr.params) | res; ??? */ res = serialize_params(ss, &r->params) | res; res = serialize_int(ss, &r->len) | res; TRACE_LOG("ROUTE: rlen=%d name=%.*s, uri=%.*s\n len=%d WHOLE=%.*s\n", r->len, FMT_STR(r->nameaddr.name), FMT_STR(r->nameaddr.uri), r->nameaddr.len, r->nameaddr.len, r->nameaddr.name.s ); } return res; } #endif /*static void trace_dlg(const char *s, dlg_t *d) { rr_t *r; TRACE_LOG("%s: callid = %.*s \nrem tag = %.*s \nloc tag = %.*s\n" "loc uri = %.*s\n rem uri = %.*s\n rem target = %.*s\n" "loc_cseq = %d rem_cseq = %d\n", s, FMT_STR(d->id.call_id), FMT_STR(d->id.rem_tag), FMT_STR(d->id.loc_tag), FMT_STR(d->loc_uri), FMT_STR(d->rem_uri), FMT_STR(d->rem_target), d->loc_seq.value, d->rem_seq.value ); r = d->route_set; while (r) { TRACE_LOG(" ... name = %.*s\n uri = %.*s", FMT_STR(r->nameaddr.name), FMT_STR(r->nameaddr.uri)); r = r->next; } }*/ int serialize_dlg(sstream_t *ss, dlg_t *dlg) { int res = 0; if (is_input_sstream(ss)) { memset(dlg, 0, sizeof(*dlg)); } res = serialize_str(ss, &dlg->id.call_id) | res; res = serialize_str(ss, &dlg->id.rem_tag) | res; res = serialize_str(ss, &dlg->id.loc_tag) | res; res = serialize_uint(ss, &dlg->loc_seq.value) | res; res = serialize_uchar(ss, &dlg->loc_seq.is_set) | res; res = serialize_uint(ss, &dlg->rem_seq.value) | res; res = serialize_uchar(ss, &dlg->rem_seq.is_set) | res; res = serialize_str(ss, &dlg->loc_uri) | res; res = serialize_str(ss, &dlg->rem_uri) | res; res = serialize_str(ss, &dlg->rem_target) | res; res = serialize_uchar(ss, &dlg->secure) | res; res = serialize_dlg_state(ss, &dlg->state) | res; res = serialize_route_set(ss, &dlg->route_set) | res; if ((res == 0) && (is_input_sstream(ss))) { /* tmb.w_calculate_hooks(dlg); */ res = tmb.calculate_hooks(dlg); if (res < 0) { ERROR_LOG("error during calculate_hooks (%d)!\n", res); } } return res; } int dlg2str(dlg_t *dlg, str *dst_str) { int res = 0; sstream_t store; init_output_sstream(&store, 256); if (serialize_dlg(&store, dlg) != 0) { ERROR_LOG("can't serialize dialog\n"); res = -1; } else { if (get_serialized_sstream(&store, dst_str) != 0) { ERROR_LOG("can't get serialized dialog data\n"); res = -1; } } destroy_sstream(&store); return res; } int str2dlg(const str *s, dlg_t *dst_dlg) { int res = 0; sstream_t store; if (!s) return -1; init_input_sstream(&store, s->s, s->len); if (serialize_dlg(&store, dst_dlg) != 0) { ERROR_LOG("can't de-serialize dialog\n"); res = -1; } destroy_sstream(&store); return res; } kamailio-4.0.4/obsolete/dialog/dlg_mod.h0000644000000000000000000000504312223032460016630 0ustar rootroot/* * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __DLG_MOD_H #define __DLG_MOD_H #include "../../modules/tm/dlg.h" #include "../../lib/srdb2/db.h" #include "../../modules/tm/t_hooks.h" #include /* Prototype of function for storing dialog into database. * This function computes ID of newly added row and returns * it in dst_id (if set). Function returns 0 if OK, nonzero * on error. */ /*typedef int (*db_store_dlg_f)(db_con_t* conn, dlg_t *dlg, str *dst_id); typedef int (*db_load_dlg_f)(db_con_t* conn, str *id, dlg_t **dst_dlg);*/ typedef int (*serialize_dlg_f)(sstream_t *ss, dlg_t *dlg); typedef int (*dlg2str_f)(dlg_t *dlg, str *dst_str); typedef int (*str2dlg_f)(const str *s, dlg_t *dst_dlg); typedef int (*preset_dialog_route_f)(dlg_t* dialog, str *route); typedef int (*request_outside_f)(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb cb, void* cbp); typedef int (*request_inside_f)(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb cb, void* cbp); typedef int (*cmp_dlg_ids_f)(dlg_id_t *a, dlg_id_t *b); typedef unsigned int (*hash_dlg_id_f)(dlg_id_t *id); typedef struct { /* dialog creation/destruction functions */ /* db_store_dlg_f db_store; db_load_dlg_f db_load;*/ /* utility functions */ serialize_dlg_f serialize; dlg2str_f dlg2str; str2dlg_f str2dlg; hash_dlg_id_f hash_dlg_id; cmp_dlg_ids_f cmp_dlg_ids; /* dialog functions */ preset_dialog_route_f preset_dialog_route; request_outside_f request_outside; request_inside_f request_inside; } dlg_func_t; typedef int (*bind_dlg_mod_f)(dlg_func_t *dst); #endif kamailio-4.0.4/obsolete/dialog/Makefile0000644000000000000000000000075612223032460016520 0ustar rootroot# $Id$ # # Registrar Presence User Agent # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME = dialog.so # if using libcds, the directive SER must be defined ! # and root ser directory must be in include directories DEFS+=-DSER INCLUDES = -I../../lib -I../.. LIBS = SERLIBPATH=../../lib SER_LIBS=$(SERLIBPATH)/cds/ser_cds DEFS+=-DSER_MOD_INTERFACE SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/registrar/0000755000000000000000000000000012223032461015614 5ustar rootrootkamailio-4.0.4/obsolete/registrar/save.h0000644000000000000000000000351512223032460016726 0ustar rootroot/* * $Id$ * * Functions that process REGISTER message * and store data in usrloc * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-03-21 save_noreply added, provided by Maxim Sobolev (janakj) */ #ifndef SAVE_H #define SAVE_H #include "../../parser/msg_parser.h" /* * Process REGISTER request and save it's contacts */ int save(struct sip_msg* _m, char* _t, char* aor_filter); /* * Process REGISTER request and save it's contacts, do not send any replies */ int save_noreply(struct sip_msg* _m, char* _t, char* aor_filter); /* * Process REGISTER request and save it's contacts, do not send any replies */ int save_memory(struct sip_msg* _m, char* _t, char* aor_filter); /* * Update memory cache only and do not send reply back */ int save_mem_nr(struct sip_msg* msg, char* table, char* aor_filter); #endif /* SAVE_H */ kamailio-4.0.4/obsolete/registrar/doc/0000755000000000000000000000000012223032460016360 5ustar rootrootkamailio-4.0.4/obsolete/registrar/doc/functions.xml0000644000000000000000000000740512223032460021120 0ustar rootroot %docentities; ] >
Functions
<function>save(domain)</function> The function processes a REGISTER message. It can add, remove or modify usrloc records depending on Contact and Expires HFs in the REGISTER message. On success, 200 OK will be returned listing all contacts that are currently in usrloc. On an error, error message will be send with a short description in reason phrase. Meaning of the parameters is as follows: domain - Logical domain within registrar. If database is used then this must be name of the table which stores the contacts. <function>save</function> usage ... save("location"); ...
<function>save_noreply(domain)</function> Same as save() but it doesn't send a reply. Meaning of the parameters is as follows: domain - Logical domain within registrar. If database is used then this must be na e of the table which stores the contacts. <function>save_noreply</function> usage ... save_noreply("location"); ...
<function>lookup(domain)</function> The functions extracts username from Request-URI and tries to find all contacts for the username in usrloc. If there are no such contacts, -1 will be returned. If there are such contacts, Request-URI will be overwritten with the contact that has the highest q value and optionally the rest will be appended to the message (depending on append_branches parameter value). Meaning of the parameters is as follows: domain - Name of table that should be used for the lookup. <function>lookup</function> usage ... lookup("location"); ...
<function>registered(domain)</function> The function returns true if the AOR in the Request-URI is registered, false otherwise. The function does not modify the message being process, it neither rewrites the Request-URI if a contact is found not append branches. Meaning of the parameters is as follows: domain - Name of table that should be used for the lookup. <function>registered</function> usage ... if (registered("location")) { sl_send_reply("100", "Trying"); ... }; ...
kamailio-4.0.4/obsolete/registrar/doc/registrar.xml0000644000000000000000000002370312223032460021111 0ustar rootroot %docentities; ] >
Jan Janak FhG FOKUS
jan@iptel.org
2003 FhG FOKUS
Registrar Module
Overview The module contains REGISTER processing logic.
NAT Support In Registrar Registrar and usrloc modules implement NAT extensions that ensure that SIP messages beging sent to registered contacts would use the same socket (with the same IP address and port) on which the REGISTER that registered the contact had been received. REGISTER messages generated by user agents behind NAT ofter open a pinhole in the NAT because REGISTER is usually the first message sent from the user agent to the SIP server. A small example follows. Let's suppose that we have a single SER instance listening on two ports -- 5060 and 5090. Using a different port seems to be often necessary because of broken SIP ALGs (Application Level Gateways) that are often built into DSL routers or other devices. Such ALGs would mangle SIP requests and responses coming to and from port 5060 and the only option how to avoid such mangling is using a different port number. Let's suppose that we have two UAs beind NAT, one of them is configured to reach SER on port 5060, the other one is configured to use port 5090 (due to the reasons described above): SER +---------+ UA1 ---- NAT1 ----| 5060 | | | UA2 ---- NAT2 ----| 5090 | +---------+ Registrar and usrloc would store the public IP of NAT with each registered contact, thus it would know how to reach both user agents. In addition to the public IP and port of the NAT device, registrar would also remember the destination IP and port of the REGISTER request (the IP and port used in SER). If registrar did not store this information, it would not know what outbound socket it should use when sending SIP messages to the registered contact. It would use the default port number (often 5060) for such outgoing requests. When an INVITE for UA1 comes, everything would work because UA1 used port 5060 when registering and that is also the destination port in the pinhole being kept open in NAT1: When an INVITE for UA2 comes, SER would again use port 5060 as the default outgoing source port number, but this time the message will be dropped by NAT2, because the pinhole opened during the registration has 5060 as the destination port number: That is the reason why registrar and usrloc also need to remember the IP and port used on the server side, that information would be used later when forwarding INVITEs: The X next to NAT2 has disappeared in this picture which means that the message will be lucky enough to make it through. SER would encode this information into a SIP URI when saving contacts in database and later, after restart of SER, this information will be restored. The URI looks like: sip:1.2.3.4:5060;dstip=5.6.7.8;dstport=5090 Where the hostname part is the public IP of the NAT, the port (5060) is the port allocated by the NAT, "dstip" parameter is the IP used on SER side and "dstport" parameter is the port number used on SER side during registration. This information is later used to find the correct outgoing socket in SER. If no such socket can be found (it can happen if you reconfigure SER to use different ports or IPs), it will use the default IP/port again.
Synchronizing UA Internal Clock Many existing user agents support date and time synchronization from the registrar. Registrar can append "Date" header field to the 200 OK to REGISTER message received from the user agent. User agents that support time synchronization would read the current date and time from the header field and set internal clock to it. For example: ;tag=2bd21cbe8bf183397d829c66d62463e6.0aea From: "1 1" Call-ID: 1767561454@62.240.165.98]]> Date: Fri, 02 Sep 2005 11:20:12 GMT You can use append_time function from textops module to append the header field to the reply. Put the function before save("location"); in the configuration file. Adding Date header to Replies if (uri == myself) { if (method == "REGISTER") { append_time(); save("location"); break; }; };
Processing +sip.instance parameter The GRUU extension for SIP (draft-ietf-sip-gruu-04) adds a new parameter to the URI of the Contact header in the REGISTER request to identify a UA instance globaly unique in the world. This is reached by adding a globaly unqiue identifier, the so called sip instace, as a parameter to the Contact URI within all REGISTER requests. A Contact header with sip instance parameter ;q=1.0;+sip.instance=""]]> By looking at this sip instance parameter the registrar can decide if the incoming request is just an update of an existing Contact URI or if a new Contact URI has to be added to the AOR of this account. Thus even after a reboot of the UA the new REGISTER can be determined the request as an update of an old, already existing entry. The registrar module now looks for the sip instance parameter in the Contact URI: If the sip instance parameter is not present in the URI of the Contact header the algorithm stays the same as like before the GRUU extension. Which means the registrar only compares the SIP URI from the Contact header with the existing URIs for the AOR (account). If exactly the same URI already exists the existing entry will be updated, otherwise the URI will be added to the list. If the sip instace parameter is present in the URI of the Contact header it will be first searched for an existing Contact with exactly the same sip instance value. If an URI with exactly the same sip instance value exists already in the database, the existing entry will be replaced with the new value, no matter if they differ or not. If no Contact with this sip instance value exists a new entry for it will be added. For example the following Contact would replace the Contact value from the example above in the usrloc database because the sip instance value is the same, although the Contact URI differs. Without the sip instance this would have created two entries in the usrloc database. Different Contact with the same sip.instance ;q=1.0;+sip.instance=""]]>
Dependencies Registrar module depends on the following SER modules: usrloc - User Location Module. sl - Stateless Replies.
kamailio-4.0.4/obsolete/registrar/doc/Makefile0000644000000000000000000000013212223032460020014 0ustar rootrootdocs = registrar.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/registrar/doc/params.xml0000644000000000000000000003616612223032460020401 0ustar rootroot %docentities; ] >
Parameters
<varname>default_expires</varname> (integer) If the processed message contains neither Expires HFs nor expires contact parameters, this value will be used for newly created usrloc records. The parameter contains number of second to expire (for example use 3600 for one hour). Default value is 3600. Set <varname>default_expires</varname> parameter ... modparam("registrar", "default_expires", 1800) ...
<varname>min_expires</varname> (integer) The minimum expires value of a Contact, values lower than this minimum will be automatically set to the minimum. Value 0 disables the checking. Default value is 60. Set <varname>min_expires</varname> parameter ... modparam("registrar", "min_expires", 60) ...
<varname>max_expires</varname> (integer) The maximum expires value of a Contact, values higher than this maximum will be automatically set to the maximum. Value 0 disables the checking. Default value is 0. Set <varname>max_expires</varname> parameter ... modparam("registrar", "max_expires", 120) ...
<varname>default_q</varname> (integer) The parameter represents default q value for new contacts. Because ser doesn't support float parameter types, the value in the parameter is divided by 100 and stored as float. For example, if you want default_q to be 0.38, use value 38 here. Default value is 0. Set <varname>default_q</varname> parameter ... modparam("registrar", "default_q", 100) ...
<varname>append_branches</varname> (integer) The parameter controls how lookup function processes multiple contacts. If there are multiple contacts for the given username in usrloc and this parameter is set to 1, Request-URI will be overwritten with the highest-q rated contact and the rest will be appended to sip_msg structure and can be later used by tm for forking. If the parameter is set to 0, only Request-URI will be overwritten with the highest-q rated contact and the rest will be left unprocessed. Default value is 1. Set <varname>append_branches</varname> parameter ... modparam("registrar", "append_branches", 0) ...
<varname>case_sensitive</varname> (integer) This parameter was removed (obsolete since ser 2.0). If set to 1 then AOR comparison will be case sensitive, if set to 0 then AOR comparison will be case insensitive--This is recommended. Default value is 0. Set <varname>case_sensitive</varname> parameter ... modparam("registrar", "case_sensitive", 1) ...
<varname>desc_time_order</varname> (integer) This parameter was removed (obsolete since ser 2.0). If set to 1 then all contacts will be ordered in descending modification time order. In this case the most recently updated/created contact will be used. Default value is 0. Set <varname>desc_time_order</varname> parameter ... modparam("registrar", "desc_time_order", 1) ...
<varname>received_avp</varname> (integer) Registrar will store the value of the AVP configured by this parameter in the received column in the user location database. It will leave the column empty if the AVP is empty. The AVP should contain a SIP URI consisting of the source IP, port, and protocol of the REGISTER message being processed. The value of this parameter should be the same as the value of corresponding parameter of nathelper module. Default value is 42. Set <varname>received_avp</varname> parameter ... modparam("registrar", "received_avp", 43) ...
<varname>received_param</varname> (integer) The name of the parameter that will be appended to Contacts of 200 OK when the received URI was set by nathelper module. Default value is "received". Set <varname>received_param</varname> parameter ... modparam("registrar", "received_param", "rcv") ...
<varname>max_contacts</varname> (integer) The parameter can be used to limit the number of contacts per AOR (Address of Record) in the user location database. Value 0 disables the check. Default value is 0. Set <varname>max_contacts</varname> parameter ... # Allow no more than 10 contacts per AOR modparam("registrar", "max_contacts", 10) ...
<varname>retry_after</varname> (integer) The registrar can generate 5xx reply to REGISTER in various situations. It can, for example, happen when the max_contacts parameter is set and the processing of REGISTER request would exceed the limit. In this case the registrar would generate "503 Service Unavailable" response. If you want to add the Retry-After header field in 5xx replies, set this parameter to a value grater than zero (0 means do not add the header field). See section 20.33 of RFC3261 for more details. Default value is 0 (disabled). Set <varname>retry_after</varname> parameter ... modparam("registrar", "retry_after", 30) ...
<varname>save_nat_flag</varname> (flagname or integer) The contact will be marked as behind the NAT if this flag is set before calling one of registrar save*() functions. See also load_nat_flag. The default value is 4 (flag number 4). Set <varname>save_nat_flag</varname> parameter flags FLAG_NAT; ... modparam("registrar", "save_nat_flag", "FLAG_NAT") ... route{ ... if (nat_uac_test("19")) setflag(FLAG_NAT); ... }
<varname>load_nat_flag</varname> (flagname or integer) This flag will be set by the registrar lookup*() functions if the contact was marked as behind the NAT during its registration. See also save_nat_flag. The default value is 4 (flag number 4). Set <varname>load_nat_flag</varname> parameter flags FLAG_NAT; ... modparam("registrar", "load_nat_flag", "FLAG_NAT") ... route{ ... if (lookup_contacts("location")) { if (isflagset(FLAG_NAT)){ log(1, "natted contact"); # use rtpproxy a.s.o } ... } ... }
<varname>trust_received_flag</varname> (flagname or integer) If this flag is set, the received information added to the REGISTER contacts on another machine (e.g. upstream proxy or replication peer) by the fix_nated_register() function from the nathelper module (ser version, under modules_s/) is trusted. If it is not set, it will be ignored. It is especially useful in REGISTER replication scenarios of registrars behind a transparent load balancer (one IP) or a proxy / load balancer that can make use of the original received information (in this case combined with received_to_uri). A contact with received information looks like: <sip:foo@10.0.0.3:5060>;received="sip:1.2.3.4:3412;transport=TCP;dstip=4.5.6.7;dstport=5060". Besides the normal flags values (flag names or positive integers), there are 2 special integer values: -1 and -2. If trust_received_flag is set to -1 it is equivalent with disabling it globally (no contact "received" parameter will be trusted, they will be all ignored). If set to -2 all the contact "received" parameters will be trusted. See also fix_nated_register() in ser nathelper version (modules_s/nathelper). The default value is -2 (always trust received), due to config compatibility reasons with older ser versions. Set <varname>trust_received_flag</varname> parameter flags FLAG_REPLICATED; ... modparam("registrar", "trust_received_flag", "FLAG_REPLICATED") ... route{ ... if (dst_ip==224.0.1.75 && method == "REGISTER") { # REGISTER replicated on multicast (trusted) setflag(FLAG_REPLICATED); save_mem_nr("location"); ... } ... }
<varname>received_to_uri</varname> (boolean) If set the lookup*() functions will add a contact received information to the uri, instead of using the contact received information as a destination for the message. The resulting message uri will look like: <sip:contact>;received="sip:src_ip:src_port;transport=proto;dstip=iface_ip;dstport=iface_port". Is is useful when the registrar is behind some other proxy (e.g. load balancer) and using Path is not desired. In this case the outer proxy would have to look at the message uri, remove the "received" parameter from the uri and use it as destination for the message. The default value is 0 (off). Set <varname>received_to_uri</varname> parameter ... modparam("registrar", "received_to_uri", 1) # example uri for a message received on 1.2.3.4:5060 from 192.168.1.1:5060: # <sip:foo@10.0.0.3>;received="sip:192.168.1.1:5060;dstip=1.2.3.4;dstport=5060"
<varname>reply_code_attr</varname> (avp name) If one of the save*() functions that do not send a reply is used (save_contacts_no_reply(), save_noreply() or save_mem_nr()), an AVP with the given name will be set to the code of the reply that should be send by other means. See also reply_reason_attr and contact_attr. The default value is "$code". Set <varname>reply_code_attr</varname>, <varname>reply_reason_attr</varname> parameters and <varname>contact_attr</varname> modparam("registrar", "reply_code_attr", "$repl_code") modparam("registrar", "reply_reason_attr", "$repl_reason") modparam("registrar", "contact_attr", "$contact") ... route{ ... if (!save_noreply("location")) { append_to_reply("$contact"); sl_send_reply($reply_code, $repl_reason); } ... }
<varname>reply_reason_attr</varname> (avp name) See reply_code_attr above. The default value is "$reason".
<varname>contact_attr</varname> (avp name) See reply_code_attr above. The default value is "$contact".
<varname>aor_attr</varname> (avp name) If set to an AVP name, the AOR will be taken from this AVP, instead of the message "To:" header (when using one of the save*() functions). The default value is "$aor". Set <varname>aor_attr</varname> modparam("registrar", "aor_attr", "$my_aor") ... route{ ... if (src_ip==1.2.3.4) $my_aor=@msg.header["From"]; save("location"); ... }
<varname>server_id_attr</varname> (avp name) If set to an AVP name, the server ID associated to the contact will be set to the AVP value. If not set or the AVP is not found, the server ID will be set to the server_id of the current server. The default value is "$server_id". See also server_id (core parameter). Set <varname>server_id_attr</varname> ... modparam("registrar", "server_id_attr", "$remote_server_id") ... route{ ... if (dst_ip==224.0.1.75 && method == "REGISTER") { # REGISTER replicated on multicast, use the originator server_id # (saved in the custom SER-Server-ID header) $remote_server_id = @msg.header["SER-Server-ID"]; setflag(FLAG_REPLICATED); save_mem_nr("location"); ... } ... }
kamailio-4.0.4/obsolete/registrar/regtime.h0000644000000000000000000000235612223032460017426 0ustar rootroot/* * $Id$ * * Registrar time related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef REGTIME_H #define REGTIME_H #include extern time_t act_time; /* * Get actual time and store * value in act_time */ void get_act_time(void); #endif /* REGTIME_H */ kamailio-4.0.4/obsolete/registrar/save.c0000644000000000000000000005153512223032461016727 0ustar rootroot/* * $Id$ * * Process REGISTER request and send reply * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ---------- * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) * 2003-02-28 scrathcpad compatibility abandoned (jiri) * 2003-03-21 save_noreply added, patch provided by * Maxim Sobolev (janakj) * 2005-02-25 incoming socket is saved in USRLOC (bogdan) * 2010-09-30 trust received info only if trust_received_flag is set (andrei) * 2010-10-01 if received info is available try to set the incoming socket * to the information saved in received. */ #include "../../comp_defs.h" #include "../../str.h" #include "../../dprint.h" #include "../../parser/parse_to.h" #include "../../parser/parse_param.h" #include "../../parser/parse_uri.h" #include "../../resolve.h" #include "../../socket_info.h" #include "../../trim.h" #include "../../ut.h" #include "../usrloc/usrloc.h" #include "../../qvalue.h" #include "../../id.h" #include "../../globals.h" #include "../../sr_module.h" #include "common.h" #include "sip_msg.h" #include "rerrno.h" #include "reply.h" #include "reg_mod.h" #include "regtime.h" #include "save.h" static int mem_only = 0; void remove_cont(urecord_t* _r, ucontact_t* _c) { if (_c->prev) { _c->prev->next = _c->next; if (_c->next) { _c->next->prev = _c->prev; } } else { _r->contacts = _c->next; if (_c->next) { _c->next->prev = 0; } } } /* * Process request that contained a star, in that case, * we will remove all bindings with the given username * from the usrloc and return 200 OK response */ static inline int star(udomain_t* _d, str* _u, str* aor_filter) { urecord_t* r; ucontact_t* c; ul.lock_udomain(_d); if (!ul.get_urecord(_d, _u, &r)) { c = r->contacts; while(c) { if (mem_only) { c->flags |= FL_MEM; } else { c->flags &= ~FL_MEM; } c = c->next; } } if (ul.delete_urecord(_d, _u) < 0) { LOG(L_ERR, "star(): Error while removing record from usrloc\n"); /* Delete failed, try to get corresponding * record structure and send back all existing * contacts */ rerrno = R_UL_DEL_R; if (!ul.get_urecord(_d, _u, &r)) { build_contact(r->contacts, aor_filter); } ul.unlock_udomain(_d); return -1; } ul.unlock_udomain(_d); return 0; } /* * Process request that contained no contact header * field, it means that we have to send back a response * containing a list of all existing bindings for the * given aor */ static inline int no_contacts(udomain_t* _d, str* _u, str* aor_filter) { urecord_t* r; int res; ul.lock_udomain(_d); res = ul.get_urecord(_d, _u, &r); if (res < 0) { rerrno = R_UL_GET_R; LOG(L_ERR, "no_contacts(): Error while retrieving record from usrloc\n"); ul.unlock_udomain(_d); return -1; } if (res == 0) { /* Contacts found */ build_contact(r->contacts, aor_filter); } ul.unlock_udomain(_d); return 0; } #define DSTIP_PARAM ";dstip=" #define DSTIP_PARAM_LEN (sizeof(DSTIP_PARAM) - 1) #define DSTPORT_PARAM ";dstport=" #define DSTPORT_PARAM_LEN (sizeof(DSTPORT_PARAM) - 1) /* * Create received SIP uri that will be either * passed to registrar in an AVP or apended * to Contact header field as a parameter */ static int create_rcv_uri(str** uri, struct sip_msg* m) { static str res; static char buf[MAX_URI_SIZE]; char* p; str src_ip, src_port, dst_ip, dst_port; int len; str proto; if (!uri || !m) { ERR("Invalid parameter value\n"); return -1; } src_ip.s = ip_addr2a(&m->rcv.src_ip); src_ip.len = strlen(src_ip.s); src_port.s = int2str(m->rcv.src_port, &src_port.len); dst_ip = m->rcv.bind_address->address_str; dst_port = m->rcv.bind_address->port_no_str; switch(m->rcv.proto) { case PROTO_NONE: case PROTO_UDP: proto.s = 0; /* Do not add transport parameter, UDP is default */ proto.len = 0; break; case PROTO_TCP: proto.s = "TCP"; proto.len = 3; break; case PROTO_TLS: proto.s = "TLS"; proto.len = 3; break; case PROTO_SCTP: proto.s = "SCTP"; proto.len = 4; break; default: ERR("BUG: Unknown transport protocol\n"); return -1; } len = 4 + src_ip.len + 1 + src_port.len; if (proto.s) { len += TRANSPORT_PARAM_LEN; len += proto.len; } len += DSTIP_PARAM_LEN + dst_ip.len; len += DSTPORT_PARAM_LEN + dst_port.len; if (len > MAX_URI_SIZE) { ERR("Buffer too small\n"); return -1; } p = buf; memcpy(p, "sip:", 4); p += 4; memcpy(p, src_ip.s, src_ip.len); p += src_ip.len; *p++ = ':'; memcpy(p, src_port.s, src_port.len); p += src_port.len; if (proto.s) { memcpy(p, TRANSPORT_PARAM, TRANSPORT_PARAM_LEN); p += TRANSPORT_PARAM_LEN; memcpy(p, proto.s, proto.len); p += proto.len; } memcpy(p, DSTIP_PARAM, DSTIP_PARAM_LEN); p += DSTIP_PARAM_LEN; memcpy(p, dst_ip.s, dst_ip.len); p += dst_ip.len; memcpy(p, DSTPORT_PARAM, DSTPORT_PARAM_LEN); p += DSTPORT_PARAM_LEN; memcpy(p, dst_port.s, dst_port.len); p += dst_port.len; res.s = buf; res.len = len; *uri = &res; return 0; } /** find send socket based on dst_ip saved in rcv_uri. * Based on usrloc(s) find_socket() (modules_s/usrloc/udomain.c), * but will try only IPs (no DNS lookups) and it is quiet on errors. * * @param received - received uri in the format: * sip:src_host:src_port;dstip=IP;dstport=port * @param ip - filled with the ip in dst_ip. * @param port - filled with the port in dst_ip (if present) * @param proto - filled with the uri protocol. * @return <0 on error, 0 on success */ int parse_uri_dstip(str* received, struct ip_addr* ip, unsigned short* port, unsigned short* proto) { struct sip_uri puri; param_hooks_t hooks; struct ip_addr* p; param_t* params = 0; int error; if (unlikely(!received)) return 0; if (unlikely(parse_uri(received->s, received->len, &puri) < 0)) goto error_uri; *proto = puri.proto; if (unlikely(parse_params(&puri.params, CLASS_URI, &hooks, ¶ms) < 0)) goto error_uri_params; if (unlikely(hooks.uri.dstip == 0 || hooks.uri.dstip->body.s == 0 || hooks.uri.dstip->body.len == 0)) goto end; /* no dst_ip param */ /* check if it's ipv4 or ipv6 */ if ( #ifdef USE_IPV6 likely(((p = str2ip(&hooks.uri.dstip->body)) != 0) || ((p = str2ip6(&hooks.uri.dstip->body)) != 0)) #else /* ! USE_IPV6 */ likely(((p = str2ip(&hooks.uri.dstip->body)) != 0)) #endif /* USE_IPV6 */ ) { *ip = *p; } else goto error_no_ip; /* no ip */ if (likely(hooks.uri.dstport != 0 && hooks.uri.dstport->body.s != 0 && hooks.uri.dstport->body.len !=0)) { *port = str2s(hooks.uri.dstport->body.s, hooks.uri.dstport->body.len, &error); if (unlikely(error != 0)) goto error_port; } else { *port = 0; } end: if (params) free_params(params); return 0; error_uri: error_uri_params: error_no_ip: error_port: if (params) free_params(params); return -1; } /** try to find the send socket based on saved received information in uri. * @param received - received uri in the format: * sip:src_host:src_port;dstip=IP;dstport=port * @return 0 on error or not found, socket_info pointer on success. */ static struct socket_info* find_send_socket(str* received) { struct ip_addr ip; unsigned short port; unsigned short proto; struct socket_info* si; if (unlikely(parse_uri_dstip(received, &ip, &port, &proto) < 0)) return 0; si = find_si(&ip, port, proto); #if 0 /* FIXME: which would be the best fallback procedure, proto:ip:* (try to keep the ip, although unlikely to be possible for replication) or proto:*: port (try to keep the same port, usefull for proxies behind transparent proxies /LBs) */ if (si == 0) si = find_si(&ip, 0, proto); #endif return si; } /* * Message contained some contacts, but record with same address of record was * not found so we have to create a new record and insert all contacts from * the message that have expires > 0. The function returns a negative number * on error, a positive number if the number of contacts would exceed * max_contacts and 0 on success. */ static inline int insert(struct sip_msg* _m, str* aor, contact_t* _c, udomain_t* _d, str* _u, str *ua, str* aor_filter, int sid) { urecord_t* r = 0; ucontact_t* c; int e, cseq, num; qvalue_t q; str callid; unsigned int flags; str *recv, *inst; struct socket_info* send_sock; if (isflagset(_m, save_nat_flag) == 1) flags = FL_NAT; else flags = FL_NONE; flags |= mem_only; num = 0; while(_c) { if (calc_contact_expires(_m, _c->expires, &e) < 0) { LOG(L_ERR, "insert(): Error while calculating expires\n"); return -1; } /* Skip contacts with zero expires */ if (e == 0) goto skip; if (max_contacts && (num >= max_contacts)) { rerrno = R_TOO_MANY; ul.delete_urecord(_d, _u); return 1; } num++; if (r == 0) { if (ul.insert_urecord(_d, _u, &r) < 0) { rerrno = R_UL_NEW_R; LOG(L_ERR, "insert(): Can't insert new record structure\n"); return -2; } } /* Calculate q value of the contact */ if (calc_contact_q(_c->q, &q) < 0) { LOG(L_ERR, "insert(): Error while calculating q\n"); ul.delete_urecord(_d, _u); return -3; } /* Get callid of the message */ callid = _m->callid->body; trim_trailing(&callid); /* Get CSeq number of the message */ if (str2int(&get_cseq(_m)->number, (unsigned int*)&cseq) < 0) { rerrno = R_INV_CSEQ; LOG(L_ERR, "insert(): Error while converting cseq number\n"); ul.delete_urecord(_d, _u); return -4; } send_sock = 0; if (_c->received && (((trust_received_flag >= 0) && (isflagset(_m, trust_received_flag) == 1)) || (trust_received_flag == -2)) ) { recv = &_c->received->body; send_sock = find_send_socket(recv); } else if (flags & FL_NAT && _m->first_line.type == SIP_REQUEST) { if (create_rcv_uri(&recv, _m) < 0) { ERR("Error while creating rcv URI\n"); ul.delete_urecord(_d, _u); return -4; } } else { recv = 0; } if(_c->instance) { inst = &_c->instance->body; } else { inst = 0; } if (ul.insert_ucontact(r, aor, &_c->uri, e, q, &callid, cseq, flags, &c, ua, recv, send_sock?send_sock:_m->rcv.bind_address, inst, sid) < 0) { rerrno = R_UL_INS_C; LOG(L_ERR, "insert(): Error while inserting contact\n"); ul.delete_urecord(_d, _u); return -5; } skip: _c = get_next_contact(_c); } if (r) { if (!r->contacts) { ul.delete_urecord(_d, _u); } else { build_contact(r->contacts, aor_filter); } } return 0; } static int test_max_contacts(struct sip_msg* _m, urecord_t* _r, contact_t* _c) { int num; int e; ucontact_t* ptr, *cont; num = 0; ptr = _r->contacts; while(ptr) { if (VALID_CONTACT(ptr, act_time)) { num++; } ptr = ptr->next; } DBG("test_max_contacts: %d valid contacts\n", num); while(_c) { if (calc_contact_expires(_m, _c->expires, &e) < 0) { LOG(L_ERR, "test_max_contacts: Error while calculating expires\n"); return -1; } if (ul.get_ucontact(_r, &_c->uri, &cont) > 0) { /* Contact not found */ if (e != 0) num++; } else { if (e == 0) num--; } _c = get_next_contact(_c); } DBG("test_max_contacts: %d contacts after commit, max_contacts=%d\n", num, max_contacts); if (num > max_contacts) { rerrno = R_TOO_MANY; return 1; } return 0; } /* * Message contained some contacts and appropriate * record was found, so we have to walk through * all contacts and do the following: * 1) If contact in usrloc doesn't exists and * expires > 0, insert new contact * 2) If contact in usrloc exists and expires * > 0, update the contact * 3) If contact in usrloc exists and expires * == 0, delete contact * * The function returns a negative number on error, a positive number if * max_contacts is set and the number of contacts after the change would * exceed that maximum number of allowed contacts. On success the function * returns 0. */ static inline int update(struct sip_msg* _m, urecord_t* _r, str* aor, contact_t* _c, str* _ua, str* aor_filter, int sid) { ucontact_t* c, *c2; str callid; int cseq, e, ret; int set, reset; qvalue_t q; unsigned int nated; str* recv, *inst; struct socket_info* send_sock; if (isflagset(_m, save_nat_flag) == 1) { nated = FL_NAT; } else { nated = FL_NONE; } if (max_contacts) { ret = test_max_contacts(_m, _r, _c); if (ret != 0) { /* test_max_contacts returns a negative number on error and a * positive number if the number of contacts after the update * exceeds the configured max_contacts. In both cases we return * here. */ build_contact(_r->contacts, aor_filter); return ret; } } _c = get_first_contact(_m); while(_c) { if (calc_contact_expires(_m, _c->expires, &e) < 0) { build_contact(_r->contacts, aor_filter); LOG(L_ERR, "update(): Error while calculating expires\n"); return -1; } if(_c->instance) { inst = &_c->instance->body; } else { inst = 0; } if (ul.get_ucontact_by_instance(_r, &_c->uri, inst, &c) > 0) { /* Contact not found */ if (e != 0) { /* Calculate q value of the contact */ if (calc_contact_q(_c->q, &q) < 0) { LOG(L_ERR, "update(): Error while calculating q\n"); return -2; } /* Get callid of the message */ callid = _m->callid->body; trim_trailing(&callid); /* Get CSeq number of the message */ if (str2int(&(((struct cseq_body*)_m->cseq->parsed)->number), (unsigned int*) &cseq) < 0) { rerrno = R_INV_CSEQ; LOG(L_ERR, "update(): Error while converting cseq number\n"); return -3; } send_sock = 0; if (_c->received && (((trust_received_flag >=0 ) && (isflagset(_m, trust_received_flag) == 1)) || (trust_received_flag == -2)) ) { recv = &_c->received->body; send_sock = find_send_socket(recv); } else if (nated & FL_NAT && _m->first_line.type == SIP_REQUEST) { if (create_rcv_uri(&recv, _m) < 0) { ERR("Error while creating rcv URI\n"); rerrno = R_UL_INS_C; return -4; } } else { recv = 0; } if (ul.insert_ucontact(_r, aor, &_c->uri, e, q, &callid, cseq, nated | mem_only, &c2, _ua, recv, send_sock?send_sock:_m->rcv.bind_address, inst, sid) < 0) { rerrno = R_UL_INS_C; LOG(L_ERR, "update(): Error while inserting contact\n"); return -4; } } } else { if (e == 0) { if (mem_only) { c->flags |= FL_MEM; } else { c->flags &= ~FL_MEM; } if (ul.delete_ucontact(_r, c) < 0) { rerrno = R_UL_DEL_C; LOG(L_ERR, "update(): Error while deleting contact\n"); return -5; } } else { /* Calculate q value of the contact */ if (calc_contact_q(_c->q, &q) < 0) { LOG(L_ERR, "update(): Error while calculating q\n"); return -6; } /* Get callid of the message */ callid = _m->callid->body; trim_trailing(&callid); /* Get CSeq number of the message */ if (str2int(&(((struct cseq_body*)_m->cseq->parsed)->number), (unsigned int*)&cseq) < 0) { rerrno = R_INV_CSEQ; LOG(L_ERR, "update(): Error while converting cseq number\n"); return -7; } send_sock = 0; if (_c->received && (((trust_received_flag >=0 ) && (isflagset(_m, trust_received_flag) == 1)) || (trust_received_flag == -2)) ) { recv = &_c->received->body; send_sock = find_send_socket(recv); } else if (nated & FL_NAT && _m->first_line.type == SIP_REQUEST) { if (create_rcv_uri(&recv, _m) < 0) { ERR("Error while creating rcv URI\n"); rerrno = R_UL_UPD_C; return -4; } } else { recv = 0; } set = nated | mem_only; reset = ~(nated | mem_only) & (FL_NAT | FL_MEM); if (ul.update_ucontact(c, &_c->uri, aor, e, q, &callid, cseq, set, reset, _ua, recv, send_sock?send_sock:_m->rcv.bind_address, inst, sid) < 0) { rerrno = R_UL_UPD_C; LOG(L_ERR, "update(): Error while updating contact\n"); return -8; } } } _c = get_next_contact(_c); } return 0; } static int get_server_id(void) { int_str name, val; int sid; if (server_id_attr.len && server_id_attr.s) { name.s.s = server_id_attr.s + 1; /* Skip the 1st char which is $ */ name.s.len = server_id_attr.len - 1; if (search_first_avp(AVP_TRACK_FROM | AVP_NAME_STR, name, &val, 0)) { if (str2sint(&val.s, &sid) == 0) return sid; } } /* No server_id attribute found or the attribute doesn't have * meaningful value, return the server id of this SER server */ return server_id; } /* * This function will process request that * contained some contact header fields */ static inline int contacts(struct sip_msg* _m, contact_t* _c, udomain_t* _d, str* _u, str* _ua, str* aor_filter) { int res, sid; urecord_t* r; str* aor; int_str name, val; if (aor_attr.len && aor_attr.s) { name.s.s = aor_attr.s + 1; /* Skip the 1st char which is $ */ name.s.len = aor_attr.len - 1; if (search_first_avp(AVP_TRACK_TO | AVP_NAME_STR, name, &val, 0)) { aor = &val.s; } else { aor = &get_to(_m)->uri; } } else { aor = &get_to(_m)->uri; } sid = get_server_id(); ul.lock_udomain(_d); res = ul.get_urecord(_d, _u, &r); if (res < 0) { rerrno = R_UL_GET_R; LOG(L_ERR, "contacts(): Error while retrieving record from usrloc\n"); ul.unlock_udomain(_d); return -2; } if (res == 0) { /* Contacts found */ if ((res = update(_m, r, aor, _c, _ua, aor_filter, sid) < 0)) { LOG(L_ERR, "contacts(): Error while updating record\n"); } build_contact(r->contacts, aor_filter); ul.release_urecord(r); } else { if ((res = insert(_m, aor, _c, _d, _u, _ua, aor_filter, sid) < 0)) { LOG(L_ERR, "contacts(): Error while inserting record\n"); } } ul.unlock_udomain(_d); return res; } #define UA_DUMMY_STR "Unknown" #define UA_DUMMY_LEN 7 /* * Process REGISTER request and save it's contacts */ static inline int save_real(struct sip_msg* _m, udomain_t* _t, char* aor_filt, int doreply) { contact_t* c; int st; str uid, ua, aor_filter; rerrno = R_FINE; if (parse_message(_m) < 0) { goto error; } if (check_contacts(_m, &st) > 0) { goto error; } if (aor_filt) { if (get_str_fparam(&aor_filter, _m, (fparam_t*)aor_filt) != 0) { ERR("registrar:save: Unable to get the AOR value\n"); return -1; } } else { aor_filter.s = 0; aor_filter.len = 0; } get_act_time(); c = get_first_contact(_m); if (get_to_uid(&uid, _m) < 0) goto error; ua.len = 0; if (parse_headers(_m, HDR_USERAGENT_F, 0) != -1 && _m->user_agent && _m->user_agent->body.len > 0) { ua.len = _m->user_agent->body.len; ua.s = _m->user_agent->body.s; } if (ua.len == 0) { ua.len = UA_DUMMY_LEN; ua.s = UA_DUMMY_STR; } if (c == 0) { if (st) { if (star(_t, &uid, &aor_filter) < 0) goto error; } else { if (no_contacts(_t, &uid, &aor_filter) < 0) goto error; } } else { if (contacts(_m, c, _t, &uid, &ua, &aor_filter) != 0) goto error; } if (doreply) { if (send_reply(_m) < 0) return -1; } else { /* No reply sent, create attributes with values * of reply code, reason text, and contacts */ if (setup_attrs(_m) < 0) return -1; } return 1; error: if (doreply) { send_reply(_m); return 0; } return -2; } /* * Process REGISTER request and save it's contacts */ int save(struct sip_msg* _m, char* table, char* aor_filter) { mem_only = FL_NONE; return save_real(_m, (udomain_t*)table, aor_filter, 1); } /* * Process REGISTER request and save it's contacts, do not send any replies */ int save_noreply(struct sip_msg* _m, char* table, char* aor_filter) { mem_only = FL_NONE; return save_real(_m, (udomain_t*)table, aor_filter, 0); } /* * Update memory cache only */ int save_memory(struct sip_msg* _m, char* table, char* aor_filter) { mem_only = FL_MEM; return save_real(_m, (udomain_t*)table, aor_filter, 1); } /* * Update memory cache only and do not send reply back */ int save_mem_nr(struct sip_msg* msg, char* table, char* aor_filter) { mem_only = FL_MEM; return save_real(msg, (udomain_t*)table, aor_filter, 0); } kamailio-4.0.4/obsolete/registrar/sip_msg.c0000644000000000000000000001374212223032460017427 0ustar rootroot/* * $Id$ * * SIP message related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../../parser/hf.h" #include #include "../../parser/parse_expires.h" #include "../../ut.h" #include "../../qvalue.h" #include "reg_mod.h" /* Module parameters */ #include "regtime.h" /* act_time */ #include "rerrno.h" #include "sip_msg.h" static struct hdr_field* act_contact; /* * Return value of Expires header field * if the HF exists converted to absolute * time, if the HF doesn't exist, returns * default value; */ static inline int get_expires_hf(struct sip_msg* _m) { exp_body_t* p; if (_m->expires) { p = (exp_body_t*)_m->expires->parsed; if (p->valid) { if (p->val != 0) { return p->val + act_time; } else return 0; } else return act_time + default_expires; } else { return act_time + default_expires; } } /* * Parse the whole message and bodies of all header fields * that will be needed by registrar */ int parse_message(struct sip_msg* _m) { struct hdr_field* ptr; if (parse_headers(_m, HDR_EOH_F, 0) == -1) { rerrno = R_PARSE; LOG(L_ERR, "parse_message(): Error while parsing headers\n"); return -1; } if (!_m->to) { rerrno = R_TO_MISS; LOG(L_ERR, "parse_message(): To not found\n"); return -2; } if (!_m->callid) { rerrno = R_CID_MISS; LOG(L_ERR, "parse_message(): Call-ID not found\n"); return -3; } if (!_m->cseq) { rerrno = R_CS_MISS; LOG(L_ERR, "parse_message(): CSeq not found\n"); return -4; } if (_m->expires && !_m->expires->parsed && (parse_expires(_m->expires) < 0)) { rerrno = R_PARSE_EXP; LOG(L_ERR, "parse_message(): Error while parsing expires body\n"); return -5; } if (_m->contact) { ptr = _m->contact; while(ptr) { if (ptr->type == HDR_CONTACT_T) { if (!ptr->parsed && (parse_contact(ptr) < 0)) { rerrno = R_PARSE_CONT; LOG(L_ERR, "parse_message(): Error while parsing Contact body\n"); return -6; } } ptr = ptr->next; } } return 0; } /* * Check if the originating REGISTER message was formed correctly * The whole message must be parsed before calling the function * _s indicates whether the contact was star */ int check_contacts(struct sip_msg* _m, int* _s) { struct hdr_field* p; *_s = 0; /* Message without contacts is OK */ if (_m->contact == 0) return 0; if (((contact_body_t*)_m->contact->parsed)->star == 1) { /* The first Contact HF is star */ /* Expires must be zero */ if (get_expires_hf(_m) > 0) { rerrno = R_STAR_EXP; return 1; } /* Message must contain no contacts */ if (((contact_body_t*)_m->contact->parsed)->contacts) { rerrno = R_STAR_CONT; return 1; } /* Message must contain no other Contact HFs */ p = _m->contact->next; while(p) { if (p->type == HDR_CONTACT_T) { rerrno = R_STAR_CONT; return 1; } p = p->next; } *_s = 1; } else { /* The first Contact HF is not star */ /* Message must contain no star Contact HF */ p = _m->contact->next; while(p) { if (p->type == HDR_CONTACT_T) { if (((contact_body_t*)p->parsed)->star == 1) { rerrno = R_STAR_CONT; return 1; } } p = p->next; } } return 0; } /* * Get the first contact in message */ contact_t* get_first_contact(struct sip_msg* _m) { if (_m->contact == 0) return 0; act_contact = _m->contact; return (((contact_body_t*)_m->contact->parsed)->contacts); } /* * Get next contact in message */ contact_t* get_next_contact(contact_t* _c) { struct hdr_field* p; if (_c->next == 0) { p = act_contact->next; while(p) { if (p->type == HDR_CONTACT_T) { act_contact = p; return (((contact_body_t*)p->parsed)->contacts); } p = p->next; } return 0; } else { return _c->next; } } /* * Calculate absolute expires value per contact as follows: * 1) If the contact has expires value, use the value. If it * is not zero, add actual time to it * 2) If the contact has no expires parameter, use expires * header field in the same way * 3) If the message contained no expires header field, use * the default value */ int calc_contact_expires(struct sip_msg* _m, param_t* _ep, int* _e) { if (!_ep || !_ep->body.len) { *_e = get_expires_hf(_m); } else { if (str2int(&_ep->body, (unsigned int*)_e) < 0) { *_e = 3600; } /* Convert to absolute value */ if (*_e != 0) *_e += act_time; } if ((*_e != 0) && ((*_e - act_time) < min_expires)) { *_e = min_expires + act_time; } if ((*_e != 0) && max_expires && ((*_e - act_time) > max_expires)) { *_e = max_expires + act_time; } return 0; } /* * Calculate contact q value as follows: * 1) If q parameter exists, use it * 2) If the parameter doesn't exist, use the default value */ int calc_contact_q(param_t* _q, qvalue_t* _r) { if (!_q || (_q->body.len == 0)) { *_r = default_q; } else { if (str2q(_r, _q->body.s, _q->body.len) < 0) { rerrno = R_INV_Q; /* Invalid q parameter */ LOG(L_ERR, "calc_contact_q(): Invalid q parameter\n"); return -1; } } return 0; } kamailio-4.0.4/obsolete/registrar/reg_mod.c0000644000000000000000000002421412223032460017376 0ustar rootroot/* * $Id$ * * Registrar module interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-16 flags export parameter added (janakj) * 2003-03-21 save_noreply added, provided by Maxim Sobolev (janakj) * 2006-02-07 named flag support (andrei) */ #include #include "../../sr_module.h" #include "../../timer.h" #include "../../dprint.h" #include "../../error.h" #include "../../usr_avp.h" #include "../../trim.h" #include "save.h" #include "lookup.h" #include "reply.h" #include "reg_mod.h" MODULE_VERSION static int mod_init(void); /* Module init function */ static int fix_save_nat_flag( modparam_t type, void* val); static int fix_load_nat_flag( modparam_t type, void* val); static int fix_trust_received_flag( modparam_t type, void* val); static int domain_fixup(void** param, int param_no); /* Fixup that converts domain name */ static int domain2_fixup(void** param, int param_no); /* Fixup that converts domain name */ static void mod_destroy(void); usrloc_api_t ul; /* Structure containing pointers to usrloc functions */ int default_expires = 3600; /* Default expires value in seconds */ qvalue_t default_q = Q_UNSPECIFIED; /* Default q value multiplied by 1000 */ int append_branches = 1; /* If set to 1, lookup will put all contacts found in msg structure */ int case_sensitive = 0; /* If set to 1, username in aor will be case sensitive */ int save_nat_flag = 4; /* The contact will be marked as behind NAT if this flag is set before calling save */ int load_nat_flag = 4; /* This flag will be set by lookup if a contact is behind NAT*/ int trust_received_flag = -2; /* if this flag is set (>=0), a contact received param. will be trusted (otherwise it will be ignored) -1 = disable -2 = trust all. */ int min_expires = 60; /* Minimum expires the phones are allowed to use in seconds, * use 0 to switch expires checking off */ int max_expires = 0; /* Minimum expires the phones are allowed to use in seconds, * use 0 to switch expires checking off */ int max_contacts = 0; /* Maximum number of contacts per AOR */ str rcv_param = STR_STATIC_INIT("received"); int received_to_uri = 0; /* copy received to uri, don't add it to dst_uri */ /* Attribute names */ str reply_code_attr = STR_STATIC_INIT("$code"); str reply_reason_attr = STR_STATIC_INIT("$reason"); str contact_attr = STR_STATIC_INIT("$contact"); str aor_attr = STR_STATIC_INIT("$aor"); str server_id_attr = STR_STATIC_INIT("$server_id"); avp_ident_t avpid_code, avpid_reason, avpid_contact; /* * sl module api */ sl_api_t slb; /* * Exported functions */ static cmd_export_t cmds[] = { {"save_contacts", save, 1, domain_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"save_contacts", save, 2, domain2_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"save", save, 1, domain_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"save", save, 2, domain2_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"save_contacts_noreply", save_noreply, 1, domain_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"save_contacts_noreply", save_noreply, 2, domain2_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"save_noreply", save_noreply, 1, domain_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE }, {"save_noreply", save_noreply, 2, domain2_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE }, {"save_memory", save_memory, 1, domain_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"save_memory", save_memory, 2, domain2_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"save_mem_nr", save_mem_nr, 1, domain_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"save_mem_nr", save_mem_nr, 2, domain2_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"lookup_contacts", lookup, 1, domain_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"lookup_contacts", lookup2, 2, domain2_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"lookup", lookup, 1, domain_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"lookup", lookup2, 2, domain2_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"registered", registered, 1, domain_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {"registered", registered2, 2, domain2_fixup, REQUEST_ROUTE | FAILURE_ROUTE }, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"default_expires", PARAM_INT, &default_expires}, {"default_q", PARAM_INT, &default_q }, {"append_branches", PARAM_INT, &append_branches}, {"save_nat_flag", PARAM_INT, &save_nat_flag }, {"save_nat_flag", PARAM_STRING|PARAM_USE_FUNC, fix_save_nat_flag}, {"load_nat_flag", PARAM_INT, &load_nat_flag }, {"load_nat_flag", PARAM_STRING|PARAM_USE_FUNC, fix_load_nat_flag}, {"min_expires", PARAM_INT, &min_expires }, {"max_expires", PARAM_INT, &max_expires }, {"received_param", PARAM_STR, &rcv_param }, {"max_contacts", PARAM_INT, &max_contacts }, {"received_to_uri", PARAM_INT, &received_to_uri}, {"reply_code_attr", PARAM_STR, &reply_code_attr}, {"reply_reason_attr", PARAM_STR, &reply_reason_attr}, {"contact_attr", PARAM_STR, &contact_attr}, {"aor_attr", PARAM_STR, &aor_attr}, {"server_id_attr", PARAM_STR, &server_id_attr}, {"trust_received_flag", PARAM_INT, &trust_received_flag}, {"trust_received_flag", PARAM_STRING|PARAM_USE_FUNC, fix_trust_received_flag}, {0, 0, 0} }; /* * Module exports structure */ struct module_exports exports = { "registrar", cmds, /* Exported functions */ 0, /* RPC methods */ params, /* Exported parameters */ mod_init, /* module initialization function */ 0, mod_destroy, /* destroy function */ 0, /* oncancel function */ 0 /* Per-child init function */ }; static int parse_attr_params(void) { str tmp; if (reply_code_attr.len) { tmp = reply_code_attr; trim(&tmp); if (!tmp.len || tmp.s[0] != '$') { ERR("Invalid attribute name '%.*s'\n", tmp.len, tmp.s); return -1; } tmp.s++; tmp.len--; if (parse_avp_ident(&tmp, &avpid_code) < 0) { ERR("Error while parsing attribute name '%.*s'\n", tmp.len, tmp.s); return -1; } } if (reply_reason_attr.len) { tmp = reply_reason_attr; trim(&tmp); if (!tmp.len || tmp.s[0] != '$') { ERR("Invalid attribute name '%.*s'\n", tmp.len, tmp.s); return -1; } tmp.s++; tmp.len--; if (parse_avp_ident(&tmp, &avpid_reason) < 0) { ERR("Error while parsing attribute name '%.*s'\n", tmp.len, tmp.s); return -1; } } if (contact_attr.len) { tmp = contact_attr; trim(&tmp); if (!tmp.len || tmp.s[0] != '$') { ERR("Invalid attribute name '%.*s'\n", tmp.len, tmp.s); return -1; } tmp.s++; tmp.len--; if (parse_avp_ident(&tmp, &avpid_contact) < 0) { ERR("Error while parsing attribute name '%.*s'\n", tmp.len, tmp.s); return -1; } } return 0; } /* * Initialize parent */ static int mod_init(void) { bind_usrloc_t bind_usrloc; DBG("registrar - initializing\n"); /* bind the SL API */ if (sl_load_api(&slb)!=0) { LM_ERR("cannot bind to SL API\n"); return -1; } bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0); if (!bind_usrloc) { ERR("Can't bind usrloc\n"); return -1; } /* Normalize default_q parameter */ if (default_q != Q_UNSPECIFIED) { if (default_q > MAX_Q) { DBG("registrar: default_q = %d, lowering to MAX_Q: %d\n", default_q, MAX_Q); default_q = MAX_Q; } else if (default_q < MIN_Q) { DBG("registrar: default_q = %d, raising to MIN_Q: %d\n", default_q, MIN_Q); default_q = MIN_Q; } } if (parse_attr_params() < 0) return -1; if (bind_usrloc(&ul) < 0) { return -1; } return 0; } /* fixes nat_flag param (resolves possible named flags) */ static int fix_save_nat_flag( modparam_t type, void* val) { return fix_flag(type, val, "registrar", "save_nat_flag", &save_nat_flag); } /* fixes nat_flag param (resolves possible named flags) */ static int fix_load_nat_flag( modparam_t type, void* val) { return fix_flag(type, val, "registrar", "load_nat_flag", &load_nat_flag); } /* fixes trust_received_flag param (resolves possible named flags) */ static int fix_trust_received_flag( modparam_t type, void* val) { return fix_flag(type, val, "registrar", "trust_received_flag", &trust_received_flag); } /* * Convert char* parameter to udomain_t* pointer */ static int domain_fixup(void** param, int param_no) { udomain_t* d; if (param_no == 1) { if (ul.register_udomain((char*)*param, &d) < 0) { LOG(L_ERR, "domain_fixup(): Error while registering domain\n"); return E_UNSPEC; } *param = (void*)d; } return 0; } /* * Convert char* parameter to udomain_t* pointer */ static int domain2_fixup(void** param, int param_no) { if (param_no == 1) { return domain_fixup(param, param_no); } else { return fixup_var_str_12(param, 2); } return 0; } static void mod_destroy(void) { free_contact_buf(); } kamailio-4.0.4/obsolete/registrar/README0000644000000000000000000005206212223032460016500 0ustar rootroot1. Registrar Module Jan Janak FhG FOKUS Copyright © 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. NAT Support In Registrar 1.3. Synchronizing UA Internal Clock 1.4. Processing +sip.instance parameter 1.5. Dependencies 1.6. Parameters 1.6.1. default_expires (integer) 1.6.2. min_expires (integer) 1.6.3. max_expires (integer) 1.6.4. default_q (integer) 1.6.5. append_branches (integer) 1.6.6. case_sensitive (integer) 1.6.7. desc_time_order (integer) 1.6.8. received_avp (integer) 1.6.9. received_param (integer) 1.6.10. max_contacts (integer) 1.6.11. retry_after (integer) 1.6.12. save_nat_flag (flagname or integer) 1.6.13. load_nat_flag (flagname or integer) 1.6.14. trust_received_flag (flagname or integer) 1.6.15. received_to_uri (boolean) 1.6.16. reply_code_attr (avp name) 1.6.17. reply_reason_attr (avp name) 1.6.18. contact_attr (avp name) 1.6.19. aor_attr (avp name) 1.6.20. server_id_attr (avp name) 1.7. Functions 1.7.1. save(domain) 1.7.2. save_noreply(domain) 1.7.3. lookup(domain) 1.7.4. registered(domain) 1.1. Overview The module contains REGISTER processing logic. 1.2. NAT Support In Registrar Registrar and usrloc modules implement NAT extensions that ensure that SIP messages beging sent to registered contacts would use the same socket (with the same IP address and port) on which the REGISTER that registered the contact had been received. REGISTER messages generated by user agents behind NAT ofter open a pinhole in the NAT because REGISTER is usually the first message sent from the user agent to the SIP server. A small example follows. Let's suppose that we have a single SER instance listening on two ports -- 5060 and 5090. Using a different port seems to be often necessary because of broken SIP ALGs (Application Level Gateways) that are often built into DSL routers or other devices. Such ALGs would mangle SIP requests and responses coming to and from port 5060 and the only option how to avoid such mangling is using a different port number. Let's suppose that we have two UAs beind NAT, one of them is configured to reach SER on port 5060, the other one is configured to use port 5090 (due to the reasons described above): SER +---------+ UA1 ---- NAT1 ----| 5060 | | | UA2 ---- NAT2 ----| 5090 | +---------+ Registrar and usrloc would store the public IP of NAT with each registered contact, thus it would know how to reach both user agents. In addition to the public IP and port of the NAT device, registrar would also remember the destination IP and port of the REGISTER request (the IP and port used in SER). If registrar did not store this information, it would not know what outbound socket it should use when sending SIP messages to the registered contact. It would use the default port number (often 5060) for such outgoing requests. When an INVITE for UA1 comes, everything would work because UA1 used port 5060 when registering and that is also the destination port in the pinhole being kept open in NAT1: SER INVITE UA1 +--------+ INVITE UA1 UA1 ---- NAT1 <------------- | 5060 | <---------------- | | UA2 ---- NAT2 | 5090 | +--------+ When an INVITE for UA2 comes, SER would again use port 5060 as the default outgoing source port number, but this time the message will be dropped by NAT2, because the pinhole opened during the registration has 5060 as the destination port number: SER INVITE UA2 +--------+ INVITE UA2 UA1 ---- NAT1 +------ | 5060 | <--------------- | | | UA2 ---- NAT2 X <----+ | 5090 | +--------+ That is the reason why registrar and usrloc also need to remember the IP and port used on the server side, that information would be used later when forwarding INVITEs: SER +--------+ INVITE UA2 UA1 ---- NAT1 | 5060 | <--------------- INVITE UA2 | | UA2 ---- NAT2 <------------- | 5090 | +--------+ Note The X next to NAT2 has disappeared in this picture which means that the message will be lucky enough to make it through. SER would encode this information into a SIP URI when saving contacts in database and later, after restart of SER, this information will be restored. The URI looks like: sip:1.2.3.4:5060;dstip=5.6.7.8;dstport=5090 Where the hostname part is the public IP of the NAT, the port (5060) is the port allocated by the NAT, "dstip" parameter is the IP used on SER side and "dstport" parameter is the port number used on SER side during registration. This information is later used to find the correct outgoing socket in SER. If no such socket can be found (it can happen if you reconfigure SER to use different ports or IPs), it will use the default IP/port again. 1.3. Synchronizing UA Internal Clock Many existing user agents support date and time synchronization from the registrar. Registrar can append "Date" header field to the 200 OK to REGISTER message received from the user agent. User agents that support time synchronization would read the current date and time from the header field and set internal clock to it. For example: SIP/2.0 200 OK Via: SIP/2.0/UDP 62.240.165.98;branch=z9hG4bK3E66B9EB CSeq: 1469 REGISTER To: "1 1" ;tag=2bd21cbe8bf183397d829c66d62463e6.0aea From: "1 1" Call-ID: 1767561454@62.240.165.98 Date: Fri, 02 Sep 2005 11:20:12 GMT You can use append_time function from textops module to append the header field to the reply. Put the function before save("location"); in the configuration file. Example 1. Adding Date header to Replies if (uri == myself) { if (method == "REGISTER") { append_time(); save("location"); break; }; }; 1.4. Processing +sip.instance parameter The GRUU extension for SIP (draft-ietf-sip-gruu-04) adds a new parameter to the URI of the Contact header in the REGISTER request to identify a UA instance globaly unique in the world. This is reached by adding a globaly unqiue identifier, the so called sip instace, as a parameter to the Contact URI within all REGISTER requests. Example 2. A Contact header with sip instance parameter Contact: ;q=1.0;+sip.instance="" By looking at this sip instance parameter the registrar can decide if the incoming request is just an update of an existing Contact URI or if a new Contact URI has to be added to the AOR of this account. Thus even after a reboot of the UA the new REGISTER can be determined the request as an update of an old, already existing entry. The registrar module now looks for the sip instance parameter in the Contact URI: * If the sip instance parameter is not present in the URI of the Contact header the algorithm stays the same as like before the GRUU extension. Which means the registrar only compares the SIP URI from the Contact header with the existing URIs for the AOR (account). If exactly the same URI already exists the existing entry will be updated, otherwise the URI will be added to the list. * If the sip instace parameter is present in the URI of the Contact header it will be first searched for an existing Contact with exactly the same sip instance value. If an URI with exactly the same sip instance value exists already in the database, the existing entry will be replaced with the new value, no matter if they differ or not. If no Contact with this sip instance value exists a new entry for it will be added. For example the following Contact would replace the Contact value from the example above in the usrloc database because the sip instance value is the same, although the Contact URI differs. Without the sip instance this would have created two entries in the usrloc database. Example 3. Different Contact with the same sip.instance Contact: ;q=1.0;+sip.instance="" 1.5. Dependencies Registrar module depends on the following SER modules: * usrloc - User Location Module. * sl - Stateless Replies. 1.6. Parameters 1.6.1. default_expires (integer) If the processed message contains neither Expires HFs nor expires contact parameters, this value will be used for newly created usrloc records. The parameter contains number of second to expire (for example use 3600 for one hour). Default value is 3600. Example 4. Set default_expires parameter ... modparam("registrar", "default_expires", 1800) ... 1.6.2. min_expires (integer) The minimum expires value of a Contact, values lower than this minimum will be automatically set to the minimum. Value 0 disables the checking. Default value is 60. Example 5. Set min_expires parameter ... modparam("registrar", "min_expires", 60) ... 1.6.3. max_expires (integer) The maximum expires value of a Contact, values higher than this maximum will be automatically set to the maximum. Value 0 disables the checking. Default value is 0. Example 6. Set max_expires parameter ... modparam("registrar", "max_expires", 120) ... 1.6.4. default_q (integer) The parameter represents default q value for new contacts. Because ser doesn't support float parameter types, the value in the parameter is divided by 100 and stored as float. For example, if you want default_q to be 0.38, use value 38 here. Default value is 0. Example 7. Set default_q parameter ... modparam("registrar", "default_q", 100) ... 1.6.5. append_branches (integer) The parameter controls how lookup function processes multiple contacts. If there are multiple contacts for the given username in usrloc and this parameter is set to 1, Request-URI will be overwritten with the highest-q rated contact and the rest will be appended to sip_msg structure and can be later used by tm for forking. If the parameter is set to 0, only Request-URI will be overwritten with the highest-q rated contact and the rest will be left unprocessed. Default value is 1. Example 8. Set append_branches parameter ... modparam("registrar", "append_branches", 0) ... 1.6.6. case_sensitive (integer) Note This parameter was removed (obsolete since ser 2.0). If set to 1 then AOR comparison will be case sensitive, if set to 0 then AOR comparison will be case insensitive--This is recommended. Default value is 0. Example 9. Set case_sensitive parameter ... modparam("registrar", "case_sensitive", 1) ... 1.6.7. desc_time_order (integer) Note This parameter was removed (obsolete since ser 2.0). If set to 1 then all contacts will be ordered in descending modification time order. In this case the most recently updated/created contact will be used. Default value is 0. Example 10. Set desc_time_order parameter ... modparam("registrar", "desc_time_order", 1) ... 1.6.8. received_avp (integer) Registrar will store the value of the AVP configured by this parameter in the received column in the user location database. It will leave the column empty if the AVP is empty. The AVP should contain a SIP URI consisting of the source IP, port, and protocol of the REGISTER message being processed. Note The value of this parameter should be the same as the value of corresponding parameter of nathelper module. Default value is 42. Example 11. Set received_avp parameter ... modparam("registrar", "received_avp", 43) ... 1.6.9. received_param (integer) The name of the parameter that will be appended to Contacts of 200 OK when the received URI was set by nathelper module. Default value is "received". Example 12. Set received_param parameter ... modparam("registrar", "received_param", "rcv") ... 1.6.10. max_contacts (integer) The parameter can be used to limit the number of contacts per AOR (Address of Record) in the user location database. Value 0 disables the check. Default value is 0. Example 13. Set max_contacts parameter ... # Allow no more than 10 contacts per AOR modparam("registrar", "max_contacts", 10) ... 1.6.11. retry_after (integer) The registrar can generate 5xx reply to REGISTER in various situations. It can, for example, happen when the max_contacts parameter is set and the processing of REGISTER request would exceed the limit. In this case the registrar would generate "503 Service Unavailable" response. If you want to add the Retry-After header field in 5xx replies, set this parameter to a value grater than zero (0 means do not add the header field). See section 20.33 of RFC3261 for more details. Default value is 0 (disabled). Example 14. Set retry_after parameter ... modparam("registrar", "retry_after", 30) ... 1.6.12. save_nat_flag (flagname or integer) The contact will be marked as behind the NAT if this flag is set before calling one of registrar save*() functions. See also load_nat_flag. The default value is 4 (flag number 4). Example 15. Set save_nat_flag parameter flags FLAG_NAT; ... modparam("registrar", "save_nat_flag", "FLAG_NAT") ... route{ ... if (nat_uac_test("19")) setflag(FLAG_NAT); ... } 1.6.13. load_nat_flag (flagname or integer) This flag will be set by the registrar lookup*() functions if the contact was marked as behind the NAT during its registration. See also save_nat_flag. The default value is 4 (flag number 4). Example 16. Set load_nat_flag parameter flags FLAG_NAT; ... modparam("registrar", "load_nat_flag", "FLAG_NAT") ... route{ ... if (lookup_contacts("location")) { if (isflagset(FLAG_NAT)){ log(1, "natted contact"); # use rtpproxy a.s.o } ... } ... } 1.6.14. trust_received_flag (flagname or integer) If this flag is set, the received information added to the REGISTER contacts on another machine (e.g. upstream proxy or replication peer) by the fix_nated_register() function from the nathelper module (ser version, under modules_s/) is trusted. If it is not set, it will be ignored. It is especially useful in REGISTER replication scenarios of registrars behind a transparent load balancer (one IP) or a proxy / load balancer that can make use of the original received information (in this case combined with received_to_uri). A contact with received information looks like: ;received="sip:1.2.3.4:3412;transport=TCP;dstip= 4.5.6.7;dstport=5060". Besides the normal flags values (flag names or positive integers), there are 2 special integer values: -1 and -2. If trust_received_flag is set to -1 it is equivalent with disabling it globally (no contact "received" parameter will be trusted, they will be all ignored). If set to -2 all the contact "received" parameters will be trusted. See also fix_nated_register() in ser nathelper version (modules_s/nathelper). The default value is -2 (always trust received), due to config compatibility reasons with older ser versions. Example 17. Set trust_received_flag parameter flags FLAG_REPLICATED; ... modparam("registrar", "trust_received_flag", "FLAG_REPLICATED") ... route{ ... if (dst_ip==224.0.1.75 && method == "REGISTER") { # REGISTER replicated on multicast (trusted) setflag(FLAG_REPLICATED); save_mem_nr("location"); ... } ... } 1.6.15. received_to_uri (boolean) If set the lookup*() functions will add a contact received information to the uri, instead of using the contact received information as a destination for the message. The resulting message uri will look like: ;received="sip:src_ip:src_port;transport=proto;dstip=iface _ip;dstport=iface_port". Is is useful when the registrar is behind some other proxy (e.g. load balancer) and using Path is not desired. In this case the outer proxy would have to look at the message uri, remove the "received" parameter from the uri and use it as destination for the message. The default value is 0 (off). Example 18. Set received_to_uri parameter ... modparam("registrar", "received_to_uri", 1) # example uri for a message received on 1.2.3.4:5060 from 192.168.1.1:5060: # ;received="sip:192.168.1.1:5060;dstip=1.2.3.4;dstport=5060" 1.6.16. reply_code_attr (avp name) If one of the save*() functions that do not send a reply is used (save_contacts_no_reply(), save_noreply() or save_mem_nr()), an AVP with the given name will be set to the code of the reply that should be send by other means. See also reply_reason_attr and contact_attr. The default value is "$code". Example 19. Set reply_code_attr, reply_reason_attr parameters and contact_attr modparam("registrar", "reply_code_attr", "$repl_code") modparam("registrar", "reply_reason_attr", "$repl_reason") modparam("registrar", "contact_attr", "$contact") ... route{ ... if (!save_noreply("location")) { append_to_reply("$contact"); sl_send_reply($reply_code, $repl_reason); } ... } 1.6.17. reply_reason_attr (avp name) See reply_code_attr above. The default value is "$reason". 1.6.18. contact_attr (avp name) See reply_code_attr above. The default value is "$contact". 1.6.19. aor_attr (avp name) If set to an AVP name, the AOR will be taken from this AVP, instead of the message "To:" header (when using one of the save*() functions). The default value is "$aor". Example 20. Set aor_attr modparam("registrar", "aor_attr", "$my_aor") ... route{ ... if (src_ip==1.2.3.4) $my_aor=@msg.header["From"]; save("location"); ... } 1.6.20. server_id_attr (avp name) If set to an AVP name, the server ID associated to the contact will be set to the AVP value. If not set or the AVP is not found, the server ID will be set to the server_id of the current server. The default value is "$server_id". See also server_id (core parameter). Example 21. Set server_id_attr ... modparam("registrar", "server_id_attr", "$remote_server_id") ... route{ ... if (dst_ip==224.0.1.75 && method == "REGISTER") { # REGISTER replicated on multicast, use the originator server_id # (saved in the custom SER-Server-ID header) $remote_server_id = @msg.header["SER-Server-ID"]; setflag(FLAG_REPLICATED); save_mem_nr("location"); ... } ... } 1.7. Functions 1.7.1. save(domain) The function processes a REGISTER message. It can add, remove or modify usrloc records depending on Contact and Expires HFs in the REGISTER message. On success, 200 OK will be returned listing all contacts that are currently in usrloc. On an error, error message will be send with a short description in reason phrase. Meaning of the parameters is as follows: * domain - Logical domain within registrar. If database is used then this must be name of the table which stores the contacts. Example 22. save usage ... save("location"); ... 1.7.2. save_noreply(domain) Same as save() but it doesn't send a reply. Meaning of the parameters is as follows: * domain - Logical domain within registrar. If database is used then this must be na e of the table which stores the contacts. Example 23. save_noreply usage ... save_noreply("location"); ... 1.7.3. lookup(domain) The functions extracts username from Request-URI and tries to find all contacts for the username in usrloc. If there are no such contacts, -1 will be returned. If there are such contacts, Request-URI will be overwritten with the contact that has the highest q value and optionally the rest will be appended to the message (depending on append_branches parameter value). Meaning of the parameters is as follows: * domain - Name of table that should be used for the lookup. Example 24. lookup usage ... lookup("location"); ... 1.7.4. registered(domain) The function returns true if the AOR in the Request-URI is registered, false otherwise. The function does not modify the message being process, it neither rewrites the Request-URI if a contact is found not append branches. Meaning of the parameters is as follows: * domain - Name of table that should be used for the lookup. Example 25. registered usage ... if (registered("location")) { sl_send_reply("100", "Trying"); ... }; ... kamailio-4.0.4/obsolete/registrar/reg_mod.h0000644000000000000000000000362612223032460017407 0ustar rootroot/* * $Id$ * * registrar module interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef REG_MOD_H #define REG_MOD_H #include "../../parser/msg_parser.h" #include "../../qvalue.h" #include "../../usr_avp.h" #include "../usrloc/usrloc.h" #include "../../modules/sl/sl.h" extern int default_expires; extern qvalue_t default_q; extern int append_branches; extern int load_nat_flag; extern int save_nat_flag; extern int trust_received_flag; extern int min_expires; extern int max_expires; extern int received_avp; extern float def_q; extern int received_to_uri; /*copy received to uri, don't add it to dst_uri*/ extern str rcv_param; extern str aor_attr; extern str server_id_attr; extern int max_contacts; extern usrloc_api_t ul; /* Structure containing pointers to usrloc functions */ extern sl_api_t slb; extern str reply_code_attr, reply_reason_attr, contact_attr; extern avp_ident_t avpid_code, avpid_reason, avpid_contact; #endif /* REG_MOD_H */ kamailio-4.0.4/obsolete/registrar/reply.c0000644000000000000000000002565412223032460017126 0ustar rootroot/* * $Id$ * * Send a reply * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2003-01-18: buffer overflow patch committed (Jan on behalf of Maxim) * 2003-01-21: Errors reported via Error-Info header field - janakj * 2003-09-11: updated to new build_lump_rpl() interface (bogdan) * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) */ #include #include "../../ut.h" #include "../../parser/msg_parser.h" #include "../../data_lump_rpl.h" #include "../usrloc/usrloc.h" #include "rerrno.h" #include "reg_mod.h" #include "regtime.h" #include "common.h" #include "reply.h" #define MAX_CONTACT_BUFFER 1024 #define E_INFO "P-Registrar-Error: " #define E_INFO_LEN (sizeof(E_INFO) - 1) #define CONTACT_BEGIN "Contact: " #define CONTACT_BEGIN_LEN (sizeof(CONTACT_BEGIN) - 1) #define Q_PARAM ";q=" #define Q_PARAM_LEN (sizeof(Q_PARAM) - 1) #define EXPIRES_PARAM ";expires=" #define EXPIRES_PARAM_LEN (sizeof(EXPIRES_PARAM) - 1) #define CONTACT_SEP ", " #define CONTACT_SEP_LEN (sizeof(CONTACT_SEP) - 1) /* * Buffer for Contact header field */ static struct { char* buf; int buf_len; int data_len; } contact = {0, 0, 0}; /* * Calculate the length of buffer needed to * print contacts */ static inline unsigned int calc_buf_len(ucontact_t* c, str* aor_filter) { unsigned int len; int qlen; len = 0; while(c) { if (VALID_CONTACT(c, act_time) && (!aor_filter->s || VALID_AOR(c, (*aor_filter)))) { if (len) len += CONTACT_SEP_LEN; len += 2 /* < > */ + c->c.len; qlen = len_q(c->q); if (qlen) len += Q_PARAM_LEN + qlen; len += EXPIRES_PARAM_LEN + INT2STR_MAX_LEN; if (c->received.s) { len += 1 /* ; */ + rcv_param.len + 1 /* = */ + 1 /* dquote */ + c->received.len + 1 /* dquote */ ; } } c = c->next; } if (len) len += CONTACT_BEGIN_LEN + CRLF_LEN; return len; } /* * Allocate a memory buffer and print Contact * header fields into it */ int build_contact(ucontact_t* c, str* aor_filter) { char *p, *cp; int fl, len; contact.data_len = calc_buf_len(c, aor_filter); if (!contact.data_len) return 0; if (!contact.buf || (contact.buf_len < contact.data_len)) { if (contact.buf) pkg_free(contact.buf); contact.buf = (char*)pkg_malloc(contact.data_len); if (!contact.buf) { contact.data_len = 0; contact.buf_len = 0; LOG(L_ERR, "build_contact(): No memory left\n"); return -1; } else { contact.buf_len = contact.data_len; } } p = contact.buf; memcpy(p, CONTACT_BEGIN, CONTACT_BEGIN_LEN); p += CONTACT_BEGIN_LEN; fl = 0; while(c) { if (VALID_CONTACT(c, act_time) && (!aor_filter->s || VALID_AOR(c, (*aor_filter)))) { if (fl) { memcpy(p, CONTACT_SEP, CONTACT_SEP_LEN); p += CONTACT_SEP_LEN; } else { fl = 1; } *p++ = '<'; memcpy(p, c->c.s, c->c.len); p += c->c.len; *p++ = '>'; len = len_q(c->q); if (len) { memcpy(p, Q_PARAM, Q_PARAM_LEN); p += Q_PARAM_LEN; memcpy(p, q2str(c->q, 0), len); p += len; } memcpy(p, EXPIRES_PARAM, EXPIRES_PARAM_LEN); p += EXPIRES_PARAM_LEN; cp = int2str((int)(c->expires - act_time), &len); memcpy(p, cp, len); p += len; if (c->received.s) { *p++ = ';'; memcpy(p, rcv_param.s, rcv_param.len); p += rcv_param.len; *p++ = '='; *p++ = '\"'; memcpy(p, c->received.s, c->received.len); p += c->received.len; *p++ = '\"'; } } c = c->next; } memcpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; contact.data_len = p - contact.buf; DBG("build_contact(): Created Contact HF: %.*s\n", contact.data_len, contact.buf); return 0; } #define MSG_200 "OK" #define MSG_400 "Bad Request" #define MSG_500 "Server Internal Error" #define MSG_503 "Service Unavailable" #define EI_R_FINE "No problem" /* R_FINE */ #define EI_R_UL_DEL_R "usrloc_record_delete failed" /* R_UL_DEL_R */ #define EI_R_UL_GET_R "usrloc_record_get failed" /* R_UL_GET */ #define EI_R_UL_NEW_R "usrloc_record_new failed" /* R_UL_NEW_R */ #define EI_R_INV_CSEQ "Invalid CSeq number" /* R_INV_CSEQ */ #define EI_R_UL_INS_C "usrloc_contact_insert failed" /* R_UL_INS_C */ #define EI_R_UL_INS_R "usrloc_record_insert failed" /* R_UL_INS_R */ #define EI_R_UL_DEL_C "usrloc_contact_delete failed" /* R_UL_DEL_C */ #define EI_R_UL_UPD_C "usrloc_contact_update failed" /* R_UL_UPD_C */ #define EI_R_TO_USER "No username in To URI" /* R_TO_USER */ #define EI_R_AOR_LEN "Address Of Record too long" /* R_AOR_LEN */ #define EI_R_AOR_PARSE "Error while parsing AOR" /* R_AOR_PARSE */ #define EI_R_INV_EXP "Invalid expires param in contact" /* R_INV_EXP */ #define EI_R_INV_Q "Invalid q param in contact" /* R_INV_Q */ #define EI_R_PARSE "Message parse error" /* R_PARSE */ #define EI_R_TO_MISS "To header not found" /* R_TO_MISS */ #define EI_R_CID_MISS "Call-ID header not found" /* R_CID_MISS */ #define EI_R_CS_MISS "CSeq header not found" /* R_CS_MISS */ #define EI_R_PARSE_EXP "Expires parse error" /* R_PARSE_EXP */ #define EI_R_PARSE_CONT "Contact parse error" /* R_PARSE_CONT */ #define EI_R_STAR_EXP "* used in contact and expires is not zero" /* R_STAR__EXP */ #define EI_R_STAR_CONT "* used in contact and more than 1 contact" /* R_STAR_CONT */ #define EI_R_OOO "Out of order request" /* R_OOO */ #define EI_R_RETRANS "Retransmission" /* R_RETRANS */ #define EI_R_UNESCAPE "Error while unescaping username" /* R_UNESCAPE */ #define EI_R_TOO_MANY "Too many registered contacts" /* R_TOO_MANY */ str error_info[] = { {EI_R_FINE, sizeof(EI_R_FINE) - 1}, {EI_R_UL_DEL_R, sizeof(EI_R_UL_DEL_R) - 1}, {EI_R_UL_GET_R, sizeof(EI_R_UL_GET_R) - 1}, {EI_R_UL_NEW_R, sizeof(EI_R_UL_NEW_R) - 1}, {EI_R_INV_CSEQ, sizeof(EI_R_INV_CSEQ) - 1}, {EI_R_UL_INS_C, sizeof(EI_R_UL_INS_C) - 1}, {EI_R_UL_INS_R, sizeof(EI_R_UL_INS_R) - 1}, {EI_R_UL_DEL_C, sizeof(EI_R_UL_DEL_C) - 1}, {EI_R_UL_UPD_C, sizeof(EI_R_UL_UPD_C) - 1}, {EI_R_TO_USER, sizeof(EI_R_TO_USER) - 1}, {EI_R_AOR_LEN, sizeof(EI_R_AOR_LEN) - 1}, {EI_R_AOR_PARSE, sizeof(EI_R_AOR_PARSE) - 1}, {EI_R_INV_EXP, sizeof(EI_R_INV_EXP) - 1}, {EI_R_INV_Q, sizeof(EI_R_INV_Q) - 1}, {EI_R_PARSE, sizeof(EI_R_PARSE) - 1}, {EI_R_TO_MISS, sizeof(EI_R_TO_MISS) - 1}, {EI_R_CID_MISS, sizeof(EI_R_CID_MISS) - 1}, {EI_R_CS_MISS, sizeof(EI_R_CS_MISS) - 1}, {EI_R_PARSE_EXP, sizeof(EI_R_PARSE_EXP) - 1}, {EI_R_PARSE_CONT, sizeof(EI_R_PARSE_CONT) - 1}, {EI_R_STAR_EXP, sizeof(EI_R_STAR_EXP) - 1}, {EI_R_STAR_CONT, sizeof(EI_R_STAR_CONT) - 1}, {EI_R_OOO, sizeof(EI_R_OOO) - 1}, {EI_R_RETRANS, sizeof(EI_R_RETRANS) - 1}, {EI_R_UNESCAPE, sizeof(EI_R_UNESCAPE) - 1}, {EI_R_TOO_MANY, sizeof(EI_R_TOO_MANY) - 1} }; int codes[] = { 200, /* R_FINE */ 500, /* R_UL_DEL_R */ 500, /* R_UL_GET */ 500, /* R_UL_NEW_R */ 400, /* R_INV_CSEQ */ 500, /* R_UL_INS_C */ 500, /* R_UL_INS_R */ 500, /* R_UL_DEL_C */ 500, /* R_UL_UPD_C */ 400, /* R_TO_USER */ 500, /* R_AOR_LEN */ 400, /* R_AOR_PARSE */ 400, /* R_INV_EXP */ 400, /* R_INV_Q */ 400, /* R_PARSE */ 400, /* R_TO_MISS */ 400, /* R_CID_MISS */ 400, /* R_CS_MISS */ 400, /* R_PARSE_EXP */ 400, /* R_PARSE_CONT */ 400, /* R_STAR_EXP */ 400, /* R_STAR_CONT */ 200, /* R_OOO */ 200, /* R_RETRANS */ 400, /* R_UNESCAPE */ 503 /* R_TOO_MANY */ }; /* * Send a reply */ int send_reply(struct sip_msg* _m) { long code; char* msg = MSG_200; /* makes gcc shut up */ char* buf; if (contact.data_len > 0) { add_lump_rpl( _m, contact.buf, contact.data_len, LUMP_RPL_HDR|LUMP_RPL_NODUP|LUMP_RPL_NOFREE); contact.data_len = 0; } code = codes[rerrno]; switch(code) { case 200: msg = MSG_200; break; case 400: msg = MSG_400; break; case 500: msg = MSG_500; break; case 503: msg = MSG_503; break; } if (code != 200) { buf = (char*)pkg_malloc(E_INFO_LEN + error_info[rerrno].len + CRLF_LEN + 1); if (!buf) { LOG(L_ERR, "send_reply(): No memory left\n"); return -1; } memcpy(buf, E_INFO, E_INFO_LEN); memcpy(buf + E_INFO_LEN, error_info[rerrno].s, error_info[rerrno].len); memcpy(buf + E_INFO_LEN + error_info[rerrno].len, CRLF, CRLF_LEN); add_lump_rpl( _m, buf, E_INFO_LEN + error_info[rerrno].len + CRLF_LEN, LUMP_RPL_HDR|LUMP_RPL_NODUP); } if (slb.zreply(_m, code, msg) == -1) { ERR("Error while sending %ld %s\n", code, msg); return -1; } else return 0; } /* * Release contact buffer if any */ void free_contact_buf(void) { if (contact.buf) { pkg_free(contact.buf); contact.buf = 0; contact.buf_len = 0; contact.data_len = 0; } } int setup_attrs(struct sip_msg* msg) { int code; str reason, c; avp_value_t val; code = codes[rerrno]; if (reply_code_attr.len) { val.n = code; if (add_avp(avpid_code.flags, avpid_code.name, val) < 0) { ERR("Error while creating reply code attribute\n"); return -1; } } if (reply_reason_attr.len) { reason.s = NULL; reason.len = 0; switch(code) { case 200: reason.s = MSG_200; reason.len = sizeof(MSG_200) - 1; break; case 400: reason.s = MSG_400; reason.len = sizeof(MSG_400) - 1; break; case 500: reason.s = MSG_500; reason.len = sizeof(MSG_500) - 1; break; case 503: reason.s = MSG_503; reason.len = sizeof(MSG_503) - 1; break; } val.s = reason; if (add_avp(avpid_reason.flags | AVP_VAL_STR, avpid_reason.name, val) < 0) { ERR("Error while creating reply reason attribute\n"); return -1; } } if (contact_attr.len && contact.data_len > 0) { c.s = contact.buf; c.len = contact.data_len; val.s = c; if (add_avp(avpid_contact.flags | AVP_VAL_STR, avpid_contact.name, val) < 0) { ERR("Error while creating contact attribute\n"); return -1; } contact.data_len = 0; } return 0; } kamailio-4.0.4/obsolete/registrar/regtime.c0000644000000000000000000000234412223032460017416 0ustar rootroot/* * $Id$ * * Registrar time related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "regtime.h" #include "../../ser_time.h" time_t act_time; /* * Get actual time and store * value in act_time */ void get_act_time(void) { act_time = ser_time(0); } kamailio-4.0.4/obsolete/registrar/lookup.h0000644000000000000000000000347712223032460017310 0ustar rootroot/* * $Id$ * * Lookup contacts in usrloc * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef LOOKUP_H #define LOOKUP_H #include "../../parser/msg_parser.h" /* * Lookup a contact in usrloc and rewrite R-URI if found */ int lookup(struct sip_msg* _m, char* _t, char* _s); /* * Lookup a contact in usrloc and rewrite R-URI if found */ int lookup2(struct sip_msg* msg, char* table, char* aor); /* * Return true if the AOR in the Request-URI is registered, * it is similar to lookup but registered neither rewrites * the Request-URI nor appends branches */ int registered(struct sip_msg* _m, char* _t, char* _s); /* * Return true if the AOR in the Request-URI is registered, * it is similar to lookup but registered neither rewrites * the Request-URI nor appends branches */ int registered2(struct sip_msg* msg, char* table, char* p2); #endif /* LOOKUP_H */ kamailio-4.0.4/obsolete/registrar/common.c0000644000000000000000000000244112223032460017250 0ustar rootroot/* * $Id$ * * Common stuff * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History * ------ * 2003-02-14 un-escaping added (janakj) * */ #include #include "../../dprint.h" #include "../../ut.h" /* q_memchr */ #include "../../parser/parse_uri.h" #include "rerrno.h" #include "reg_mod.h" #include "common.h" kamailio-4.0.4/obsolete/registrar/rerrno.h0000644000000000000000000000455312223032460017302 0ustar rootroot/* * $Id$ * * Registrar errno * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RERRNO_H #define RERRNO_H typedef enum rerr { R_FINE = 0, /* Everything went OK */ R_UL_DEL_R, /* Usrloc record delete failed */ R_UL_GET_R, /* Usrloc record get failed */ R_UL_NEW_R, /* Usrloc new record failed */ R_INV_CSEQ, /* Invalid CSeq value */ R_UL_INS_C, /* Usrloc insert contact failed */ R_UL_INS_R, /* Usrloc insert record failed */ R_UL_DEL_C, /* Usrloc contact delete failed */ R_UL_UPD_C, /* Usrloc contact update failed */ R_TO_USER, /* No username part in To URI */ R_AOR_LEN, /* Address Of Record too long */ R_AOR_PARSE, /* Error while parsing Address Of Record */ R_INV_EXP, /* Invalid expires parameter in contact */ R_INV_Q, /* Invalid q parameter in contact */ R_PARSE, /* Error while parsing message */ R_TO_MISS, /* Missing To header field */ R_CID_MISS, /* Missing Call-ID header field */ R_CS_MISS, /* Missing CSeq header field */ R_PARSE_EXP, /* Error while parsing Expires */ R_PARSE_CONT, /* Error while parsing Contact */ R_STAR_EXP, /* star and expires != 0 */ R_STAR_CONT, /* star and more contacts */ R_OOO, /* Out-Of-Order request */ R_RETRANS, /* Request is retransmission */ R_UNESCAPE, /* Error while unescaping username */ R_TOO_MANY /* Too many contacts */ } rerr_t; extern rerr_t rerrno; #endif /* RERRNO_H */ kamailio-4.0.4/obsolete/registrar/reply.h0000644000000000000000000000272512223032460017125 0ustar rootroot/* * $Id$ * * Send a reply * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef REPLY_H #define REPLY_H #include "../../str.h" #include "../../parser/msg_parser.h" #include "ucontact.h" /* * Send a reply */ int send_reply(struct sip_msg* _m); /* * Build Contact HF for reply */ int build_contact(ucontact_t* c, str* aor); /* * Release contact buffer if any */ void free_contact_buf(void); /* * Sets the reason and contact attributes. */ int setup_attrs(struct sip_msg* msg); #endif /* REPLY_H */ kamailio-4.0.4/obsolete/registrar/Makefile0000644000000000000000000000041412223032460017252 0ustar rootroot# $Id$ # # registrar module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=registrar.so LIBS= DEFS+=-I../.. -I../usrloc DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/registrar/rerrno.c0000644000000000000000000000210412223032460017263 0ustar rootroot/* * $Id$ * * Registrar errno * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "rerrno.h" rerr_t rerrno; kamailio-4.0.4/obsolete/registrar/common.h0000644000000000000000000000241712223032460017260 0ustar rootroot/* * $Id$ * * Common stuff * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef COMMON_H #define COMMON_H #include "../../str.h" #define VALID_AOR(contact, aor_filter) \ ((aor_filter).len == (contact)->aor.len && \ !strncasecmp((aor_filter).s, (contact)->aor.s, (aor_filter.len))) #endif /* COMMON_H */ kamailio-4.0.4/obsolete/registrar/sip_msg.h0000644000000000000000000000454412223032460017434 0ustar rootroot/* * $Id$ * * SIP message related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef SIP_MSG_H #define SIP_MSG_H #include "../../qvalue.h" #include "../../parser/msg_parser.h" #include "../../parser/contact/parse_contact.h" /* * Parse the whole message and bodies of all header fields * that will be needed by registrar */ int parse_message(struct sip_msg* _m); /* * Check if the originating REGISTER message was formed correctly * The whole message must be parsed before calling the function * _s indicates whether the contact was star */ int check_contacts(struct sip_msg* _m, int* _s); /* * Get the first contact in message */ contact_t* get_first_contact(struct sip_msg* _m); /* * Get next contact in message */ contact_t* get_next_contact(contact_t* _c); /* * Calculate absolute expires value per contact as follows: * 1) If the contact has expires value, use the value. If it * is not zero, add actual time to it * 2) If the contact has no expires parameter, use expires * header field in the same way * 3) If the message contained no expires header field, use * the default value */ int calc_contact_expires(struct sip_msg* _m, param_t* _ep, int* _e); /* * Calculate contact q value as follows: * 1) If q parameter exist, use it * 2) If the parameter doesn't exist, use default value */ int calc_contact_q(param_t* _q, qvalue_t* _r); #endif /* SIP_MSG_H */ kamailio-4.0.4/obsolete/registrar/lookup.c0000644000000000000000000002403712223032460017276 0ustar rootroot/* * $Id$ * * Lookup contacts in usrloc * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2005-08-01 fix: "received=" is used also for the branches (andrei) * 2005-04-22 added received_to_uri option support (andrei) * 2003-03-12 added support for zombie state (nils) * 2005-02-25 lookup() forces the socket stored in USRLOC (bogdan) */ #include #include "../../ut.h" #include "../../dset.h" #include "../../str.h" #include "../../config.h" #include "../../action.h" #include "../usrloc/usrloc.h" #include "../../error.h" #include "../../id.h" #include "../../sr_module.h" #include "common.h" #include "regtime.h" #include "reg_mod.h" #include "lookup.h" #include "../../usr_avp.h" /* new_uri= uri "; received=" received * returns 0 on success, < 0 on error (out of memory) * new_uri.s is pkg_malloc'ed (so you must pkg_free it when you are done * with it) */ static inline int add_received(str* new_uri, str* uri, str* received) { /* copy received into an uri param */ new_uri->len=uri->len+RECEIVED_LEN+1+received->len+1; new_uri->s=pkg_malloc(new_uri->len+1); if (new_uri->s==0){ LOG(L_ERR, "ERROR: add_received(): out of memory\n"); return -1; } memcpy(new_uri->s, uri->s, uri->len); memcpy(new_uri->s+uri->len, RECEIVED, RECEIVED_LEN); new_uri->s[uri->len+RECEIVED_LEN]='"'; memcpy(new_uri->s+uri->len+RECEIVED_LEN+1, received->s, received->len); new_uri->s[new_uri->len-1]='"'; new_uri->s[new_uri->len]=0; /* for debugging */ return 0; } /* * Lookup contact in the database and rewrite Request-URI */ int lookup(struct sip_msg* _m, char* _t, char* _s) { urecord_t* r; str uid; ucontact_t* ptr; int res; unsigned int nat; str new_uri; nat = 0; if (get_to_uid(&uid, _m) < 0) return -1; get_act_time(); ul.lock_udomain((udomain_t*)_t); res = ul.get_urecord((udomain_t*)_t, &uid, &r); if (res < 0) { LOG(L_ERR, "lookup(): Error while querying usrloc\n"); ul.unlock_udomain((udomain_t*)_t); return -2; } if (res > 0) { DBG("lookup(): '%.*s' Not found in usrloc\n", uid.len, ZSW(uid.s)); ul.unlock_udomain((udomain_t*)_t); return -3; } ptr = r->contacts; while ((ptr) && !VALID_CONTACT(ptr, act_time)) ptr = ptr->next; if (ptr) { if (ptr->received.s && ptr->received.len) { if (received_to_uri){ if (add_received(&new_uri, &ptr->c, &ptr->received)<0){ LOG(L_ERR, "ERROR: lookup(): out of memory\n"); return -4; } /* replace the msg uri */ if (_m->new_uri.s) pkg_free(_m->new_uri.s); _m->new_uri=new_uri; _m->parsed_uri_ok=0; ruri_mark_new(); goto skip_rewrite_uri; }else if (set_dst_uri(_m, &ptr->received) < 0) { ul.unlock_udomain((udomain_t*)_t); return -4; } } if (rewrite_uri(_m, &ptr->c) < 0) { LOG(L_ERR, "lookup(): Unable to rewrite Request-URI\n"); ul.unlock_udomain((udomain_t*)_t); return -4; } if (ptr->sock) { set_force_socket(_m, ptr->sock); } skip_rewrite_uri: set_ruri_q(ptr->q); nat |= ptr->flags & FL_NAT; ptr = ptr->next; } else { /* All contacts expired */ ul.unlock_udomain((udomain_t*)_t); return -5; } /* Append branches if enabled */ if (!append_branches) goto skip; while(ptr) { if (VALID_CONTACT(ptr, act_time)) { if (received_to_uri && ptr->received.s && ptr->received.len){ if (add_received(&new_uri, &ptr->c, &ptr->received)<0){ LOG(L_ERR, "ERROR: lookup(): branch: out of memory\n"); goto cont; /* try to continue */ } if (append_branch(_m, &new_uri, 0, 0, ptr->q, 0, 0, 0, 0) == -1) { LOG(L_ERR, "lookup(): Error while appending a branch\n"); pkg_free(new_uri.s); if (ser_error==E_TOO_MANY_BRANCHES) goto skip; else goto cont; /* try to continue, maybe we have an oversized contact */ } pkg_free(new_uri.s); /* append_branch doesn't free it */ }else{ if (append_branch(_m, &ptr->c, &ptr->received, 0 /* path */, ptr->q, 0 /* brflags*/, ptr->sock, 0, 0) == -1) { LOG(L_ERR, "lookup(): Error while appending a branch\n"); goto skip; /* Return OK here so the function succeeds */ } } nat |= ptr->flags & FL_NAT; } cont: ptr = ptr->next; } skip: ul.unlock_udomain((udomain_t*)_t); if (nat) setflag(_m, load_nat_flag); return 1; } /* * Return true if the AOR in the Request-URI is registered, * it is similar to lookup but registered neither rewrites * the Request-URI nor appends branches */ int registered(struct sip_msg* _m, char* _t, char* _s) { str uid; urecord_t* r; ucontact_t* ptr; int res; if (get_to_uid(&uid, _m) < 0) return -1; ul.lock_udomain((udomain_t*)_t); res = ul.get_urecord((udomain_t*)_t, &uid, &r); if (res < 0) { ul.unlock_udomain((udomain_t*)_t); LOG(L_ERR, "registered(): Error while querying usrloc\n"); return -1; } if (res == 0) { ptr = r->contacts; while (ptr && !VALID_CONTACT(ptr, act_time)) { ptr = ptr->next; } if (ptr) { ul.unlock_udomain((udomain_t*)_t); DBG("registered(): '%.*s' found in usrloc\n", uid.len, ZSW(uid.s)); return 1; } } ul.unlock_udomain((udomain_t*)_t); DBG("registered(): '%.*s' not found in usrloc\n", uid.len, ZSW(uid.s)); return -1; } /* * Lookup contact in the database and rewrite Request-URI, * and filter them by aor */ int lookup2(struct sip_msg* msg, char* table, char* p2) { urecord_t* r; str uid; ucontact_t* ptr; int res; unsigned int nat; str new_uri, aor; nat = 0; if (get_str_fparam(&aor, msg, (fparam_t*)p2) != 0) { ERR("Unable to get the AOR value\n"); return -1; } if (get_to_uid(&uid, msg) < 0) return -1; get_act_time(); ul.lock_udomain((udomain_t*)table); res = ul.get_urecord((udomain_t*)table, &uid, &r); if (res < 0) { ERR("Error while querying usrloc\n"); ul.unlock_udomain((udomain_t*)table); return -2; } if (res > 0) { DBG("'%.*s' Not found in usrloc\n", uid.len, ZSW(uid.s)); ul.unlock_udomain((udomain_t*)table); return -3; } ptr = r->contacts; while (ptr && (!VALID_CONTACT(ptr, act_time) || !VALID_AOR(ptr, aor))) ptr = ptr->next; if (ptr) { if (ptr->received.s && ptr->received.len) { if (received_to_uri){ if (add_received(&new_uri, &ptr->c, &ptr->received) < 0) { ERR("Out of memory\n"); return -4; } /* replace the msg uri */ if (msg->new_uri.s) pkg_free(msg->new_uri.s); msg->new_uri = new_uri; msg->parsed_uri_ok = 0; ruri_mark_new(); goto skip_rewrite_uri; } else if (set_dst_uri(msg, &ptr->received) < 0) { ul.unlock_udomain((udomain_t*)table); return -4; } } if (rewrite_uri(msg, &ptr->c) < 0) { ERR("Unable to rewrite Request-URI\n"); ul.unlock_udomain((udomain_t*)table); return -4; } if (ptr->sock) { set_force_socket(msg, ptr->sock); } skip_rewrite_uri: set_ruri_q(ptr->q); nat |= ptr->flags & FL_NAT; ptr = ptr->next; } else { /* All contacts expired */ ul.unlock_udomain((udomain_t*)table); return -5; } /* Append branches if enabled */ if (!append_branches) goto skip; while(ptr) { if (VALID_CONTACT(ptr, act_time) && VALID_AOR(ptr, aor)) { if (received_to_uri && ptr->received.s && ptr->received.len) { if (add_received(&new_uri, &ptr->c, &ptr->received) < 0) { ERR("branch: out of memory\n"); goto cont; /* try to continue */ } if (append_branch(msg, &new_uri, 0, 0, ptr->q, 0, 0, 0, 0) == -1) { ERR("Error while appending a branch\n"); pkg_free(new_uri.s); if (ser_error == E_TOO_MANY_BRANCHES) goto skip; else goto cont; /* try to continue, maybe we have an oversized contact */ } pkg_free(new_uri.s); /* append_branch doesn't free it */ } else { if (append_branch(msg, &ptr->c, &ptr->received, 0 /* path */, ptr->q, 0, ptr->sock, 0, 0) == -1) { ERR("Error while appending a branch\n"); goto skip; /* Return OK here so the function succeeds */ } } nat |= ptr->flags & FL_NAT; } cont: ptr = ptr->next; } skip: ul.unlock_udomain((udomain_t*)table); if (nat) setflag(msg, load_nat_flag); return 1; } /* * Return true if the AOR in the Request-URI is registered, * it is similar to lookup but registered neither rewrites * the Request-URI nor appends branches */ int registered2(struct sip_msg* _m, char* _t, char* p2) { str uid, aor; urecord_t* r; ucontact_t* ptr; int res; if (get_str_fparam(&aor, _m, (fparam_t*)p2) != 0) { ERR("Unable to get the AOR value\n"); return -1; } if (get_to_uid(&uid, _m) < 0) return -1; ul.lock_udomain((udomain_t*)_t); res = ul.get_urecord((udomain_t*)_t, &uid, &r); if (res < 0) { ul.unlock_udomain((udomain_t*)_t); LOG(L_ERR, "registered(): Error while querying usrloc\n"); return -1; } if (res == 0) { ptr = r->contacts; while (ptr && (!VALID_CONTACT(ptr, act_time) || !VALID_AOR(ptr, aor))) { ptr = ptr->next; } if (ptr) { ul.unlock_udomain((udomain_t*)_t); DBG("registered(): '%.*s' found in usrloc\n", uid.len, ZSW(uid.s)); return 1; } } ul.unlock_udomain((udomain_t*)_t); DBG("registered(): '%.*s' not found in usrloc\n", uid.len, ZSW(uid.s)); return -1; } kamailio-4.0.4/obsolete/rls/0000755000000000000000000000000012223032460014411 5ustar rootrootkamailio-4.0.4/obsolete/rls/virtual_subscription.c0000644000000000000000000003207612223032460021057 0ustar rootroot#include "rl_subscription.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include #include "rls_mod.h" #include "result_codes.h" #include #include #include #include #include #include #include "rls_data.h" #include "rls_auth.h" /* shared structure holding the data */ typedef struct { virtual_subscription_t *first; virtual_subscription_t *last; /* hash, ... */ notifier_domain_t *domain; qsa_content_type_t *ct_presence_info; qsa_content_type_t *ct_raw; } vs_data_t; static vs_data_t *vsd = NULL; /******** global functions (initialization) ********/ int vs_init() { static str presence_info = STR_STATIC_INIT(CT_PRESENCE_INFO); static str raw = STR_STATIC_INIT(CT_RAW); vsd = (vs_data_t*)mem_alloc(sizeof(vs_data_t)); if (!vsd) { LOG(L_ERR, "vs_init(): memory allocation error\n"); return -1; } vsd->first = NULL; vsd->last = NULL; vsd->domain = qsa_get_default_domain(); if (!vsd->domain) { LOG(L_ERR, "vs_init(): can't register notifier domain\n"); return -1; } DEBUG_LOG("QSA (vs) domain: %p\n", vsd->domain); vsd->ct_presence_info = register_content_type(vsd->domain, &presence_info, (destroy_function_f)free_presentity_info); if (!vsd->ct_presence_info) { ERR("can't register QSA content type\n"); return -1; } else TRACE("RLS_PRESENCE_INFO: %p\n", vsd->ct_presence_info); vsd->ct_raw = register_content_type(vsd->domain, &raw, (destroy_function_f)free_raw_presence_info); if (!vsd->ct_raw) { ERR("can't register QSA content type\n"); return -1; } else TRACE("RLS_RAW: %p\n", vsd->ct_raw); return 0; } int vs_destroy() { /* virtual subscriptions are freed in rls_free */ if (vsd) { qsa_release_domain(vsd->domain); vsd->domain = NULL; mem_free(vsd); vsd = NULL; } return 0; } /******** Helper functions ********/ /* sets new documents (frees them if equal) */ static void set_vs_document(virtual_subscription_t *vs, str_t *new_doc, str_t *new_content_type) { if (str_case_equals(&vs->state_document, new_doc) == 0) { /* DEBUG("new document is equal to the older one\n"); */ str_free_content(new_doc); } else { str_free_content(&vs->state_document); if (new_doc) vs->state_document = *new_doc; else str_clear(&vs->state_document); vs->changed = 1; } if (str_case_equals(&vs->content_type, new_content_type) == 0) { /* DEBUG("new content-type is equal to the older one\n"); */ str_free_content(new_content_type); } else { str_free_content(&vs->content_type); if (new_content_type) vs->content_type = *new_content_type; else str_clear(&vs->content_type); vs->changed = 1; } } /* duplicates documents if changed */ static int set_vs_document_dup(virtual_subscription_t *vs, str_t *new_doc, str_t *new_content_type) { if (str_case_equals(&vs->state_document, new_doc) == 0) { /* DEBUG("new document is equal to the older one\n"); */ } else { str_free_content(&vs->state_document); str_dup(&vs->state_document, new_doc); vs->changed = 1; } if (str_case_equals(&vs->content_type, new_content_type) == 0) { /* DEBUG("new content-type is equal to the older one\n"); */ } else { str_free_content(&vs->content_type); str_dup(&vs->content_type, new_content_type); vs->changed = 1; } return 0; } static void propagate_change(virtual_subscription_t *vs) { if (vs->subscription->type == rls_internal_subscription) { /* propagate change to higher level */ rls_generate_notify(vs->subscription, 1); } else { /* external subscriptions will send NOTIFY only sometimes * => we mark it as changed now */ vs->subscription->changed++; /* FIXME: put this subscription in some queue? (needs remove from it * when freeing!) */ if (rls) rls->changed_subscriptions++; /* change indicator */ } } void process_rls_notification(virtual_subscription_t *vs, client_notify_info_t *info) { presentity_info_t *pinfo; raw_presence_info_t *raw; str_t new_doc = STR_NULL; str_t new_type = STR_NULL; subscription_status_t old_status; if ((!vs) || (!info)) return; DBG("Processing notification for VS %p\n", vs); /* FIXME: put information from more sources together ? */ old_status = vs->status; switch (info->status) { case qsa_subscription_active: vs->status = subscription_active; break; case qsa_subscription_pending: vs->status = subscription_pending; break; case qsa_subscription_rejected: vs->status = subscription_terminated; break; case qsa_subscription_terminated: vs->status = subscription_terminated; break; } if (old_status != vs->status) vs->changed = 1; if (info->content_type == vsd->ct_raw) { DEBUG("Processing raw notification\n"); raw = (raw_presence_info_t*)info->data; if (!raw) return; /* document MUST be duplicated !!! */ if (set_vs_document_dup(vs, &raw->pres_doc, &raw->content_type) < 0) { ERR("can't set new status document for VS %p\n", vs); return; } } else { if (info->content_type == vsd->ct_presence_info) { DEBUG("Processing structured notification\n"); pinfo = (presentity_info_t*)info->data; if (!pinfo) { str_clear(&new_doc); str_clear(&new_type); } else { if (create_pidf_document(pinfo, &new_doc, &new_type) < 0) { ERR("can't create PIDF document\n"); str_free_content(&vs->state_document); str_free_content(&vs->content_type); return; } set_vs_document(vs, &new_doc, &new_type); } } else { if (info->content_type) ERR("received unacceptable notification (%.*s)\n", FMT_STR(info->content_type->name)); else ERR("received unacceptable notification without content type\n"); str_free_content(&vs->state_document); str_free_content(&vs->content_type); return; } } if (vs->changed) propagate_change(vs); } void process_internal_notify(virtual_subscription_t *vs, str_t *new_state_document, str_t *new_content_type) { if (!vs) return; DBG("Processing internal notification for VS %p\n", vs); /* don't copy document - use it directly */ set_vs_document(vs, new_state_document, new_content_type); if (vs->changed) propagate_change(vs); } #if 0 static void mark_as_modified(virtual_subscription_t *vs) { rl_subscription_t *rls = vs->subscription; switch (rls->type) { case rls_external_subscription: if (sm_subscription_pending(&rls->u.external) == 0) { /* pending subscription will not be notified */ return; } break; case rls_internal_subscription: /* FIXME: something like above? */ break; } /* NOTIFY should be send only for nonpending subscriptions (or active?)*/ vs->subscription->changed++; DEBUG_LOG("RL subscription status changed (%p, %d)\n", rls, rls->changed); } static void vs_timer_cb(unsigned int ticks, void *param) { virtual_subscription_t *vs; int changed = 0; int cntr = 0; time_t start, stop; start = time(NULL); rls_lock(); /* process all messages for virtual subscriptions */ vs = vsd->first; while (vs) { if (process_vs_messages(vs) > 0) { DEBUG_LOG("VS status changed\n"); mark_as_modified(vs); changed = 1; } vs = vs->next; cntr++; /* debugging purposes */ } /* TRACE_LOG("processed messages for %d virtual subscription(s)\n", cntr); */ if (changed) { /* this could be called from some rli_timer ? */ notify_all_modified(); } rls_unlock(); stop = time(NULL); if (stop - start > 1) WARN("vs_timer_cb took %d secs\n", (int) (stop - start)); } #endif static int add_to_vs_list(virtual_subscription_t *vs) { if (!vs) return RES_INTERNAL_ERR; if (!vsd) { LOG(L_ERR, "vs_add(): vsd not set!\n"); return RES_INTERNAL_ERR; } DOUBLE_LINKED_LIST_ADD(vsd->first, vsd->last, vs); return RES_OK; } static int remove_from_vs_list(virtual_subscription_t *vs) { if (!vs) return RES_INTERNAL_ERR; if (!vsd) { LOG(L_ERR, "vs_remove(): vsd not set!\n"); return RES_INTERNAL_ERR; } DOUBLE_LINKED_LIST_REMOVE(vsd->first, vsd->last, vs); return RES_OK; } int xcap_query_rls_services(xcap_query_params_t *xcap_params, const str *uri, const str *package, flat_list_t **dst) { if (dst) *dst = NULL; if (reduce_xcap_needs) return get_rls_from_full_doc(uri, xcap_params, package, dst); else return get_rls(uri, xcap_params, package, dst); } static int create_subscriptions(virtual_subscription_t *vs, int nesting_level) { /* create concrete local subscription */ str *package = NULL; str *subscriber = NULL; flat_list_t *flat = NULL; package = rls_get_package(vs->subscription); DEBUG_LOG("creating local subscription to %.*s\n", FMT_STR(vs->uri)); if ((nesting_level != 0) && (xcap_query_rls_services(&vs->subscription->xcap_params, &vs->uri, package, &flat) == 0)) { if (nesting_level > 0) nesting_level--; /* it is resource list -> do internal subscription to RLS */ if (rls_create_internal_subscription(vs, &vs->local_subscription_list, flat, nesting_level) != 0) { ERR("can't create internal subscription\n"); free_flat_list(flat); return -1; } free_flat_list(flat); vs->status = subscription_active; /* FIXME: rls_authorize_subscription(vs->local_subscription_list); */ } else { /* fill QSA subscription data */ clear_subscription_data(&vs->local_subscription_pres_data); vs->local_subscription_pres_data.dst = &rls->notify_mq; vs->local_subscription_pres_data.record_id = vs->uri; subscriber = rls_get_subscriber(vs->subscription); vs->local_subscription_pres_data.subscriber_data = vs; if (subscriber) vs->local_subscription_pres_data.subscriber_id = *subscriber; /* not RLS record -> do QSA subscription to given package */ vs->local_subscription_pres = subscribe(vsd->domain, package, &vs->local_subscription_pres_data); if (!vs->local_subscription_pres) { LOG(L_ERR, "can't create local subscription (pres)!\n"); return -1; } } return 0; } /******** VS manipulation ********/ int vs_create(str *uri, virtual_subscription_t **dst, display_name_t *dnames, rl_subscription_t *subscription, int nesting_level) { int res; display_name_t *d; if (!dst) return RES_INTERNAL_ERR; *dst = NULL; if (!uri) { LOG(L_ERR, "vs_create(): no uri given\n"); return RES_INTERNAL_ERR; } if ((!uri->s) || (uri->len < 1)) { LOG(L_ERR, "vs_create(): no uri given\n"); return RES_INTERNAL_ERR; } *dst = (virtual_subscription_t*)mem_alloc(sizeof(virtual_subscription_t) + uri->len + 1); if (!(*dst)) { LOG(L_ERR, "vs_create(): can't allocate memory\n"); return RES_MEMORY_ERR; } (*dst)->next = NULL; (*dst)->prev = NULL; vector_init(&(*dst)->display_names, sizeof(vs_display_name_t), 4); memcpy((*dst)->uri_str, uri->s, uri->len); (*dst)->uri.s = (*dst)->uri_str; (*dst)->uri.len = uri->len; (*dst)->state_document.len = 0; (*dst)->state_document.s = NULL; (*dst)->content_type.len = 0; (*dst)->content_type.s = NULL; (*dst)->status = subscription_pending; (*dst)->local_subscription_pres = NULL; (*dst)->local_subscription_list = NULL; (*dst)->subscription = subscription; (*dst)->changed = 0; generate_db_id(&(*dst)->dbid, *dst); add_to_vs_list(*dst); DBG("created VS %p to %.*s\n", *dst, uri->len, uri->s); res = create_subscriptions(*dst, nesting_level); if (res != 0) { vs_free(*dst); return res; } /* TODO: remember the list of Accept headers from client subscribe * it will be used for Back-End subscriptions */ /* add names */ if (dnames) { d = SEQUENCE_FIRST(dnames); while (d) { vs_add_display_name((*dst), d->name, d->lang); d = SEQUENCE_NEXT(d); } } return RES_OK; } int vs_add_display_name(virtual_subscription_t *vs, const char *name, const char *lang) { vs_display_name_t dn; if (name) { dn.name.len = strlen(name); if (dn.name.len > 0) { dn.name.s = (char *)mem_alloc(dn.name.len); if (!dn.name.s) dn.name.len = 0; else memcpy(dn.name.s, name, dn.name.len); } } else { dn.name.len = 0; dn.name.s = NULL; } if (lang) { dn.lang.len = strlen(lang); if (dn.lang.len > 0) { dn.lang.s = (char *)mem_alloc(dn.lang.len); if (!dn.lang.s) dn.lang.len = 0; else memcpy(dn.lang.s, lang, dn.lang.len); } } else { dn.lang.len = 0; dn.lang.s = NULL; } /* TRACE_LOG("adding display name: %s\n", name);*/ return vector_add(&vs->display_names, &dn); } void vs_free(virtual_subscription_t *vs) { int i, cnt; vs_display_name_t dn; if (vs) { if (vs->local_subscription_pres) unsubscribe(vsd->domain, vs->local_subscription_pres); if (vs->local_subscription_list) rls_remove(vs->local_subscription_list); /* remove notification messages for given subscription */ destroy_notifications(vs->local_subscription_pres); remove_from_vs_list(vs); str_free_content(&vs->state_document); str_free_content(&vs->content_type); /* if ( (vs->package.len > 0) && (vs->package.s) ) mem_free(vs->package.s); */ cnt = vector_size(&vs->display_names); for (i = 0; i < cnt; i++) { if (vector_get(&vs->display_names, i, &dn) != 0) continue; if (dn.name.s && (dn.name.len > 0)) mem_free(dn.name.s); if (dn.lang.s && (dn.lang.len > 0)) mem_free(dn.lang.s); } vector_destroy(&vs->display_names); mem_free(vs); /* LOG(L_TRACE, "Virtual Subscription freed\n");*/ } } kamailio-4.0.4/obsolete/rls/result_codes.h0000644000000000000000000000053012223032460017253 0ustar rootroot#ifndef __RESULT_CODES_H #define __RESULT_CODES_H #include /* result codes (non negative numbers) */ #define RES_NOT_FOUND 1 /* errors */ #define RES_PARSE_HEADERS_ERR (-10) #define RES_EXPIRATION_INTERVAL_TOO_SHORT (-11) #define RES_SUBSCRIPTION_TERMINATED (-12) #define RES_SUBSCRIPTION_REJECTED (-13) #endif kamailio-4.0.4/obsolete/rls/rls_mod.c0000644000000000000000000002405212223032460016217 0ustar rootroot#include "rls_mod.h" #include "../../sr_module.h" #include "../../timer_ticks.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include #include #include #include #include #include #include "rl_subscription.h" #include "rls_handler.h" #include "rpc.h" #include "uri_ops.h" #include "time_event_manager.h" #include MODULE_VERSION int rls_mod_init(void); void rls_mod_destroy(void); int rls_child_init(int _rank); static int rls_subscribe_fixup(void** param, int param_no); /* authorization parameters */ char *auth_type_str = NULL; /* type of authorization: none,implicit,xcap */ int db_mode = 0; /* 0 -> no DB, 1 -> write through */ char *db_url = NULL; int reduce_xcap_needs = 0; /* internal data members */ /* static ptr_vector_t *xcap_servers = NULL; */ db_con_t* rls_db = NULL; /* database connection handle */ db_func_t rls_dbf; /* database functions */ /* one shot timer for reloading data from DB - they can not be reloaded in init or child_init due to internal subscriptions to other modules (may be not itialised yet) */ static int init_timer_delay = 3; /* parameters for optimizations */ int max_notifications_at_once = 1000000; /* timer for processing notifications from QSA */ int rls_timer_interval = 10; /* ignore if NOTIFY times out (don't destroy subscription */ int rls_ignore_408_on_notify = 0; /* maximum nested list level (if 0, no nested lists are possible, * if 1 only one nested list level is possible, if 2 it is possible to * have lists in lists, ..., unlimited if -1) */ int max_list_nesting_level = -1; /** Exported functions */ static cmd_export_t cmds[]={ /* {"handle_r_subscription", handle_r_subscription, 0, subscribe_fixup, REQUEST_ROUTE | FAILURE_ROUTE}, */ {"handle_rls_subscription", (cmd_function)handle_rls_subscription, 1, rls_subscribe_fixup, REQUEST_ROUTE | FAILURE_ROUTE}, {"is_simple_rls_target", is_simple_rls_target, 1, NULL, REQUEST_ROUTE | FAILURE_ROUTE}, {"query_rls_services", query_rls_services, 0, NULL, REQUEST_ROUTE | FAILURE_ROUTE}, {"query_resource_list", query_resource_list, 1, NULL, REQUEST_ROUTE | FAILURE_ROUTE}, {"have_flat_list", have_flat_list, 0, NULL, REQUEST_ROUTE | FAILURE_ROUTE}, {0, 0, 0, 0, 0} }; /** Exported parameters */ static param_export_t params[]={ {"min_expiration", PARAM_INT, &rls_min_expiration }, {"max_expiration", PARAM_INT, &rls_max_expiration }, {"default_expiration", PARAM_INT, &rls_default_expiration }, {"auth", PARAM_STRING, &auth_type_str }, /* type of authorization: none, implicit, xcap, ... */ {"db_mode", PARAM_INT, &db_mode }, {"db_url", PARAM_STRING, &db_url }, {"reduce_xcap_needs", PARAM_INT, &reduce_xcap_needs }, {"max_notifications_at_once", PARAM_INT, &max_notifications_at_once }, {"timer_interval", PARAM_INT, &rls_timer_interval }, {"max_list_nesting_level", PARAM_INT, &max_list_nesting_level }, {"expiration_timer_period", PARAM_INT, &rls_expiration_timer_period }, {"ignore_408_on_notify", PARAM_INT, &rls_ignore_408_on_notify }, {"init_timer_delay", PARAM_INT, &init_timer_delay }, /* timer for delayed DB reload (due to internal subscriptions can't be reloaded from init or child init) */ {0, 0, 0} }; struct module_exports exports = { "rls", cmds, /* Exported functions */ rls_rpc_methods, /* RPC methods */ params, /* Exported parameters */ rls_mod_init, /* module initialization function */ 0, /* response function*/ rls_mod_destroy, /* pa_destroy, / * destroy function */ 0, /* oncancel function */ rls_child_init /* per-child init function */ }; struct tm_binds tmb; dlg_func_t dlg_func; fill_xcap_params_func fill_xcap_params = NULL; int use_db = 0; int rls_min_expiration = 60; int rls_max_expiration = 7200; int rls_default_expiration = 3761; int rls_expiration_timer_period = 10; rls_auth_params_t rls_auth_params; /* structure filled according to parameters (common for all XCAP servers now) */ char *xcap_server = NULL; /* XCAP server URI */ /* TODO: settings of other xcap parameters (auth, ssl, ...) */ static int set_auth_params(rls_auth_params_t *dst, const char *auth_type_str) { if (!auth_type_str) { LOG(L_ERR, "no subscription authorization type given, using \'implicit\'!\n"); dst->type = rls_auth_none; return 0; } if (strcmp(auth_type_str, "xcap") == 0) { dst->type = rls_auth_xcap; return 0; } if (strcmp(auth_type_str, "none") == 0) { dst->type = rls_auth_none; LOG(L_WARN, "using \'none\' rls-subscription authorization!\n"); return 0; } if (strcmp(auth_type_str, "implicit") == 0) { dst->type = rls_auth_implicit; return 0; } LOG(L_ERR, "Can't resolve subscription authorization type: \'%s\'." " Use one of: none, implicit, xcap.\n", auth_type_str); return -1; } static ticks_t init_timer_cb(ticks_t ticks, struct timer_ln* tl, void* data) { /* initialization (like read data from database) which can trigger * database operations in other modules/... */ if (use_db && (rls_db)) { INFO("reading RLS data from database\n"); rls_lock(); db_load_rls(); rls_unlock(); } if (data) { mem_free(data); /* ERR("freeing myself!\n"); */ } return 0; /* one shot timer */ } int rls_mod_init(void) { load_tm_f load_tm; bind_dlg_mod_f bind_dlg; struct timer_ln *i_timer = NULL; DEBUG_LOG("RLS module initialization\n"); /* ??? if other module uses this libraries it might be a problem ??? */ xmlInitParser(); DEBUG_LOG(" ... common libraries\n"); qsa_initialize(); if (time_event_management_init() != 0) { LOG(L_ERR, "rls_mod_init(): Can't initialize time event management!\n"); return -1; } if (subscription_management_init() != 0) { LOG(L_ERR, "rls_mod_init(): Can't initialize time event management!\n"); return -1; } /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) { LOG(L_ERR, "rls_mod_init(): Can't import tm!\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm(&tmb)==-1) { LOG(L_ERR, "rls_mod_init(): load_tm() failed\n"); return -1; } bind_dlg = (bind_dlg_mod_f)find_export("bind_dlg_mod", -1, 0); if (!bind_dlg) { LOG(L_ERR, "Can't import dlg\n"); return -1; } if (bind_dlg(&dlg_func) != 0) { return -1; } if (rls_init() != 0) { return -1; } if (vs_init() != 0) { return -1; } /* xcap_servers = (ptr_vector_t*)mem_alloc(sizeof(ptr_vector_t)); if (!xcap_servers) { LOG(L_ERR, "rls_mod_init(): can't allocate memory for XCAP servers vector\n"); return -1; } ptr_vector_init(xcap_servers, 8); */ /* set authorization type according to requested "auth type name" * and other (type specific) parameters */ if (set_auth_params(&rls_auth_params, auth_type_str) != 0) return -1; use_db = 0; if (db_mode > 0) { int db_url_len = db_url ? strlen(db_url) : 0; if (!db_url_len) { LOG(L_ERR, "rls_mod_init(): no db_url specified but db_mode > 0\n"); db_mode = 0; } } if (db_mode > 0) { if (bind_dbmod(db_url, &rls_dbf) < 0) { LOG(L_ERR, "rls_mod_init(): Can't bind database module via url %s\n", db_url); return -1; } if (!DB_CAPABILITY(rls_dbf, DB_CAP_ALL)) { /* ? */ LOG(L_ERR, "rls_mod_init(): Database module does not implement all functions needed by the module\n"); return -1; } use_db = 1; } /* once-shot timer for reloading data from DB - * needed because it can trigger database operations * in other modules and they mostly intialize their * database connection in child_init functions */ i_timer = timer_alloc(); if (!i_timer) { ERR("can't allocate memory for DB init timer\n"); return -1; } else { timer_init(i_timer, init_timer_cb, i_timer, 0); timer_add(i_timer, S_TO_TICKS(init_timer_delay)); } fill_xcap_params = (fill_xcap_params_func)find_export("fill_xcap_params", 0, -1); return 0; } int rls_child_init(int _rank) { rls_db = NULL; if (use_db) { if (_rank==PROC_INIT || _rank==PROC_MAIN || _rank==PROC_TCP_MAIN) return 0; /* do nothing for the main or tcp_main processes */ if (rls_dbf.init) rls_db = rls_dbf.init(db_url); if (!rls_db) { LOG(L_ERR, "ERROR: rls_child_init(%d): " "Error while connecting database\n", _rank); return -1; } /* if (_rank == 0) { rls_lock(); db_load_rls(); rls_unlock(); } */ } return 0; } void rls_mod_destroy(void) { /*int i, cnt; char *s;*/ DEBUG_LOG("RLS module cleanup\n"); /* destroy used XCAP servers */ /* DEBUG_LOG(" ... xcap servers\n"); if (xcap_servers) { cnt = ptr_vector_size(xcap_servers); DEBUG_LOG(" count = %d\n", cnt); for (i = 0; i < cnt; i++) { s = ptr_vector_get(xcap_servers, i); if (s) { DEBUG_LOG(" ... freeing %s (%p)\n", s, s); cds_free(s); } } ptr_vector_destroy(xcap_servers); mem_free(xcap_servers); xcap_servers = NULL; } */ DEBUG_LOG(" ... rls\n"); rls_destroy(); DEBUG_LOG(" ... vs\n"); vs_destroy(); DEBUG_LOG(" ... time event management\n"); time_event_management_destroy(); DEBUG_LOG(" %s: ... db\n", __func__); if (use_db) { if (rls_db && rls_dbf.close) rls_dbf.close(rls_db); rls_db = NULL; } DEBUG_LOG(" ... common libs\n"); qsa_cleanup(); /* ??? if other module uses this libraries it might be a problem ??? */ /* xmlCleanupParser(); */ DEBUG_LOG("RLS module cleanup finished\n"); } static int rls_subscribe_fixup(void** param, int param_no) { /* char *xcap_server = NULL; */ long send_errors = 0; /* if (param_no == 1) { if (!param) { LOG(L_ERR, "rls_subscribe_fixup(): XCAP server address not set!\n"); return E_UNSPEC; } xcap_server = zt_strdup((char *)*param); if (!xcap_server) { LOG(L_ERR, "rls_subscribe_fixup(): Can't set XCAP server address!\n"); return E_UNSPEC; } / * store not only the root string? (create a structure rather?) * / ptr_vector_add(xcap_servers, xcap_server); DEBUG_LOG("rls_subscribe_fixup(): XCAP server is %s (%p)\n", xcap_server, xcap_server); *param = (void*)xcap_server; } */ if (param_no == 1) { if (param) { if (*param) send_errors = atoi(*param); } DEBUG_LOG("rls_subscribe_fixup(): send errors: %ld\n", send_errors); *param = (void*)send_errors; } return 0; } kamailio-4.0.4/obsolete/rls/rl_subscription.c0000644000000000000000000003745612223032460020015 0ustar rootroot#include "rl_subscription.h" #include "rls_mod.h" #include #include #include #include "result_codes.h" #include "rlmi_doc.h" #include #include #include "../../str.h" #include "../../id.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../lock_alloc.h" #include "../../ut.h" #include "../../parser/hf.h" #include "../../parser/parse_from.h" #include "../../data_lump_rpl.h" typedef struct { dlg_id_t id; char buf[1]; } rls_notify_cb_param_t; subscription_manager_t *rls_manager = NULL; #define METHOD_NOTIFY "NOTIFY" /************* Helper functions for TM callback ************/ static rls_notify_cb_param_t *create_notify_cb_param(rl_subscription_t *s) { rls_notify_cb_param_t *cbd; int size; dlg_t *dlg; if (!s) return NULL; if (s->type != rls_external_subscription) return NULL; dlg = s->u.external.dialog; if (!dlg) return NULL; size = sizeof(*cbd) + dlg->id.call_id.len + dlg->id.rem_tag.len + dlg->id.loc_tag.len; cbd = (rls_notify_cb_param_t*)mem_alloc(size); if (!cbd) { ERR("can't allocate memory (%d bytes)\n", size); return NULL; } cbd->id.call_id.s = cbd->buf; cbd->id.call_id.len = dlg->id.call_id.len; cbd->id.rem_tag.s = cbd->id.call_id.s + cbd->id.call_id.len; cbd->id.rem_tag.len = dlg->id.rem_tag.len; cbd->id.loc_tag.s = cbd->id.rem_tag.s + cbd->id.rem_tag.len; cbd->id.loc_tag.len = dlg->id.loc_tag.len; /* copy data */ if (dlg->id.call_id.s) memcpy(cbd->id.call_id.s, dlg->id.call_id.s, dlg->id.call_id.len); if (dlg->id.rem_tag.s) memcpy(cbd->id.rem_tag.s, dlg->id.rem_tag.s, dlg->id.rem_tag.len); if (dlg->id.loc_tag.s) memcpy(cbd->id.loc_tag.s, dlg->id.loc_tag.s, dlg->id.loc_tag.len); return cbd; } static void destroy_subscription(rls_notify_cb_param_t *cbd) { int res; rl_subscription_t *s = NULL; rls_lock(); res = rls_find_subscription(&cbd->id.rem_tag, &cbd->id.loc_tag, &cbd->id.call_id, &s); if ((res != RES_OK) || (!s)) { /* subscription NOT found */ rls_unlock(); return; } rls_remove(s); rls_unlock(); } /************* Helper functions for RL subscription manipulation ************/ str_t * rls_get_uri(rl_subscription_t *s) { if (!s) return NULL; if (s->type == rls_external_subscription) { return &((s)->u.external.record_id); } else { return s->u.internal.record_id; } return NULL; } str_t * rls_get_package(rl_subscription_t *s) { static str presence = STR_STATIC_INIT("presence"); str_t *package = NULL; if (!s) return NULL; if (s->type == rls_external_subscription) package = &((s)->u.external.package); else package = s->u.internal.package; if (!package) package = &presence; return package; } str_t * rls_get_subscriber(rl_subscription_t *subscription) { if (!subscription) return NULL; switch (subscription->type) { case rls_external_subscription: return &subscription->u.external.subscriber; case rls_internal_subscription: return subscription->u.internal.subscriber_id; } return NULL; } int add_virtual_subscriptions(rl_subscription_t *ss, flat_list_t *flat, int nesting_level) { flat_list_t *e; /* xcap_query_t xcap; */ virtual_subscription_t *vs; int res = 0; str s; /* TODO: create virtual subscriptions using Accept headers * ... (for remote subscriptions) */ /* go through flat list and find/create virtual subscriptions */ e = flat; while (e) { s.s = e->uri; if (s.s) s.len = strlen(s.s); else s.len = 0; res = vs_create(&s, &vs, e->names, ss, nesting_level); if (res != RES_OK) { /* FIXME: remove already added members? */ return res; } ptr_vector_add(&ss->vs, vs); e = e->next; } return RES_OK; } int create_virtual_subscriptions(rl_subscription_t *ss, int nesting_level) { flat_list_t *flat = NULL; int res = 0; str_t *ss_uri = NULL; str_t *ss_package = NULL; ss_uri = rls_get_uri(ss); ss_package = rls_get_package(ss); res = xcap_query_rls_services(&ss->xcap_params, ss_uri, ss_package, &flat); if (res != RES_OK) return res; /* go through flat list and find/create virtual subscriptions */ res = add_virtual_subscriptions(ss, flat, nesting_level); DEBUG_LOG("rli_create_content(): freeing flat list\n"); free_flat_list(flat); return RES_OK; } static void clear_change_flags(rl_subscription_t *s) { int i, cnt; virtual_subscription_t *vs; cnt = ptr_vector_size(&s->vs); for (i = 0; i < cnt; i++) { vs = ptr_vector_get(&s->vs, i); if (!vs) continue; vs->changed = 0; } s->changed = 0; } /************* RL subscription manipulation function ************/ rl_subscription_t *rls_alloc_subscription(rls_subscription_type_t type) { rl_subscription_t *s; s = (rl_subscription_t*)mem_alloc(sizeof(rl_subscription_t)); if (!s) { LOG(L_ERR, "rls_alloc_subscription(): can't allocate memory\n"); return NULL; } memset(s, 0, sizeof(*s)); s->u.external.status = subscription_uninitialized; s->u.external.usr_data = s; s->doc_version = 0; s->changed = 0; s->type = type; s->dbid[0] = 0; /* s->first_vs = NULL; s->last_vs = NULL; */ str_clear(&s->from_uid); ptr_vector_init(&s->vs, 4); return s; } int rls_create_subscription(struct sip_msg *m, rl_subscription_t **dst, flat_list_t *flat, xcap_query_params_t *params) { rl_subscription_t *s; str from_uid = STR_NULL; int res; if (!dst) return RES_INTERNAL_ERR; *dst = NULL; s = rls_alloc_subscription(rls_external_subscription); if (!s) { LOG(L_ERR, "rls_create_new(): can't allocate memory\n"); return RES_MEMORY_ERR; } generate_db_id(&s->dbid, s); res = sm_init_subscription_nolock(rls_manager, &s->u.external, m); if (res != RES_OK) { rls_free(s); return res; } if (params) { if (dup_xcap_params(&s->xcap_params, params) < 0) { ERR("can't duplicate xcap_params\n"); rls_free(s); return -1; } } /* store pointer to this RL subscription as user data * of (low level) subscription */ s->u.external.usr_data = s; if (get_from_uid(&from_uid, m) < 0) str_clear(&s->from_uid); else str_dup(&s->from_uid, &from_uid); /* res = set_rls_info(m, s, xcap_root); if (res != 0) { rls_free(s); return res; }*/ res = add_virtual_subscriptions(s, flat, max_list_nesting_level); if (res != 0) { rls_free(s); return res; } if (use_db) { if (rls_db_add(s) != 0) { rls_free(s); return RES_INTERNAL_ERR; /* FIXME RES_DB_ERR */ } } *dst = s; return RES_OK; } int rls_find_subscription(str *from_tag, str *to_tag, str *call_id, rl_subscription_t **dst) { subscription_data_t *s; int res; *dst = NULL; res = sm_find_subscription(rls_manager, from_tag, to_tag, call_id, &s); if ((res == RES_OK) && (s)) { if (!s->usr_data) { LOG(L_ERR, "found subscription without filled usr_data\n"); return RES_INTERNAL_ERR; } else { *dst = (rl_subscription_t*)s->usr_data; return RES_OK; } } return RES_NOT_FOUND; } int rls_refresh_subscription(struct sip_msg *m, rl_subscription_t *s) { int res; if (!s) return RES_INTERNAL_ERR; if (s->type != rls_external_subscription) return RES_INTERNAL_ERR; res = sm_refresh_subscription_nolock(rls_manager, &s->u.external, m); if (use_db) rls_db_update(s); return res; } void rls_remove(rl_subscription_t *s) { if (!s) return; if (use_db) rls_db_remove(s); rls_free(s); } void rls_free(rl_subscription_t *s) { int i, cnt; virtual_subscription_t *vs; if (!s) return; if (use_db) rls_db_remove(s); cnt = ptr_vector_size(&s->vs); for (i = 0; i < cnt; i++) { vs = ptr_vector_get(&s->vs, i); if (!vs) continue; vs_free(vs); } if (s->type == rls_external_subscription) { sm_release_subscription_nolock(rls_manager, &s->u.external); /* free ONLY for external subscriptions */ free_xcap_params_content(&s->xcap_params); } else { /* release_internal_subscription(s); */ /* don't free xcap_params */ } ptr_vector_destroy(&s->vs); str_free_content(&s->from_uid); mem_free(s); } /* void rls_notify_cb(struct cell* t, struct sip_msg* msg, int code, void *param) */ static void rls_notify_cb(struct cell* t, int type, struct tmcb_params* params) { rls_notify_cb_param_t *cbd = NULL; if (!params) return; if (params->param) cbd = (rls_notify_cb_param_t *)*(params->param); if (!cbd) { ERR("BUG empty cbd parameter given to callback function\n"); return; } if (params->code >= 300) { /* what else can we do with 3xx ? */ int ignore = 0; switch (params->code) { case 408: if (rls_ignore_408_on_notify) ignore = 1; /* due to eyeBeam's problems with processing more NOTIFY * requests sent consequently without big delay */ break; } if (!ignore) { WARN("destroying subscription from callback due to %d response on NOTIFY\n", params->code); destroy_subscription(cbd); TRACE("subscription destroyed!!!\n"); } } mem_free(cbd); } static int rls_generate_notify_ext(rl_subscription_t *s, int full_info) { /* !!! the main mutex must be locked here !!! */ int res; str doc; dstring_t dstr; str headers, content_type; static str method = STR_STATIC_INIT(METHOD_NOTIFY); dlg_t *dlg; int exp_time = 0; char expiration[32]; str body = STR_STATIC_INIT(""); int removed = 0; dlg = s->u.external.dialog; if (!dlg) return -1; DEBUG("generating external notify\n"); str_clear(&doc); str_clear(&content_type); if (sm_subscription_pending(&s->u.external) != 0) { /* create the document only for non-pending subscriptions */ if (create_rlmi_document(&doc, &content_type, s, full_info) != 0) { return -1; } } exp_time = sm_subscription_expires_in(rls_manager, &s->u.external); sprintf(expiration, ";expires=%d\r\n", exp_time); dstr_init(&dstr, 256); dstr_append_zt(&dstr, "Subscription-State: "); switch (s->u.external.status) { case subscription_active: dstr_append_zt(&dstr, "active"); dstr_append_zt(&dstr, expiration); break; case subscription_pending: dstr_append_zt(&dstr, "pending"); dstr_append_zt(&dstr, expiration); break; case subscription_terminated_pending: case subscription_terminated: dstr_append_zt(&dstr, "terminated\r\n"); break; case subscription_terminated_pending_to: case subscription_terminated_to: dstr_append_zt(&dstr, "terminated;reason=timeout\r\n"); break; case subscription_uninitialized: dstr_append_zt(&dstr, "pending\r\n"); /* this is an error ! */ LOG(L_ERR, "sending NOTIFY for an unitialized subscription!\n"); break; } dstr_append_str(&dstr, &s->u.external.contact); /* required by RFC 3261 */ dstr_append_zt(&dstr, "Max-Forwards: 70\r\n"); dstr_append_zt(&dstr, "Event: "); dstr_append_str(&dstr, rls_get_package(s)); dstr_append_zt(&dstr, "\r\n"); dstr_append_zt(&dstr, "Require: eventlist\r\nContent-Type: "); dstr_append_str(&dstr, &content_type); dstr_append_zt(&dstr, "\r\n"); res = dstr_get_str(&dstr, &headers); dstr_destroy(&dstr); if (res >= 0) { /* DEBUG("sending NOTIFY message to %.*s (subscription %p)\n", dlg->rem_uri.len, ZSW(dlg->rem_uri.s), s); */ if (!is_str_empty(&doc)) body = doc; if (sm_subscription_terminated(&s->u.external) == 0) { /* doesn't matter if delivered or not, it will be freed otherwise !!! */ res = tmb.t_request_within(&method, &headers, &body, dlg, 0, 0); if (res >= 0) clear_change_flags(s); } else { rls_notify_cb_param_t *cbd = create_notify_cb_param(s); if (!cbd) { ERR("Can't create notify cb data! Freeing RL subscription.\n"); rls_remove(s); /* ?????? */ removed = 1; res = -13; } else { /* the subscritpion will be destroyed if NOTIFY delivery problems */ /* rls_unlock(); the callback locks this mutex ! */ /* !!!! FIXME: callbacks can't be safely used (may be called or not, * may free memory automaticaly or not) !!! */ res = tmb.t_request_within(&method, &headers, &body, dlg, rls_notify_cb, cbd); /* res = tmb.t_request_within(&method, &headers, &body, dlg, rls_notify_cb, s); */ /* rls_lock(); the callback locks this mutex ! */ if (res < 0) { /* does this mean, that the callback was not called ??? */ ERR("t_request_within FAILED: %d! Freeing RL subscription.\n", res); rls_remove(s); /* ?????? */ removed = 1; } else clear_change_flags(s); } } } if (doc.s) cds_free(doc.s); if (content_type.s) cds_free(content_type.s); if (headers.s) cds_free(headers.s); if ((!removed) && use_db) rls_db_update(s); if (res < 0) DEBUG("external notify NOT generated\n"); else DEBUG("external notify generated\n"); return res; } /* static raw_presence_info_t* rls2raw_presence_info(rl_subscription_t *s) { raw_presence_info_t *info; info = create_raw_presence_info(s->u.internal.record_id); if (!info) return info; str_clear(&info->pres_doc); str_clear(&info->content_type); DEBUG_LOG(" ... create RLMI document\n"); create_rlmi_document(&info->pres_doc, &info->content_type, s, 1); return info; } */ static int rls_generate_notify_int(rl_subscription_t *s, int full_info) { /* generate internal notification */ str_t doc, content_type; /* TRACE("generating internal list notify\n"); */ if (!s->u.internal.vs) return 1; DBG("generating internal rls notification\n"); /* raw = rls2raw_presence_info(s); */ if (create_rlmi_document(&doc, &content_type, s, full_info) < 0) { ERR("can't generate internal notification document\n"); return -1; } clear_change_flags(s); /* documents are given to VS (we don't care about them * more - no free, ... */ process_internal_notify(s->u.internal.vs, &doc, &content_type); return 0; } int rls_generate_notify(rl_subscription_t *s, int full_info) { /* !!! the main mutex must be locked here !!! */ DBG("generating rls notification\n"); if (!s) { ERR("called with subscription\n"); return -1; } switch (s->type) { case rls_external_subscription: return rls_generate_notify_ext(s, full_info); case rls_internal_subscription: return rls_generate_notify_int(s, full_info); } return -1; } int rls_prepare_subscription_response(rl_subscription_t *s, struct sip_msg *m) { /* char *hdr = "Supported: eventlist\r\n"; */ char *hdr = "Require: eventlist\r\n"; if (s->type != rls_external_subscription) return -1; if (!add_lump_rpl(m, hdr, strlen(hdr), LUMP_RPL_HDR)) return -1; return sm_prepare_subscription_response(rls_manager, &s->u.external, m); } /** returns the count of seconds remaining to subscription expiration */ int rls_subscription_expires_in(rl_subscription_t *s) { if (s->type == rls_external_subscription) return sm_subscription_expires_in(rls_manager, &s->u.external); else return -1; } /* static str_t notifier_name = { s: "rls", len: 3 }; */ /* static str_t pres_list_package = { s: "presence.list", len: 13 }; int is_presence_list_package(const str_t *package) { return (str_case_equals(package, &pres_list_package) == 0); }*/ int rls_create_internal_subscription(virtual_subscription_t *vs, rl_subscription_t **dst, flat_list_t *flat, int nesting_level) { rl_subscription_t *rls; /* try to make subscription and release it if internal subscription * not created */ if (dst) *dst = NULL; rls = rls_alloc_subscription(rls_internal_subscription); if (!rls) { ERR("processing INTERNAL RLS subscription - memory allocation error\n"); return -1; } rls->u.internal.record_id = &vs->uri; /* !!! NEVER !!! free this */ rls->u.internal.package = rls_get_package(vs->subscription); /* !!! NEVER !!! free this */ rls->u.internal.subscriber_id = rls_get_subscriber(vs->subscription); /* !!! NEVER !!! free this */ rls->xcap_params = vs->subscription->xcap_params; /* !!! NEVER free this !!! */ rls->u.internal.vs = vs; if (dst) *dst = rls; DBG("creating internal subscription to %.*s (VS %p)\n", FMT_STR(*rls->u.internal.record_id), rls->u.internal.vs); if (add_virtual_subscriptions(rls, flat, nesting_level) != 0) { rls_free(rls); if (dst) *dst = NULL; return -1; } rls_generate_notify(rls, 1); return 0; } kamailio-4.0.4/obsolete/rls/time_event_manager.h0000644000000000000000000000375112223032460020421 0ustar rootroot#ifndef __TIME_EVENT_MANAGER_H #define __TIME_EVENT_MANAGER_H #include "../../lock_ops.h" #include "../../timer.h" struct _time_event_data_t; typedef void(*time_event_func)(struct _time_event_data_t *s); typedef struct _time_event_data_t { unsigned int tick_time; /** callback function */ time_event_func cb; /** callback function argument */ void *cb_param; /** callback function argument */ void *cb_param1; /** next element in time slot */ struct _time_event_data_t *next; /** previous element in time slot */ struct _time_event_data_t *prev; } time_event_data_t; typedef struct _time_event_slot_t { time_event_data_t *first, *last; } time_event_slot_t; typedef struct _time_event_manager_t { time_event_slot_t *time_slots; unsigned int slot_cnt; /** atomic time in seconds */ unsigned int atomic_time; /** allow the event to be "called" after its time */ int enable_delay; /** counts ticks - this is an absolute value "timer" */ unsigned int tick_counter; /** mutex is taken from parent (locking must be common - deadlock prevention) */ gen_lock_t *mutex; /** count of seconds after which should be called this timer's step */ unsigned int process_timer_counter; struct _time_event_manager_t *next; struct _time_event_manager_t *prev; } time_event_manager_t; time_event_manager_t *tem_create(unsigned int atomic_time, unsigned int slot_cnt, int enable_delay, gen_lock_t *mutex); int tem_init(time_event_manager_t *tm, unsigned int atomic_time, unsigned int slot_cnt, int enable_delay, gen_lock_t *mutex); void tem_destroy(time_event_manager_t *tem); void tem_add_event(time_event_manager_t *tem, unsigned int action_time, time_event_data_t *te); void tem_remove_event(time_event_manager_t *tem, time_event_data_t *te); void tem_add_event_nolock(time_event_manager_t *tem, unsigned int action_time, time_event_data_t *te); void tem_remove_event_nolock(time_event_manager_t *tem, time_event_data_t *te); int time_event_management_init(); void time_event_management_destroy(); #endif kamailio-4.0.4/obsolete/rls/time_event_manager.c0000644000000000000000000001306112223032460020407 0ustar rootroot#include "time_event_manager.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include #include #include "trace.h" typedef struct { time_event_manager_t *first; time_event_manager_t *last; gen_lock_t structure_mutex; } tem_info_t; static tem_info_t *tem_info = NULL; static void tem_do_step(time_event_manager_t *tem); static void tem_timer_cb(unsigned int ticks, void *param) { time_event_manager_t *e, *n; PROF_START(tem_timer_cb) if (!tem_info) return; e = tem_info->first; while (e) { n = e->next; if (--e->process_timer_counter == 0) { tem_do_step(e); e->process_timer_counter = e->atomic_time; } e = n; } PROF_STOP(tem_timer_cb) } int time_event_management_init() { if (tem_info) return 0; /* already initialized */ tem_info = (tem_info_t *)mem_alloc(sizeof(tem_info_t)); if (!tem_info) { LOG(L_ERR, "time_event_management_init(): can't allocate shared memory\n"); return -1; } tem_info->first = NULL; tem_info->last = NULL; lock_init(&tem_info->structure_mutex); /* register a SER timer */ if (register_timer(tem_timer_cb, NULL, 1) < 0) { LOG(L_ERR, "time_event_management_init(): can't register timer\n"); return -1; } return 0; } void time_event_management_destroy() { time_event_manager_t *e, *n; tem_info_t *ti = tem_info; tem_info = NULL; /* F I X M E: unregister SER timer ? */ if (!ti) return; e = ti->first; while (e) { n = e->next; tem_destroy(n); e = n; } mem_free(ti); } int tem_init(time_event_manager_t *tm, unsigned int atomic_time, unsigned int slot_cnt, int enable_delay, gen_lock_t *mutex) { if (!tm) return -1; tm->tick_counter = 0; tm->atomic_time = atomic_time; tm->slot_cnt = slot_cnt; tm->enable_delay = enable_delay; tm->mutex = mutex; tm->time_slots = (time_event_slot_t *)mem_alloc(slot_cnt * sizeof(time_event_slot_t)); if (!tm->time_slots) { LOG(L_ERR, "can't initialize time event manager slots\n"); return -1; } memset(tm->time_slots, 0, slot_cnt * sizeof(time_event_slot_t)); tm->next = NULL; tm->process_timer_counter = atomic_time; lock_get(&tem_info->structure_mutex); tm->prev = tem_info->last; if (tem_info->last) tem_info->last->next = tm; else tem_info->first = tm; tem_info->last = tm; lock_release(&tem_info->structure_mutex); return 0; } time_event_manager_t *tem_create(unsigned int atomic_time, unsigned int slot_cnt, int enable_delay, gen_lock_t *mutex) { time_event_manager_t *tm; tm = (time_event_manager_t*)mem_alloc(sizeof(time_event_manager_t)); if (!tm) { LOG(L_ERR, "can't allocate time event manager\n"); return tm; } if (tem_init(tm, atomic_time, slot_cnt, enable_delay, mutex) != 0) { mem_free(tm); return NULL; } return tm; } void tem_destroy(time_event_manager_t *tem) { if (tem) { lock_get(&tem_info->structure_mutex); if (tem->prev) tem->prev->next = tem->next; else tem_info->first = tem->next; if (tem->next) tem->next->prev = tem->prev; else tem_info->last = tem->prev; lock_release(&tem_info->structure_mutex); if (tem->time_slots) mem_free(tem->time_slots); mem_free(tem); } } void tem_add_event(time_event_manager_t *tem, unsigned int action_time, time_event_data_t *te) { if (tem->mutex) lock_get(tem->mutex); tem_add_event_nolock(tem, action_time, te); if (tem->mutex) lock_release(tem->mutex); } void tem_remove_event(time_event_manager_t *tem, time_event_data_t *te) { if (tem->mutex) lock_get(tem->mutex); tem_remove_event_nolock(tem, te); if (tem->mutex) lock_release(tem->mutex); } void tem_add_event_nolock(time_event_manager_t *tem, unsigned int action_time, time_event_data_t *te) { unsigned int tick, s; PROF_START(tem_add_event) if (!te) return; tick = action_time / tem->atomic_time; if ((tem->enable_delay) && (action_time % tem->atomic_time > 0)) { /* rather call the action later than before */ tick++; } if (tick <= 0) tick = 1; /* never add to current slot (? only if not processing ?)*/ tick += tem->tick_counter; s = tick % tem->slot_cnt; te->next = NULL; te->prev = tem->time_slots[s].last; if (tem->time_slots[s].last) tem->time_slots[s].last->next = te; else tem->time_slots[s].first = te; tem->time_slots[s].last = te; te->tick_time = tick; PROF_STOP(tem_add_event) } void tem_remove_event_nolock(time_event_manager_t *tem, time_event_data_t *te) { time_event_slot_t *slot; PROF_START(tem_remove_event) if (!te) return; slot = &tem->time_slots[te->tick_time % tem->slot_cnt]; if (te->prev) te->prev->next = te->next; else slot->first = te->next; if (te->next) te->next->prev = te->prev; else slot->last = te->prev; te->next = NULL; te->prev = NULL; PROF_STOP(tem_remove_event) } static void tem_do_step(time_event_manager_t *tem) { time_event_data_t *e, *n, *unprocessed_first, *unprocessed_last; time_event_slot_t *slot; PROF_START(tem_do_step) if (tem->mutex) lock_get(tem->mutex); unprocessed_first = NULL; unprocessed_last = NULL; slot = &tem->time_slots[tem->tick_counter % tem->slot_cnt]; e = slot->first; while (e) { n = e->next; if (e->tick_time == tem->tick_counter) { if (e->cb) e->cb(e); /* the pointer to this element is forgotten - it MUST be * freed in the callback function */ } else { /* it is not the right time => give it into unprocessed events */ e->prev = unprocessed_last; e->next = NULL; if (unprocessed_last) unprocessed_last->next = e; else unprocessed_first = e; unprocessed_last = e; } e = n; } slot->first = unprocessed_first; slot->last = unprocessed_last; tem->tick_counter++; if (tem->mutex) lock_release(tem->mutex); PROF_STOP(tem_do_step) } kamailio-4.0.4/obsolete/rls/doc/0000755000000000000000000000000012223032460015156 5ustar rootrootkamailio-4.0.4/obsolete/rls/doc/rls_base.xml0000644000000000000000000000267112223032460017500 0ustar rootrootResource lists server is a server which allows subscriptions to lists of users. Its behaviour is defined in and . As described there, it uses XCAP server for storing data about lists of users. These data can be manipulated in any way by user's client software.
Dependencies Modules tm pa dialog optionaly database module (mysql, ...) Libraries libcds (internal) libxcap (internal) - XCAP queries libpresence (internal) - used for internal subscriptions ta PA/presence_b2b
kamailio-4.0.4/obsolete/rls/doc/rls.xml0000644000000000000000000000127012223032460016500 0ustar rootroot ]>
RLS (Resource List Server) Vaclav Kubart Iptel/Tekelec vaclav.kubart@iptel.org &base;
kamailio-4.0.4/obsolete/rls/doc/functions.xml0000644000000000000000000001112512223032460017710 0ustar rootroot
Functions handle_rls_subscription Handle subscription to resource list uri. Parameters: create_error_response (integer) If set to 0 error responses are not sent to the client. This may be useful, if used together with PA module. This function handles resource list subscription. XCAP document containing the list must be loaded before using one of query function (, ). handling subscription ... if (method=="SUBSCRIBE") { if (!t_newtran()) { sl_reply_error(); }; if (query_rls_services()) { handle_rls_subscription("1"); } else { # no such list exists -> process it with PA module handle_subscription("registrar"); } break; }; ... In this case for every incomming SUBSCRIBE request SER asks for "global resource list". If such list exists, the subscription is processed like resource list subscription. is_simple_rls_target Test uri according to given template. Parameters: template (string) Template to be compared with To URI. Function tries to compare username in To URI with given template. If there are different domains in To and From URIs, the function fails. There can be used "$uid" in template and it is replaced by value returned by get_from_uid. In the future should be this function replaced by AVP operations but I was not able to do it with them now... handling subscription ... if (method=="SUBSCRIBE") { if (!t_newtran()) { sl_reply_error(); break; }; if (lookup_domain("From")) { if (lookup_user("From")) { if (is_simple_rls_target("$uid-list")) { # takes From UID and makes XCAP query # for user's list named "default" if (!query_resource_list("default")) { t_reply("500", "XCAP query error"); break; } handle_rls_subscription("1"); break; } } } } ... In this case if SUBSCRIBE request arrives and From is smith@test-domain.com and To is smith-list@test-domain.com, the function returns true and the subscription is handled like subscription to "user's resource list" (not "global") as written above. query_rls_services Get list from global rls-services document. Function loads "global resource list" from XCAP server. This list is then processed by RLS module using . query_resource_list Get user's resource list. Parameters: list_name (string) Name of the requested list. If empty, it loads whole user's resource-lists document as one list. Function loads "user's resource list" from XCAP server. This list is then processed by RLS module using . have_flat_list Test if a resource list is loaded. Function tests if a list was loaded using one of query functions ( or ) and returns 1 if yes and -1 otherwise.
kamailio-4.0.4/obsolete/rls/doc/xcap.xml0000644000000000000000000002255712223032460016646 0ustar rootroot
RLS and XCAP
Used terms RLS Resource List Server - server processing subscriptions to special URIs (RL-URIs) which represent more resources than only one like "normal URIs". RL-URI Resource list URI. URI which represents a list of resources instead of a single user.
Processing subscriptions Specification says that each time a SUBSCRIBE request to a RL-URI comes, RLS has to ask XCAP server for coresponding <service> in default rls-services document. The XCAP query in this case is for document like <xcap-root>/rls-services/global/index/~~/rls-services/service[@uri=%22<AOR>%22]. Returned <service> is processed according to specification then and the result of it is flat list of URIs to subscribe to. This processing has its weaknesses. In case of such processing is one <service> element in rls-services needed for every user who wants to have his buddies stored on XCAP server. Such <service> will (for user's records) mostly point into his resource-lists document and thus another XCAP query for result list is needed. Another problem is, that each user has to modify global rls-services document. This is security problem and there is no client software doing this itself. [Exists something?] These reasons lead to separation of resource lists into "global lists" and "users lists". Global lists are not stored/modified by regular users; they are managed by administrators and they are handled according the draft. Example of such lists may be "technical support", "human resources", ... Resource lists for users are stored only in their resource-lists documents and the <service> element in global rls-services document is ommited (it is implicit). In this case RLS reads directly user's resource-lists document instead of trying to find it in global rls-services document.
Global resource lists As said above, resources described in rls-services documents are global resources accessible by all users. No regular user should have acces rights to modify global rls-services document on XCAP server.
rls-services document URI The construction of rls-services document URI is described in . Only in short: the AOR from SIP SUBSCRIBE request is combined with XCAP root given in configuration like this: <xcap-root>/rls-services/global/index/~~/rls-services/service[@uri=%22<AOR>%22]. This URI doesn't not specify namespaces as mentioned in definition, but this is due to problems with XCAP server used for tests (problems querying parts of documents with namespaces). rls-services uri example Let us assume xcap-root = http://localhost/xcap-root AOR of SUBSCRIBE request = sip:technical-support@someorg.org Resulting document describing the list will be get from URI: http://localhost/xcap-root/rls-services/global/index/~~/rls-services/service[@uri=%22<technical-support@someorg.org>%22], which means the service element with uri parameter value technical-support@someorg.org stored in rls-services document named index.
rls-services document There is example rls-services document describing resources technical-support@test-domain.com, hr@test-domain.com and work@test-domain.com which puts previous two into itself. This document should be stored on xcap server in file <xcap-root>/rls-services/global/index. Smith Joe Agatha presence Johny V Joe presence Technical support Human resources presence ]]>
Disadvantages Working with URIs presented in this section have one big disadvantage - it needs full XCAP server which is able to work with partial documents and able to process XPointer expressions in XCAP queries. Due to unavailability of free XCAP servers is there a possibility to use SER's RLS server in mode of reduced XCAP server needs (see RLS module parameters). If operating in this mode, RLS requests full rls-service document from uri <xcap-root>/rls-services/global/index, inspects it and finds requested resource list according to URI and AOR by itself. (Only if possible! There can't be links to partial documents in rls-services document.)
User's resource lists Opposite to global resource lists are resource lists of standalone users. As was told above, these lists are stored as resource-lists documents under user's directories. These resource-lists documents are accessed directly without searching for them in rls-services document - they behave like if they have implicit link there. User's resource lists Let us assume xcap root = http://localhost/xcap-root user smith (sends SUBSCRIBE with smith@... in From URI) Smith's UUID = smith The document below for user Smith can be stored on XCAP server in http://localhost/xcap-root/resource-lists/users/smith/resource-lists.xml. It contains two resource lists: default and contacts. Buddy List Joe Jan Contact List Alois ]]>
Standard incompliances SER's resource lists support is not finished yet, there are some standard incompliances now: uri canonicalization not done yet according to definition full status documents only
kamailio-4.0.4/obsolete/rls/doc/rls_incl.xml0000644000000000000000000000044712223032460017512 0ustar rootroot ]>
RLS (Resource List Server) &base;
kamailio-4.0.4/obsolete/rls/doc/Makefile0000644000000000000000000000012412223032460016613 0ustar rootrootdocs = rls.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/rls/doc/params.xml0000644000000000000000000001402712223032460017167 0ustar rootroot
Parameters min_expiration (integer) Minimal subscription expiration timeout in seconds. If client supplies value, which is less than value of min_expiration, the server returns response "423 Interval too small" as described in . Default value is 60. max_expiration (integer) Maximal subscription expiration timeout in seconds. If client supplies value, which is more than value of max_expiration, the server shortens this value to value of this variable as described in . Default value is 7200. default_expiration (integer) Default subscription expiration timeout in seconds. If client doesn't supply subscription expiration timeout this value is used. Default value is 3761. auth (string) This variable specifies authorization type for list watchers. Value can be one of: none All watchers are always authorized. This is not recommended because it ignores user's wish. implicit In this case is implicit authorization done. This means, that for list URIs in the form <username>-list@domain is subscription allowed only for user with username <username>, rejected for others. If the URI is not in the form presented above, the subscription is marked as pending. Default value is empty. In this case implict authorization is used with an error message. reduce_xcap_needs (int) If set to 1 the module tries to do simplify XCAP queries - queries will be done for whole documents, not partial, thus the XCAP server may be simulated using standard web server. It has influence only on querying resource lists and it will work correctly only if there are no links to resource lists containg partial documents URIs (lists should be contained directly in main rls-services document). Default value is 0. db_mode (integer) If set to 1, RLS module stores all subscription data into database and reloads them on startup. Requires db_url to be set. Default value is 0. db_url (integer) Database connection URL. It has to be specified if db_mode is set. Default value is empty. <varname>db_url</varname> settings ... modparam("rls", "db_mode", 1) modparam("rls", "db_url", "mysql://ser:heslo@127.0.0.1:3306/ser") ... max_notifications_at_once Max. number of notifications sent within one timer tick (experimental). It might be used to reduce SER's machine load if there are lots of changes in presence status. Default value is 1000000. (Too high number which in praxis means unlimited.) timer_interval Interval in seconds when are processed internal notifications and sent NOTIFYs to subscribers. max_nesting_level Maximum number of nested lists. For example if set to 2, it it possible to use "list nested in list nested in root list". Default value is -1 what means unlimited. It is possible to use this for speedup - if you know, that you will need only flat lists (no nested lists), you can set this to 0. In this case RLS doesn't try to query XCAP server for possibly netsted list URIs and directly creates subscription to URIs in list like if they are URIs of standalone users. expiration_timer_period Interval in seconds of timer which removes expired subscriptions. ignore_408_on_notify If set to 1 and 408 response to NOTIFY arrives, the dialog is NOT destroyed like in the case of other non-2xx responses. Use for testing only. Default value is 0. init_timer_delay Delay in seconds of timer which triggers loading data from database after startup. This is needed due to dependencies on other modules (pa/presence_b2b) - we need that these modules will be successfully initialised before reading data from DB and querying these modules. Default value is 3.
kamailio-4.0.4/obsolete/rls/uri_ops.c0000644000000000000000000000303412223032460016235 0ustar rootroot#include "uri_ops.h" #include "../../id.h" #include "../../parser/parse_from.h" #include #include int is_simple_rls_target(struct sip_msg *m, char *_template, char *unused) { str from_uid; struct sip_uri furi, turi; str from_uri, to_uri; str tmp; static str sample = STR_STATIC_INIT("$uid"); static str templ; int res = 1; PROF_START(rls_is_simple_rls_target) if (get_from_uid(&from_uid, m) < 0) { ERR("can't get From UID\n"); PROF_STOP(rls_is_simple_rls_target) return -1; } if (_template) { templ.s = _template; templ.len = strlen(_template); } else { templ.s = NULL; templ.len = 0; } from_uri = get_from(m)->uri; to_uri = get_to(m)->uri; if (parse_uri(from_uri.s, from_uri.len, &furi) < 0) { LOG(L_ERR, "Error while parsing From URI\n"); PROF_STOP(rls_is_simple_rls_target) return -1; } if (parse_uri(to_uri.s, to_uri.len, &turi) < 0) { LOG(L_ERR, "Error while parsing To URI\n"); PROF_STOP(rls_is_simple_rls_target) return -1; } /* compare domains */ if (str_nocase_equals(&turi.host, &furi.host) != 0) { /* not equal */ DBG("different domains\n"); PROF_STOP(rls_is_simple_rls_target) return -1; } /* compare usernames */ if (replace_str(&templ, &tmp, &sample, &from_uid) < 0) { ERR("can't allocate memory\n"); PROF_STOP(rls_is_simple_rls_target) return -1; } if (str_nocase_equals(&turi.user, &tmp) != 0) { /* not equal */ DBG("template doesn't match\n"); res = -1; } str_free_content(&tmp); PROF_STOP(rls_is_simple_rls_target) return res; } kamailio-4.0.4/obsolete/rls/subscription_manager.h0000644000000000000000000001057712223032460021012 0ustar rootroot#ifndef __SUBSCRIPTION_MANAGER_H #define __SUBSCRIPTION_MANAGER_H #include #include "time_event_manager.h" #include "../../modules/tm/dlg.h" #include "trace.h" struct _subscription_data_t; typedef enum { auth_rejected, auth_polite_block, auth_unresolved, auth_granted } authorization_result_t; typedef int(*send_notify_func)(struct _subscription_data_t *s); typedef int(*terminate_func)(struct _subscription_data_t *s); typedef authorization_result_t (*subscription_authorize_func)(struct _subscription_data_t *s); typedef enum { subscription_uninitialized, subscription_active, subscription_pending, subscription_terminated, subscription_terminated_to, /* terminated timeout */ subscription_terminated_pending, /* terminated pending subscription */ subscription_terminated_pending_to /* terminated pending subscription (timeout) */ } subscription_status_t; typedef struct _subscription_data_t { /** data for timer events */ time_event_data_t expiration; /** SIP dialog structure */ dlg_t *dialog; /** whatever user data */ void *usr_data; /** the status of this subscription */ subscription_status_t status; /** linking element */ struct _subscription_data_t *next; /** linking element */ struct _subscription_data_t *prev; /** contact for re-subscribe and responses */ str_t contact; /** subscription destination identifier */ str_t record_id; /** event package */ str_t package; /** subscriber's uri (due to authorization) */ str_t subscriber; } subscription_data_t; typedef struct { subscription_data_t *first; subscription_data_t *last; /** callback function for notify message sending */ send_notify_func notify; /** callback function for subscription terminating (timeout) */ terminate_func terminate; /** callback function for authorization */ subscription_authorize_func authorize; /** mutex given from caller (common for timer and subscription structures) */ gen_lock_t *mutex; /** its own time event manager */ time_event_manager_t timer; int default_expiration; int min_expiration; int max_expiration; } subscription_manager_t; /** initialization for all subscription managers - MUST be called */ int subscription_management_init(void); /** create a new subscription manager */ subscription_manager_t *sm_create(send_notify_func notify, terminate_func terminate, subscription_authorize_func authorize, gen_lock_t *mutex, int min_exp, int max_exp, int default_exp, int expiration_timer_period); /** initialize a new subscription manager */ int sm_init(subscription_manager_t *sm, send_notify_func notify, terminate_func terminate, subscription_authorize_func authorize, gen_lock_t *mutex, int min_exp, int max_exp, int default_exp, int expiration_timer_period); /** initializes internal data members SIP dialog and status * and intializes expiration timer */ int sm_init_subscription_nolock(subscription_manager_t *mng, subscription_data_t *dst, struct sip_msg *m); /** refreshes expiration timer and SIP dialog for given subscription * from given message */ int sm_refresh_subscription_nolock(subscription_manager_t *mng, subscription_data_t *s, struct sip_msg *m); /** releases timer and removes subscription from subscription * manager, but it doesn't free the occupied memory !*/ void sm_release_subscription_nolock(subscription_manager_t *mng, subscription_data_t *dst); /** adds some response lumps into message */ int sm_prepare_subscription_response(subscription_manager_t *mng, subscription_data_t *s, struct sip_msg *m); /** finds subscription according to dialog id */ int sm_find_subscription(subscription_manager_t *mng, str_t *from_tag, str_t *to_tag, str_t *call_id, subscription_data_t **dst); /** returns 0 if the subscriptions is in one of terminated states */ int sm_subscription_terminated(subscription_data_t *s); /** returns 0 if the subscriptions is in one of pending states */ int sm_subscription_pending(subscription_data_t *s); /** returns the count of seconds remaining to subscription expiration */ int sm_subscription_expires_in(subscription_manager_t *mng, subscription_data_t *s); int sm_init_subscription_nolock_ex(subscription_manager_t *mng, subscription_data_t *dst, dlg_t *dialog, subscription_status_t status, const str_t *contact, const str_t *record_id, const str_t *package, const str_t *subscriber, int expires_after, void *subscription_data); #endif kamailio-4.0.4/obsolete/rls/subscription_manager.c0000644000000000000000000004102012223032460020770 0ustar rootroot#include "subscription_manager.h" #include "../../parser/parse_expires.h" #include "../../modules/tm/tm_load.h" #include "../../parser/hf.h" #include "../../parser/parse_from.h" #include "../../data_lump_rpl.h" #include #include #include "result_codes.h" #include static struct tm_binds tmb; /****** Functions for global initialization ******/ int subscription_management_init(void) { load_tm_f load_tm; /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) { LOG(L_ERR, "subscription_management_init(): Can't import tm!\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm(&tmb)==-1) { LOG(L_ERR, "subscription_management_init(): load_tm() failed\n"); return -1; } return 0; } /****** Functions for standalone subscription manager manipulation ******/ int sm_init(subscription_manager_t *sm, send_notify_func notify, terminate_func terminate, subscription_authorize_func authorize, gen_lock_t *mutex, int min_exp, int max_exp, int default_exp, int expiration_timer_period) { if (!sm) return -1; sm->first = NULL; sm->last = NULL; sm->notify = notify; sm->terminate = terminate; sm->authorize = authorize; sm->mutex = mutex; sm->default_expiration = default_exp; sm->min_expiration = min_exp; sm->max_expiration = max_exp; return tem_init(&sm->timer, expiration_timer_period, /* atomic time = 1 s */ 4093, /* time slot count */ 1, /* enable delay <= terminate AFTER the timeout */ mutex); } subscription_manager_t *sm_create(send_notify_func notify, terminate_func terminate, subscription_authorize_func authorize, gen_lock_t *mutex, int min_exp, int max_exp, int default_exp, int expiration_timer_period) { subscription_manager_t *sm; sm = (subscription_manager_t*)mem_alloc(sizeof(subscription_manager_t)); if (!sm) { LOG(L_ERR, "can't allocate subscription manager\n"); return sm; } if (sm_init(sm, notify, terminate, authorize, mutex, min_exp, max_exp, default_exp, expiration_timer_period) != 0) { mem_free(sm); return NULL; } return sm; } void sm_add_subscription_nolock(subscription_manager_t *mng, subscription_data_t *s) { /* adds the subscription into the list of subscriptions */ if (!s) return; s->next = NULL; s->prev = mng->last; if (mng->last) mng->last->next = s; else mng->first = s; mng->last = s; } void sm_remove_subscription_nolock(subscription_manager_t *mng, subscription_data_t *s) { /* removes the subscription from the list of subscriptions */ if (s->prev) s->prev->next = s->next; else mng->first = s->next; if (s->next) s->next->prev = s->prev; else mng->last = s->prev; s->next = NULL; s->prev = NULL; } /****** Helper functions for subscription initialization ******/ static int create_subscription_dialog(subscription_data_t *dst, struct sip_msg *m) { /* create SIP dialog for subscription */ if (tmb.new_dlg_uas(m, 200, &dst->dialog) < 0) { LOG(L_ERR, "create_subscription_dialog(): Error while creating dialog.\n"); return -1; } else { DEBUG_LOG("create_subscription_dialog(): new dialog created (%.*s, %.*s, %.*s)\n", dst->dialog->id.call_id.len, dst->dialog->id.call_id.s, dst->dialog->id.rem_tag.len, dst->dialog->id.rem_tag.s, dst->dialog->id.loc_tag.len, dst->dialog->id.loc_tag.s); } return 0; } static int get_subscription_expiration(subscription_manager_t *mng, struct sip_msg *m) { int e = 0; /* parse Expires header field */ if (parse_headers(m, HDR_EXPIRES_T, 0) == -1) { LOG(L_ERR, "set_subscription_expiration(): Error while parsing headers\n"); return RES_PARSE_HEADERS_ERR; } if (m->expires) { if (parse_expires(m->expires) < 0) { LOG(L_ERR, "set_subscription_expiration(): Error parsing Expires header\n"); return RES_PARSE_HEADERS_ERR; } } e = mng->default_expiration; if (m->expires) { exp_body_t *expires = (exp_body_t *)m->expires->parsed; if (expires) if (expires->valid) e = expires->val; } if (e < 0) e = 0; if ((e != 0) && (e < mng->min_expiration)) { /* e = 0; */ /*e = mng->min_expiration;*/ /* Interval too short - must not be longer (RFC 3265) */ LOG(L_ERR, "set_subscription_expiration(): interval too short (%d s)\n", e); return RES_EXPIRATION_INTERVAL_TOO_SHORT; } if (e > mng->max_expiration) e = mng->max_expiration; return e; } static void free_subscription_dialog(subscription_data_t *dst) { if (dst->dialog) tmb.free_dlg(dst->dialog); dst->dialog = NULL; } static int cmp_subscription(str_t *from_tag, str_t *to_tag, str_t *call_id, subscription_data_t *s) { /* LOG(L_TRACE, "comparing element dlg: %.*s, %.*s, %.*s\n", s->dialog->id.call_id.len, s->dialog->id.call_id.s, s->dialog->id.rem_tag.len, s->dialog->id.rem_tag.s, s->dialog->id.loc_tag.len, s->dialog->id.loc_tag.s); LOG(L_TRACE, "searching for: %.*s, %.*s, %.*s\n", call_id->len, call_id->s, from_tag->len, from_tag->s, to_tag->len, to_tag->s); */ if (str_case_equals(call_id, &s->dialog->id.call_id) != 0) return 1; if (str_nocase_equals(from_tag, &s->dialog->id.rem_tag) != 0) return 1; if (str_nocase_equals(to_tag, &s->dialog->id.loc_tag) != 0) return 1; /* are the tags case sensitive? */ return 0; } /* Get resource-list URI from SUBSCRIBE request */ static int get_dst_uri(struct sip_msg* _m, str* dst_uri) { /* FIXME: get raw request URI? or from TO? * FIXME: skip uri parameters and everything else, leave only * sip:xxx@yyy ???!!! */ str uri; if (_m->new_uri.s) { uri.s = _m->new_uri.s; uri.len = _m->new_uri.len; } else { uri.s = _m->first_line.u.request.uri.s; uri.len = _m->first_line.u.request.uri.len; } if (dst_uri) *dst_uri = uri; return RES_OK; } static inline int get_from_uri(struct sip_msg* _m, str* _u) { if (parse_from_header(_m) < 0) { LOG(L_ERR, "get_from_uri(): Error while parsing From body\n"); return -1; } _u->s = ((struct to_body*)_m->from->parsed)->uri.s; _u->len = ((struct to_body*)_m->from->parsed)->uri.len; return 0; } /* Get subscriber's URI from SUBSCRIBE request */ static int get_subscribers_uri(struct sip_msg* _m, str* dst_uri) { /* FIXME: skip uri parameters !!! */ /* str uri; */ str u; struct sip_uri s; if (!dst_uri) return RES_INTERNAL_ERR; if (parse_from_header(_m) < 0) { LOG(L_ERR, "get_subscribers_uri(): Error while parsing From header\n"); return RES_PARSE_HEADERS_ERR; } u = ((struct to_body*)_m->from->parsed)->uri; if (parse_uri(u.s, u.len, &s) < 0) { LOG(L_ERR, "get_subscribers_uri(): Error while parsing From content\n"); return RES_PARSE_HEADERS_ERR; } dst_uri->s = u.s; dst_uri->len = s.host.s + s.host.len - dst_uri->s; /* if (s.user.len > 0) uri.s = s.user.s; else uri.s = u.s; if (s.host.len <= 0) uri = u; else uri.len = s.host.s - uri.s + s.host.len; if (dst_uri) *dst_uri = uri;*/ return RES_OK; } /* Get Event package from SUBSCRIBE request */ static int get_package(struct sip_msg* m, str* dst) { dst->len = 0; dst->s = NULL; if ( (parse_headers(m, HDR_EVENT_T, 0) == -1) || (!m->event) ) { LOG(L_ERR, "get_package(): Error while parsing Event header\n"); return RES_PARSE_HEADERS_ERR; } dst->s = m->event->body.s; dst->len = m->event->body.len; return RES_OK; } static int set_subscription_info(struct sip_msg *m, subscription_data_t *s) { str uri, subscriber_uri; str package; int r; /* get requested resource list URI */ r = get_dst_uri(m, &uri); if (r != RES_OK) { LOG(L_ERR, "set_rls_info(): Can't decode resource list URI\n"); return r; } /* get subscriber's URI */ r = get_subscribers_uri(m, &subscriber_uri); if (r != RES_OK) { LOG(L_ERR, "set_rls_info(): Can't decode subscriber's URI\n"); return r; } /* get event package */ r = get_package(m, &package); if (r != RES_OK) { return r; } extract_server_contact(m, &s->contact, 0); DEBUG_LOG("set_subscription_info(): uri=\'%.*s\'\n", FMT_STR(uri)); DEBUG_LOG("set_subscription_info(): package=\'%.*s\'\n", FMT_STR(package)); DEBUG_LOG("set_subscription_info(): subscriber_uri=\'%.*s\'\n", FMT_STR(subscriber_uri)); DEBUG_LOG("set_subscription_info(): contact=\'%.*s\'\n", FMT_STR(s->contact)); r = str_dup(&s->record_id, &uri); if (r == 0) r = str_dup(&s->subscriber, &subscriber_uri); else str_clear(&s->subscriber); if (r == 0) r = str_dup(&s->package, &package); else str_clear(&s->package); return r; } static void free_subscription(subscription_data_t *s) { DEBUG_LOG("subscription manager: freeing subscription\n"); str_free_content(&s->record_id); str_free_content(&s->package); str_free_content(&s->subscriber); str_free_content(&s->contact); free_subscription_dialog(s); } /****** Functions for standalone subscription manipulation ******/ void subscription_expiration_cb(struct _time_event_data_t *ted) { /* the time event manager uses the same mutex and it is locked now ! */ time_t t = time(NULL); subscription_manager_t *mng; subscription_data_t *s; mng = ted->cb_param1; s = ted->cb_param; DBG("subscription %p(%p) expired at: %s\n", s, mng, ctime(&t)); if (mng && s) { if (s->status == subscription_pending) s->status = subscription_terminated_pending_to; else s->status = subscription_terminated_to; if (mng->notify) mng->notify(s); if (mng->terminate) mng->terminate(s); } } int sm_init_subscription_nolock(subscription_manager_t *mng, subscription_data_t *dst, struct sip_msg *m) { int e, res; authorization_result_t ares = auth_granted; if (!dst) return RES_INTERNAL_ERR; /* dst->usr_data = NULL; */ /* !!! do not initialize this - its user's and may be already initialized !!! */ dst->prev = NULL; dst->next = NULL; dst->dialog = NULL; dst->contact.s = NULL; dst->contact.len = 0; dst->status = subscription_uninitialized; str_clear(&dst->record_id); str_clear(&dst->subscriber); str_clear(&dst->package); /* fill time event structure */ dst->expiration.cb = subscription_expiration_cb; dst->expiration.cb_param = dst; dst->expiration.cb_param1 = mng; res = set_subscription_info(m, dst); if (res != RES_OK) { free_subscription(dst); return res; } create_subscription_dialog(dst, m); if (mng->authorize) ares = mng->authorize(dst); switch (ares) { case auth_granted: dst->status = subscription_active; break; case auth_polite_block: LOG(L_WARN, "polite blocking not implemented - marking subscription as rejected!\n"); /* other possibility is to give it to pending state, but this eats resources */ dst->status = subscription_terminated; return RES_SUBSCRIPTION_REJECTED; case auth_rejected: dst->status = subscription_terminated; return RES_SUBSCRIPTION_REJECTED; case auth_unresolved: dst->status = subscription_pending; break; } /* set expiration timeout from min, max, default and Expires header field */ e = get_subscription_expiration(mng, m); if (e < 0) { free_subscription(dst); return e; /* it contains the error number */ } /* add this subscription to the list of subscriptions */ sm_add_subscription_nolock(mng, dst); /* FIXME - bug? - add if e == 0 too? */ if (e > 0) { /* start timeout timer for this subscription */ tem_add_event_nolock(&mng->timer, e, &dst->expiration); DEBUG_LOG("subscription will expire in %d s\n", e); } else { /* polling */ if (dst->status == subscription_pending) dst->status = subscription_terminated_pending; else dst->status = subscription_terminated; } return RES_OK; } int sm_init_subscription_nolock_ex(subscription_manager_t *mng, subscription_data_t *dst, dlg_t *dialog, subscription_status_t status, const str_t *contact, const str_t *record_id, const str_t *package, const str_t *subscriber, int expires_after, void *subscription_data) { int r = 0; if (!dst) return RES_INTERNAL_ERR; dst->usr_data = subscription_data; dst->prev = NULL; dst->next = NULL; dst->dialog = dialog; r = str_dup(&dst->contact, contact); dst->status = status; if (r == 0) r = str_dup(&dst->record_id, record_id); else str_clear(&dst->record_id); if (r == 0) r = str_dup(&dst->subscriber, subscriber); else str_clear(&dst->subscriber); if (r == 0) r = str_dup(&dst->package, package); else str_clear(&dst->package); /* fill time event structure */ dst->expiration.cb = subscription_expiration_cb; dst->expiration.cb_param = dst; dst->expiration.cb_param1 = mng; DEBUG_LOG("uri=\'%.*s\'\n", FMT_STR(dst->record_id)); DEBUG_LOG("package=\'%.*s\'\n", FMT_STR(dst->package)); DEBUG_LOG("subscriber_uri=\'%.*s\'\n", FMT_STR(dst->subscriber)); DEBUG_LOG("contact=\'%.*s\'\n", FMT_STR(dst->contact)); /* set expiration timeout from min, max, default and Expires header field */ if (expires_after < 0) expires_after = 0; if (expires_after > 0) { /* start timeout timer for this subscription */ tem_add_event_nolock(&mng->timer, expires_after, &dst->expiration); DEBUG_LOG("subscription will expire in %d s\n", expires_after); } else { /* polling */ if (dst->status == subscription_pending) dst->status = subscription_terminated_pending; else dst->status = subscription_terminated; } /* add this subscription to the list of subscriptions */ sm_add_subscription_nolock(mng, dst); /* FIXME - bug? - add if e == 0 too? */ return r; } int sm_refresh_subscription_nolock(subscription_manager_t *mng, subscription_data_t *s, struct sip_msg *m) { int e; if (!s) return RES_INTERNAL_ERR; /* refresh SIP dialog */ if (s->dialog) tmb.dlg_request_uas(s->dialog, m, IS_TARGET_REFRESH); if (sm_subscription_terminated(s) != 0) { /* not terminated */ tem_remove_event_nolock(&mng->timer, &s->expiration); } else return RES_SUBSCRIPTION_TERMINATED; /* fill time event structure */ s->expiration.cb = subscription_expiration_cb; s->expiration.cb_param = s; s->expiration.cb_param1 = mng; /* set expiration timeout from min, max, default and Expires header field */ e = get_subscription_expiration(mng, m); if (e < 0) return e; /* it contains the error number */ if (e == 0) { /* unsubscribe */ if (s->status == subscription_pending) s->status = subscription_terminated_pending; else s->status = subscription_terminated; } else { /* start timeout timer for this subscription */ tem_add_event_nolock(&mng->timer, e, &s->expiration); DEBUG_LOG("subscription refreshed, will expire in %d s\n", e); } return RES_OK; } void sm_release_subscription_nolock(subscription_manager_t *mng, subscription_data_t *dst) { if (!dst) return; if (dst->status == subscription_uninitialized) return; if (sm_subscription_terminated(dst) != 0) { /* NOT terminated */ /* remove timeout timer */ tem_remove_event_nolock(&mng->timer, &dst->expiration); } /* remove this subscription from the list */ sm_remove_subscription_nolock(mng, dst); free_subscription(dst); } int sm_prepare_subscription_response(subscription_manager_t *mng, subscription_data_t *s, struct sip_msg *m) { char tmp[64]; int t = 0; if (s->contact.len > 0) { if (!add_lump_rpl(m, s->contact.s, s->contact.len, LUMP_RPL_HDR)) { LOG(L_ERR, "sm_prepare_subscription_response(): Can't add Contact header to the response\n"); return -1; } } t = sm_subscription_expires_in(mng, s); sprintf(tmp, "Expires: %d\r\n", t); if (!add_lump_rpl(m, tmp, strlen(tmp), LUMP_RPL_HDR)) { LOG(L_ERR, "sm_prepare_subscription_response(): Can't add Expires header to the response\n"); return -1; } return 0; } int sm_subscription_expires_in(subscription_manager_t *mng, subscription_data_t *s) { int t = 0; if (sm_subscription_terminated(s) != 0) /* NOT terminated */ t = (s->expiration.tick_time - mng->timer.tick_counter) * mng->timer.atomic_time; return t; } int sm_find_subscription(subscription_manager_t *mng, str_t *from_tag, str_t *to_tag, str_t *call_id, subscription_data_t **dst) { subscription_data_t *e; /* FIXME: use hash table or something like that ! */ *dst = NULL; e = mng->first; while (e) { if (cmp_subscription(from_tag, to_tag, call_id, e) == 0) { *dst = e; return RES_OK; } e = e->next; } return RES_NOT_FOUND; } int sm_subscription_terminated(subscription_data_t *s) { if (!s) return 0; if (s->status == subscription_terminated) return 0; if (s->status == subscription_terminated_to) return 0; if (s->status == subscription_terminated_pending) return 0; if (s->status == subscription_terminated_pending_to) return 0; return 1; /* 1 means NOT terminated ! */ } int sm_subscription_pending(subscription_data_t *s) { if (!s) return 0; if (s->status == subscription_pending) return 0; if (s->status == subscription_terminated_pending) return 0; if (s->status == subscription_terminated_pending_to) return 0; return 1; /* 1 means NOT pending ! */ } kamailio-4.0.4/obsolete/rls/README0000644000000000000000000004363012223032460015277 0ustar rootroot1. RLS (Resource List Server) Vaclav Kubart Iptel/Tekelec __________________________________________________________________ 1.1. Dependencies 1.2. RLS and XCAP 1.2.1. Used terms 1.2.2. Processing subscriptions 1.2.3. Global resource lists 1.2.3.1. rls-services document URI 1.2.3.2. Disadvantages 1.2.4. User's resource lists 1.2.5. Standard incompliances 1.3. Parameters 1.4. Functions Resource lists server is a server which allows subscriptions to lists of users. Its behaviour is defined in [rls] and [sip rls]. As described there, it uses XCAP server for storing data about lists of users. These data can be manipulated in any way by user's client software. 1.1. Dependencies Modules * tm * pa * dialog * optionaly database module (mysql, ...) Libraries * libcds (internal) * libxcap (internal) - XCAP queries * libpresence (internal) - used for internal subscriptions ta PA/presence_b2b 1.2. RLS and XCAP 1.2.1. Used terms RLS Resource List Server - server processing subscriptions to special URIs (RL-URIs) which represent more resources than only one like "normal URIs". RL-URI Resource list URI. URI which represents a list of resources instead of a single user. 1.2.2. Processing subscriptions Specification says that each time a SUBSCRIBE request to a RL-URI comes, RLS has to ask XCAP server for coresponding in default rls-services document. The XCAP query in this case is for document like /rls-services/global/index/~~/rls-services/service[@uri=%22< AOR>%22]. Returned is processed according to specification then and the result of it is flat list of URIs to subscribe to. This processing has its weaknesses. * In case of such processing is one element in rls-services needed for every user who wants to have his "buddies" stored on XCAP server. Such will (for user's records) mostly point into his resource-lists document and thus another XCAP query for result list is needed. * Another problem is, that each user has to modify global rls-services document. This is security problem and there is no client software doing this itself. [Exists something?] These reasons lead to separation of resource lists into "global lists" and "users lists". Global lists are not stored/modified by regular users; they are managed by administrators and they are handled according the draft. Example of such lists may be "technical support", "human resources", ... Resource lists for users are stored only in their resource-lists documents and the element in global rls-services document is ommited (it is "implicit"). In this case RLS reads directly user's resource-lists document instead of trying to find it in global rls-services document. 1.2.3. Global resource lists As said above, resources described in rls-services documents are global resources accessible by all users. No regular user should have acces rights to modify global rls-services document on XCAP server. 1.2.3.1. rls-services document URI The construction of rls-services document URI is described in [rls]. Only in short: the AOR from SIP SUBSCRIBE request is combined with XCAP root given in configuration like this: /rls-services/global/index/~~/rls-services/service[@uri=%22< AOR>%22]. This URI doesn't not specify namespaces as mentioned in definition, but this is due to problems with XCAP server used for tests (problems querying parts of documents with namespaces). Example 1. rls-services uri example Let us assume * xcap-root = http://localhost/xcap-root * AOR of SUBSCRIBE request = sip:technical-support@someorg.org Resulting document describing the list will be get from URI: http://localhost/xcap-root/rls-services/global/index/~~/rls-services/se rvice[@uri=%22%22], which means the service element with uri parameter value technical-support@someorg.org stored in rls-services document named index. Example 2. rls-services document There is example rls-services document describing resources "technical-support@test-domain.com", "hr@test-domain.com" and "work@test-domain.com" which puts previous two into itself. This document should be stored on xcap server in file /rls-services/global/index. Smith Joe Agatha presence Johny V Joe presence Technical support Human resources presence 1.2.3.2. Disadvantages Working with URIs presented in this section have one big disadvantage - it needs full XCAP server which is able to work with partial documents and able to process XPointer expressions in XCAP queries. Due to unavailability of free XCAP servers is there a possibility to use SER's RLS server in mode of reduced XCAP server needs (see RLS module parameters). If operating in this mode, RLS requests full rls-service document from uri /rls-services/global/index, inspects it and finds requested resource list according to URI and AOR by itself. (Only if possible! There can't be links to partial documents in rls-services document.) 1.2.4. User's resource lists Opposite to global resource lists are resource lists of standalone users. As was told above, these lists are stored as resource-lists documents under user's directories. These resource-lists documents are accessed directly without searching for them in rls-services document - they behave like if they have implicit link there. Example 3. User's resource lists Let us assume * xcap root = http://localhost/xcap-root * user smith (sends SUBSCRIBE with smith@... in From URI) * Smith's UUID = smith The document below for user Smith can be stored on XCAP server in http://localhost/xcap-root/resource-lists/users/smith/resource-lists.xm l. It contains two resource lists: "default" and "contacts". Buddy List Joe Jan Contact List Alois 1.2.5. Standard incompliances SER's resource lists support is not finished yet, there are some standard incompliances now: * uri canonicalization not done yet according to definition * full status documents only 1.3. Parameters min_expiration (integer) Minimal subscription expiration timeout in seconds. If client supplies value, which is less than value of min_expiration, the server returns response "423 Interval too small" as described in [events]. Default value is 60. max_expiration (integer) Maximal subscription expiration timeout in seconds. If client supplies value, which is more than value of max_expiration, the server shortens this value to value of this variable as described in [events]. Default value is 7200. default_expiration (integer) Default subscription expiration timeout in seconds. If client doesn't supply subscription expiration timeout this value is used. Default value is 3761. auth (string) This variable specifies authorization type for list watchers. Value can be one of: none All watchers are always authorized. This is not recommended because it ignores user's wish. implicit In this case is implicit authorization done. This means, that for list URIs in the form "-list@domain" is subscription allowed only for user with username , rejected for others. If the URI is not in the form presented above, the subscription is marked as pending. Default value is empty. In this case "implict" authorization is used with an error message. reduce_xcap_needs (int) If set to 1 the module tries to do simplify XCAP queries - queries will be done for whole documents, not partial, thus the XCAP server may be simulated using standard web server. It has influence only on querying resource lists and it will work correctly only if there are no links to resource lists containg partial documents URIs (lists should be contained directly in main rls-services document). Default value is 0. db_mode (integer) If set to 1, RLS module stores all subscription data into database and reloads them on startup. Requires db_url to be set. Default value is 0. db_url (integer) Database connection URL. It has to be specified if db_mode is set. Default value is empty. Example 4. db_url settings ... modparam("rls", "db_mode", 1) modparam("rls", "db_url", "mysql://ser:heslo@127.0.0.1:3306/ser") ... max_notifications_at_once Max. number of notifications sent within one timer tick (experimental). It might be used to reduce SER's machine load if there are lots of changes in presence status. Default value is 1000000. (Too high number which in praxis means unlimited.) timer_interval Interval in seconds when are processed internal notifications and sent NOTIFYs to subscribers. max_nesting_level Maximum number of nested lists. For example if set to 2, it it possible to use "list nested in list nested in root list". Default value is -1 what means "unlimited". It is possible to use this for speedup - if you know, that you will need only "flat" lists (no nested lists), you can set this to 0. In this case RLS doesn't try to query XCAP server for "possibly netsted list URIs" and directly creates subscription to URIs in list like if they are URIs of standalone users. expiration_timer_period Interval in seconds of timer which removes expired subscriptions. ignore_408_on_notify If set to 1 and 408 response to NOTIFY arrives, the dialog is NOT destroyed like in the case of other non-2xx responses. Use for testing only. Default value is 0. init_timer_delay Delay in seconds of timer which triggers loading data from database after startup. This is needed due to dependencies on other modules (pa/presence_b2b) - we need that these modules will be successfully initialised before reading data from DB and querying these modules. Default value is 3. 1.4. Functions handle_rls_subscription Handle subscription to resource list uri. Parameters: create_error_response (integer) If set to 0 error responses are not sent to the client. This may be useful, if used together with PA module. This function handles resource list subscription. XCAP document containing the list must be loaded before using one of query function (query_rls_services, query_resource_list). Example 5. handling subscription ... if (method=="SUBSCRIBE") { if (!t_newtran()) { sl_reply_error(); }; if (query_rls_services()) { handle_rls_subscription("1"); } else { # no such list exists -> process it with PA module handle_subscription("registrar"); } break; }; ... In this case for every incomming SUBSCRIBE request SER asks for "global resource list". If such list exists, the subscription is processed like resource list subscription. is_simple_rls_target Test uri according to given template. Parameters: template (string) Template to be compared with To URI. Function tries to compare username in To URI with given template. If there are different domains in To and From URIs, the function fails. There can be used "$uid" in template and it is replaced by value returned by get_from_uid. In the future should be this function replaced by AVP operations but I was not able to do it with them now... Example 6. handling subscription ... if (method=="SUBSCRIBE") { if (!t_newtran()) { sl_reply_error(); break; }; if (lookup_domain("From")) { if (lookup_user("From")) { if (is_simple_rls_target("$uid-list")) { # takes From UID and makes XCAP query # for user's list named "default" if (!query_resource_list("default")) { t_reply("500", "XCAP query error"); break; } handle_rls_subscription("1"); break; } } } } ... In this case if SUBSCRIBE request arrives and From is smith@test-domain.com and To is smith-list@test-domain.com, the function returns true and the subscription is handled like subscription to "user's resource list" (not "global") as written above. query_rls_services Get list from global rls-services document. Function loads "global resource list" from XCAP server. This list is then processed by RLS module using handle_rls_subscription. query_resource_list Get user's resource list. Parameters: list_name (string) Name of the requested list. If empty, it loads whole user's resource-lists document as one list. Function loads "user's resource list" from XCAP server. This list is then processed by RLS module using handle_rls_subscription. have_flat_list Test if a resource list is loaded. Function tests if a list was loaded using one of query functions (query_resource_list or query_rls_services) and returns 1 if yes and -1 otherwise. Bibliography Note There might be new versions of internet drafts and thus links to them my be obsolete. In such case try increment version in link or find the draft on IETF by name. XCAP [xcap] draft-ietf-simple-xcap-12.txt - XCAP specification. [xcap_diff] draft-ietf-simple-xcap-diff-01.txt - XCAP changes notifications format. [xcap_profiles] draft-ietf-sipping-config-framework-07.txt -XCAP user profiles. Presence [events] RFC 3265 - SIP Events. [presence] RFC 3856 - Presence Event package. [pidf] RFC 3863 - Presence Information Data Format. [rpid] RFC 4480 - Rich Presence Extensions to the Presence Information Data Format. [publish] RFC 3903 - Event state publication. [winfo] RFC 3857 - Watcher info event package. [winfo_doc] RFC 3858 - Data Format for watcher info. [reg] RFC 3680 - SIP Reg Events. Authorization [common auth] draft-ietf-geopriv-common-policy-05.txt. [presence auth] draft-ietf-simple-presence-rules-03.txt - presence authorization XML based data format and usage with XCAP. Resource lists [rls] draft-ietf-simple-xcap-list-usage-05.txt - XML formats for representing resource lists. [sip rls] draft-ietf-simple-event-list-07.txt - Event Notification Extension for Resource Lists. kamailio-4.0.4/obsolete/rls/rpc.h0000644000000000000000000000016012223032460015343 0ustar rootroot#ifndef __RLS_RPC_H #define __RLS_RPC_H #include "../../rpc.h" extern rpc_export_t rls_rpc_methods[]; #endif kamailio-4.0.4/obsolete/rls/rls_auth.c0000644000000000000000000000344112223032460016400 0ustar rootroot#include "rls_auth.h" #if 0 static int get_user_from_uri(str_t *uri, str_t *user) { char *a; char *d; char *s; /* we can't use SER's parser - the uri may have not the protocol prefix! */ str_clear(user); if (uri->len > 0) { d = strchr(uri->s, ':'); if (d) s = d + 1; else s = uri->s; a = strchr(s, '@'); if (a) { user->s = s; user->len = a - s; return 0; } } return -1; } static authorization_result_t authorize_implicit(struct _subscription_data_t *s) { str_t user, list; str_t list_user, list_rest; str_t appendix = { s: "-list", len: 5 }; if (get_user_from_uri(&s->subscriber, &user) != 0) return auth_unresolved; /* we can't decide - it is not "implicit" uri */ if (get_user_from_uri(&s->record_id, &list) != 0) return auth_unresolved; /* we can't decide - it is not "implicit" uri */ if (list.len <= appendix.len) return auth_unresolved; /* we can't decide - it is not "implicit" uri */ list_rest.len = appendix.len; list_rest.s = list.s + list.len - appendix.len; if (str_case_equals(&list_rest, &appendix) != 0) return auth_unresolved; /* we can't decide - it is not "implicit" uri */ /* now we know, that it ends with implicit uri ending */ list_user.s = list.s; list_user.len = list.len - appendix.len; if (str_case_equals(&user, &list_user) != 0) return auth_rejected; else return auth_granted; } #endif authorization_result_t rls_authorize_subscription(struct _subscription_data_t *s) { switch (rls_auth_params.type) { case rls_auth_none: return auth_granted; /* ! no auth done ! */ case rls_auth_implicit: return auth_granted; /* ! no auth done ! */ /* return authorize_implicit(s); */ case rls_auth_xcap: LOG(L_ERR, "XCAP auth for resource lists not done yet!\n"); return auth_unresolved; } return auth_unresolved; } kamailio-4.0.4/obsolete/rls/trace.h0000644000000000000000000000034612223032460015663 0ustar rootroot#ifndef __TRACE_H #define __TRACE_H #include #include #include #define mem_alloc cds_malloc #define mem_free cds_free #define TRACE(...) TRACE_LOG("RLS: " __VA_ARGS__) #endif kamailio-4.0.4/obsolete/rls/rpc.c0000644000000000000000000000462012223032460015343 0ustar rootroot#include "rpc.h" #include "rl_subscription.h" #include "rls_data.h" #include "../../dprint.h" #include /* #define rpc_lf(rpc, c) rpc->add(c, "s","") rpc->printf(c, " %.*s contact=\'%.*s\' exp=%u status=%d published=%d (id=%.*s)", FMT_STR(t->id), FMT_STR(t->contact), t->expires - time(NULL), (int)t->state, t->is_published, FMT_STR(t->published_id)); rpc_lf(rpc, c); */ /* #define rpc_lf(rpc, c) rpc->add(c, "s","") */ #define rpc_lf(rpc, c) do { } while (0) static void trace_vs(rpc_t *rpc, void *c, virtual_subscription_t *vs, int details) { rpc->printf(c, " Virtual subscriptions:"); rpc_lf(rpc, c); rpc->printf(c, " -> URI = %.*s", FMT_STR(vs->uri)); rpc_lf(rpc, c); rpc->printf(c, " -> status = %d", vs->status); rpc_lf(rpc, c); if (details > 0) { rpc->printf(c, " -> document = %.*s", FMT_STR(vs->state_document)); rpc_lf(rpc, c); } rpc_lf(rpc, c); } static void rls_trace_subscription(rpc_t *rpc, void *c, rl_subscription_t *s, int details) { virtual_subscription_t *vs; int cnt, i; switch (s->type) { case rls_internal_subscription: rpc->printf(c, "URI = %.*s", FMT_STR(*s->u.internal.record_id)); rpc_lf(rpc, c); break; case rls_external_subscription: rpc->printf(c, "URI = %.*s", FMT_STR(s->u.external.record_id)); rpc_lf(rpc, c); break; } cnt = ptr_vector_size(&s->vs); for (i = 0; i < cnt; i++) { vs = ptr_vector_get(&s->vs, i); if (!vs) continue; if (details > 0) trace_vs(rpc, c, vs, details - 1); } rpc_lf(rpc, c); } static void rls_trace(rpc_t *rpc, void *c) { int i = 0; subscription_data_t *s; rl_subscription_t *rs; int details = 0; if (rpc->scan(c, "d", &details) <= 0) details = 0; rpc->fault(c, 200, "OK"); rpc->add(c, "s", "RLS Trace:"); if (!rls) { rpc->printf(c, "problems"); rpc->send(c); return; } s = rls_manager->first; while (s) { i++; rs = (rl_subscription_t*)(s->usr_data); if (details > 0) rls_trace_subscription(rpc, c, rs, details); s = s->next; } rpc->printf(c, "subscription count: %d", i); rpc_lf(rpc, c); rpc->send(c); } /* ----- exported data structure with methods ----- */ static const char* rls_trace_doc[] = { "RLS trace.", /* Documentation string */ 0 /* Method signature(s) */ }; /* * RPC Methods exported by this module */ rpc_export_t rls_rpc_methods[] = { {"rls.trace", rls_trace, rls_trace_doc, 0}, {0, 0, 0, 0} }; kamailio-4.0.4/obsolete/rls/uri_ops.h0000644000000000000000000000034412223032460016243 0ustar rootroot#ifndef __RLS_URI_OPS_H #include "rls_mod.h" /* URI functions for usage from CFG script */ /* tries to look on To URI/AOR according to given*/ int is_simple_rls_target(struct sip_msg *m, char *what, char *_template); #endif kamailio-4.0.4/obsolete/rls/rl_subscription.h0000644000000000000000000001207712223032460020012 0ustar rootroot#ifndef __RL_SUBSCRIPTION_H #define __RL_SUBSCRIPTION_H #include "../../modules/tm/dlg.h" #include "../../lock_ops.h" #include "subscription_manager.h" #include #include #include #include #include #include #include #include "trace.h" /* type for generated database ID */ typedef char db_id_t[48]; typedef enum { rls_auth_none, rls_auth_implicit, rls_auth_xcap } rls_authorization_type_t; typedef struct { rls_authorization_type_t type; } rls_auth_params_t; struct _virtual_subscription_t; struct _rl_subscription_t; typedef struct _rl_subscription_t rl_subscription_t; typedef struct { str name; str lang; } vs_display_name_t; typedef struct _virtual_subscription_t { /* helper to reduce memory allocations */ qsa_subscription_data_t local_subscription_pres_data; /* local subscription data */ qsa_subscription_t *local_subscription_pres; rl_subscription_t *local_subscription_list; vector_t display_names; rl_subscription_t *subscription; int changed; /** whole document describing the state of this resource */ str state_document; /** type of this state_document */ str content_type; /** status of this subscription */ subscription_status_t status; /* VS identifier */ str uri; struct _virtual_subscription_t *next; struct _virtual_subscription_t *prev; /* generated id for database */ db_id_t dbid; char uri_str[1]; } virtual_subscription_t; typedef enum { rls_internal_subscription, rls_external_subscription } rls_subscription_type_t; typedef struct { str *package; /* points to "parent" subscription */ str *record_id; /* NEVER free this - it points into VS data */ str *subscriber_id; /* NEVER free this - it points into "parent" subscription */ /* created from this virtual subscription */ virtual_subscription_t *vs; } internal_subscription_data_t; /** subscription to the list of resources */ struct _rl_subscription_t { rls_subscription_type_t type; /* XCAP server settings (needed for reloading internal subscriptions * from DB, XCAP notifications, ...) */ xcap_query_params_t xcap_params; union { /* data of external subscription */ subscription_data_t external; /* data of internal subscription (pointer to "parent" * virtual subscription) */ internal_subscription_data_t internal; } u; /** sequence number of NOTIFY */ int doc_version; /** the count of changed virtual subscriptions * (enough changes ?= send notify) */ int changed; /* virtual subscriptions for this rls */ ptr_vector_t vs; /* uid of the watcher */ str from_uid; /* generated id for database */ db_id_t dbid; }; str_t * rls_get_package(rl_subscription_t *s); str_t * rls_get_uri(rl_subscription_t *s); str_t * rls_get_subscriber(rl_subscription_t *subscription); /********* resource list subscription functions ********/ int rls_create_subscription(struct sip_msg *m, rl_subscription_t **dst, flat_list_t *flat, xcap_query_params_t *params); int rls_create_internal_subscription(virtual_subscription_t *vs, rl_subscription_t **dst, flat_list_t *flat, int nesting_level); int rls_refresh_subscription(struct sip_msg *m, rl_subscription_t *s); int rls_find_subscription(str *from_tag, str *to_tag, str *call_id, rl_subscription_t **dst); void rls_free(rl_subscription_t *s); /* removes from memory only */ void rls_remove(rl_subscription_t *s); /* finishes subscription - removes from DB, mem, ... */ int rls_generate_notify(rl_subscription_t *s, int full_info); int rls_prepare_subscription_response(rl_subscription_t *s, struct sip_msg *m); /* void rls_notify_all_modified(); */ /********* virtual subscription functions ********/ int vs_init(); int vs_destroy(); int vs_create(str *uri, virtual_subscription_t **dst, display_name_t *dnames, rl_subscription_t *subscription, int nesting_level); int vs_add_display_name(virtual_subscription_t *vs, const char *name, const char *lang); void vs_free(virtual_subscription_t *vs); int create_virtual_subscriptions(rl_subscription_t *ss, int nesting_level); int add_virtual_subscriptions(rl_subscription_t *ss, flat_list_t *flat, int nesting_level); /* database operations */ int rls_db_add(rl_subscription_t *s); int rls_db_remove(rl_subscription_t *s); int rls_db_update(rl_subscription_t *s); int db_load_rls(); /* load stored subscriptions on startup */ /* helper functions */ void generate_db_id(db_id_t *id, void *data); /** returns the count of seconds remaining to subscription expiration */ int rls_subscription_expires_in(rl_subscription_t *s); /* allocates and initializes structure */ rl_subscription_t *rls_alloc_subscription(rls_subscription_type_t type); /* XCAP queries */ int xcap_query_rls_services(xcap_query_params_t *xcap_params, const str *uri, const str *package, flat_list_t **dst); /* internal notification */ void process_internal_notify(virtual_subscription_t *vs, str_t *new_state_document, str_t *new_content_type); void process_rls_notification(virtual_subscription_t *vs, client_notify_info_t *info); #endif kamailio-4.0.4/obsolete/rls/rls.sql0000644000000000000000000000117012223032460015731 0ustar rootrootdrop table if exists rls_subscription; CREATE TABLE rls_subscription ( id varchar(48) NOT NULL, doc_version int, dialog blob, expires datetime NOT NULL, status int, contact varchar(128), uri varchar(128), package varchar(128), w_uri varchar(128), PRIMARY KEY (id) ) TYPE=MyISAM; drop table if exists rls_vs; CREATE TABLE rls_vs ( id varchar(48) NOT NULL, rls_id varchar(48) NOT NULL, uri varchar(128), PRIMARY KEY (id) ) TYPE=MyISAM; drop table if exists rls_vs_names; CREATE TABLE rls_vs_names ( id varchar(48) NOT NULL, name varchar(64), lang varchar(64) ) TYPE=MyISAM; kamailio-4.0.4/obsolete/rls/rls_data.h0000644000000000000000000000120112223032460016345 0ustar rootroot#ifndef __RLS_DATA_H #define __RLS_DATA_H #include "rl_subscription.h" typedef struct { /* optimization - when a subscription is flagged as changed, * this number is increased (means something like priority of * call to "change all modified RLS") */ int changed_subscriptions; /* hash, ... */ msg_queue_t notify_mq; } rls_data_t; extern rls_data_t *rls; extern subscription_manager_t *rls_manager; /* removes all notifications for given qsa_subscription from message queue * and discards them */ void destroy_notifications(qsa_subscription_t *s); int rls_init(); int rls_destroy(); void rls_lock(); void rls_unlock(); #endif kamailio-4.0.4/obsolete/rls/rls_mod.h0000644000000000000000000000223212223032460016220 0ustar rootroot#ifndef __RLS_MOD_H #define __RLS_MOD_H #include "../../modules/tm/tm_load.h" #include "../../lib/srdb2/db.h" #include "rl_subscription.h" #include "../dialog/dlg_mod.h" #include "rls_data.h" #include #include "../xcap/xcap_mod.h" extern struct tm_binds tmb; /** min interval for subscription expiration */ extern int rls_min_expiration; /** max interval for subscription expiration */ extern int rls_max_expiration; /* how often test subscriptions for expiration */ extern int rls_expiration_timer_period; /** default expiration timeout */ extern int rls_default_expiration; /** authorization parameters */ extern rls_auth_params_t rls_auth_params; extern int use_db; extern db_con_t* rls_db; /* database connection handle */ extern db_func_t rls_dbf; /* database functions */ extern dlg_func_t dlg_func; extern char *db_url; extern int reduce_xcap_needs; /* allows XCAP simulation with web server if possible */ extern int rls_timer_interval; extern fill_xcap_params_func fill_xcap_params; /* parameters for optimizations */ extern int max_notifications_at_once; extern int max_list_nesting_level; extern int rls_ignore_408_on_notify; #endif kamailio-4.0.4/obsolete/rls/db_rls.c0000644000000000000000000003265012223032460016030 0ustar rootroot#include #include #include "rl_subscription.h" #include "rls_mod.h" char *rls_table = "rls_subscription"; char *vs_table = "rls_vs"; char *vs_names_table = "rls_vs_names"; /* generate ID for given data */ void generate_db_id(db_id_t *id, void *data) { if (id) { snprintf(*id, sizeof(*id), "%px%xx%x", data, (int)time(NULL), rand()); /* DEBUG_LOG("generated DB ID = %s\n", *id); */ } } #define string_val(v,s) (v).type = DB_STR; \ (v).val.str_val=s; \ (v).nul=(s.len == 0); #define int_val(v,i) (v).type = DB_INT; \ (v).val.int_val=i;\ (v).nul=0; #define time_val(v,t) (v).type = DB_DATETIME; \ (v).val.time_val=t;\ (v).nul=0; #define string_val_ex(v,str,l) (v).type = DB_STR; \ (v).val.str_val.s=str; \ (v).val.str_val.len=l; \ (v).nul=0; #define blob_val(v,str) (v).type = DB_BLOB; \ (v).val.blob_val=str; \ (v).nul=0; /* ------------- virtual subscriptions ------------- */ static int virtual_subscription_db_add(virtual_subscription_t *vs, rl_subscription_t *s) { db_key_t cols[20]; db_val_t vals[20]; int n = -1; int i, cnt; vs_display_name_t *dn; DEBUG_LOG("storing into database\n"); if (rls_dbf.use_table(rls_db, vs_table) < 0) { LOG(L_ERR, "vsub_db_add: Error in use_table\n"); return -1; } cols[++n] = "uri"; string_val(vals[n], vs->uri); cols[++n] = "id"; string_val_ex(vals[n], vs->dbid, strlen(vs->dbid)); cols[++n] = "rls_id"; string_val_ex(vals[n], s->dbid, strlen(s->dbid)); /* insert new record into database */ if (rls_dbf.insert(rls_db, cols, vals, n + 1) < 0) { LOG(L_ERR, "vsub_db_add: Error while inserting virtual subscription\n"); return -1; } /* store display names */ cnt = vector_size(&vs->display_names); for (i = 0; i < cnt; i++) { if (rls_dbf.use_table(rls_db, vs_names_table) < 0) { LOG(L_ERR, "vsub_db_add (names): Error in use_table\n"); return -1; } dn = vector_get_ptr(&vs->display_names, i); if (!dn) continue; n = -1; cols[++n] = "id"; string_val_ex(vals[n], vs->dbid, strlen(vs->dbid)); cols[++n] = "name"; string_val(vals[n], dn->name); cols[++n] = "lang"; string_val(vals[n], dn->lang); if (rls_dbf.insert(rls_db, cols, vals, n + 1) < 0) { LOG(L_ERR, "vsub_db_add: Error while inserting name\n"); return -1; } } return 0; } static int vs_db_add(rl_subscription_t *s) { int i, cnt; int res = 0; virtual_subscription_t *vs; cnt = ptr_vector_size(&s->vs); for (i = 0; i < cnt; i++) { vs = ptr_vector_get(&s->vs, i); if (!vs) continue; res = virtual_subscription_db_add(vs, s); if (res != 0) break; } return res; } static int vs_db_update(rl_subscription_t *s) { /* There is nothing to be updated now - may be dialogs for * external subscriptions and their expirations * in the future ! * * Status is newly generated on the other side (in PA)! */ return 0; } static int vs_db_remove(rl_subscription_t *s) { db_key_t keys[] = { "id" }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[1]; int i, cnt; int res = 0; virtual_subscription_t *vs; cnt = ptr_vector_size(&s->vs); for (i = 0; i < cnt; i++) { vs = ptr_vector_get(&s->vs, i); if (!vs) continue; string_val_ex(k_vals[0], vs->dbid, strlen(vs->dbid)); /* remove virtual subscription */ if (rls_dbf.use_table(rls_db, vs_table) < 0) { LOG(L_ERR, "db_remove_presence_tuple: Error in use_table\n"); res = -1; } if (rls_dbf.delete(rls_db, keys, ops, k_vals, 1) < 0) { LOG(L_ERR, "db_remove_presence_tuple: Can't delete record\n"); res = -1; } /* remove display names */ if (rls_dbf.use_table(rls_db, vs_names_table) < 0) { LOG(L_ERR, "db_remove_presence_tuple: Error in use_table\n"); res = -1; } if (rls_dbf.delete(rls_db, keys, ops, k_vals, 1) < 0) { LOG(L_ERR, "db_remove_presence_tuple: Can't delete record\n"); res = -1; } } return res; } /* ------------- rls subscriptions ------------- */ int rls_db_add(rl_subscription_t *s) { db_key_t cols[20]; db_val_t vals[20]; str_t dialog = STR_NULL; str_t str_xcap_params = STR_NULL; int n = -1; int res = 0; time_t t; if (!use_db) return 0; /* store only external subscriptions */ if (s->type != rls_external_subscription) return 0; DEBUG_LOG("storing into database\n"); if (rls_dbf.use_table(rls_db, rls_table) < 0) { LOG(L_ERR, "rls_db_add: Error in use_table\n"); return -1; } cols[++n] = "doc_version"; int_val(vals[n], s->doc_version); cols[++n] = "status"; int_val(vals[n], s->u.external.status); t = time(NULL); t += rls_subscription_expires_in(s); cols[++n] = "expires"; time_val(vals[n], t); if (dlg_func.dlg2str(s->u.external.dialog, &dialog) != 0) { LOG(L_ERR, "Error while serializing dialog\n"); return -1; } cols[++n] = "dialog"; blob_val(vals[n], dialog); cols[++n] = "contact"; string_val(vals[n], s->u.external.contact); cols[++n] = "uri"; string_val(vals[n], s->u.external.record_id); cols[++n] = "package"; string_val(vals[n], s->u.external.package); cols[++n] = "w_uri"; string_val(vals[n], s->u.external.subscriber); if (xcap_params2str(&str_xcap_params, &s->xcap_params) != 0) { LOG(L_ERR, "Error while serializing xcap params\n"); str_free_content(&dialog); return -1; } cols[++n] = "xcap_params"; blob_val(vals[n], str_xcap_params); cols[++n] = "id"; string_val_ex(vals[n], s->dbid, strlen(s->dbid)); /* insert new record into database */ if (rls_dbf.insert(rls_db, cols, vals, n + 1) < 0) { LOG(L_ERR, "rls_db_add: Error while inserting subscription\n"); res = -1; } str_free_content(&dialog); str_free_content(&str_xcap_params); if (res == 0) res = vs_db_add(s); return res; } int rls_db_remove(rl_subscription_t *s) { db_key_t keys[] = { "id" }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = { s: s->dbid, len: strlen(s->dbid) } } } }; if (!use_db) return 0; /* only external subscriptions are stored */ if (s->type != rls_external_subscription) return 0; if (rls_dbf.use_table(rls_db, rls_table) < 0) { LOG(L_ERR, "db_remove_presence_tuple: Error in use_table\n"); return -1; } if (rls_dbf.delete(rls_db, keys, ops, k_vals, 1) < 0) { LOG(L_ERR, "db_remove_presence_tuple: Can't delete record\n"); return -1; } return vs_db_remove(s); } int rls_db_update(rl_subscription_t *s) { db_key_t cols[20]; db_val_t vals[20]; str_t dialog = STR_NULL; str_t str_xcap_params = STR_NULL; int n = -1; int res = 0; time_t t; db_key_t keys[] = { "id" }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = { s: s->dbid, len: strlen(s->dbid) } } } }; if (!use_db) return 0; /* only external subscriptions are stored */ if (s->type != rls_external_subscription) return 0; if (rls_dbf.use_table(rls_db, rls_table) < 0) { LOG(L_ERR, "rls_db_add: Error in use_table\n"); return -1; } cols[++n] = "doc_version"; int_val(vals[n], s->doc_version); cols[++n] = "status"; int_val(vals[n], s->u.external.status); t = time(NULL); t += rls_subscription_expires_in(s); cols[++n] = "expires"; time_val(vals[n], t); if (dlg_func.dlg2str(s->u.external.dialog, &dialog) != 0) { LOG(L_ERR, "Error while serializing dialog\n"); return -1; } cols[++n] = "dialog"; blob_val(vals[n], dialog); cols[++n] = "contact"; string_val(vals[n], s->u.external.contact); cols[++n] = "uri"; string_val(vals[n], s->u.external.record_id); cols[++n] = "package"; string_val(vals[n], s->u.external.package); cols[++n] = "w_uri"; string_val(vals[n], s->u.external.subscriber); if (xcap_params2str(&str_xcap_params, &s->xcap_params) != 0) { LOG(L_ERR, "Error while serializing xcap params\n"); str_free_content(&dialog); return -1; } cols[++n] = "xcap_params"; blob_val(vals[n], str_xcap_params); if (rls_dbf.update(rls_db, keys, ops, k_vals, cols, vals, 1, n + 1) < 0) { LOG(L_ERR, "rls_db_add: Error while inserting subscription\n"); res = -1; } str_free_content(&dialog); str_free_content(&str_xcap_params); return vs_db_update(s); } /* ------------- Loading ------------- */ #define get_str_val(rvi,dst) do{if(!rvi.nul){dst.s=(char*)rvi.val.string_val;dst.len=strlen(dst.s);}}while(0) #define get_blob_val(rvi,dst) do{if(!rvi.nul){dst=rvi.val.blob_val;}else dst.len=0;}while(0) #define get_time_val(rvi,dst) do{if(!rvi.nul){dst=rvi.val.time_val;}}while(0) #define get_int_val(rvi,dst) do{if(!rvi.nul){dst=rvi.val.int_val;}else dst=0;}while(0) static dlg_t *dlg2str(str_t *s) { dlg_t *dlg = (dlg_t*)mem_alloc(sizeof(*dlg)); if (!dlg) LOG(L_ERR, "Can't allocate dialog\n"); else { if (dlg_func.str2dlg(s, dlg) != 0) { LOG(L_ERR, "Error while deserializing dialog\n"); mem_free(dlg); dlg = NULL; } } return dlg; } int db_load_vs_names(db_con_t *rls_db, virtual_subscription_t *vs) { int i, r = 0; db_res_t *res = NULL; db_key_t result_cols[] = { "name", "lang" }; db_key_t keys[] = { "id" }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = { s: vs->dbid, len: strlen(vs->dbid) } } } }; if (rls_dbf.use_table(rls_db, vs_names_table) < 0) { LOG(L_ERR, "vs_load_vs_names: Error in use_table\n"); return -1; } if (rls_dbf.query (rls_db, keys,ops, k_vals, result_cols, 1, sizeof(result_cols) / sizeof(db_key_t), 0, &res) < 0) { LOG(L_ERR, "db_load_vs_names: Error while querying vs names\n"); r = -1; res = NULL; } if (res) { for (i = 0; i < res->n; i++) { db_row_t *row = &res->rows[i]; db_val_t *row_vals = ROW_VALUES(row); str name = STR_NULL; str lang = STR_NULL; get_str_val(row_vals[0], name); get_str_val(row_vals[1], lang); DEBUG_LOG(" adding name %.*s\n", FMT_STR(name)); vs_add_display_name(vs, name.s, lang.s); } rls_dbf.free_result(rls_db, res); } return r; } int db_load_vs(db_con_t *rls_db, rl_subscription_t *s) { int i, r = 0; db_res_t *res = NULL; virtual_subscription_t *vs; db_key_t result_cols[] = { "id", "uri" }; db_key_t keys[] = { "rls_id" }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = { s: s->dbid, len: strlen(s->dbid) } } } }; if (rls_dbf.use_table(rls_db, vs_table) < 0) { LOG(L_ERR, "vs_load_vs: Error in use_table\n"); return -1; } if (rls_dbf.query (rls_db, keys,ops, k_vals, result_cols, 1, sizeof(result_cols) / sizeof(db_key_t), 0, &res) < 0) { LOG(L_ERR, "db_load_vs: Error while querying presentity\n"); r = -1; res = NULL; } if (res) { for (i = 0; i < res->n; i++) { db_row_t *row = &res->rows[i]; db_val_t *row_vals = ROW_VALUES(row); str id = STR_NULL; str uri = STR_NULL; get_str_val(row_vals[0], id); get_str_val(row_vals[1], uri); r = vs_create(&uri, &vs, NULL, s, max_list_nesting_level) | r; if ((r != 0) || (!vs)) { r = -1; break; } strcpy(vs->dbid, id.s); DEBUG_LOG(" created VS to %.*s\n", FMT_STR(uri)); ptr_vector_add(&s->vs, vs); db_load_vs_names(rls_db, vs); } rls_dbf.free_result(rls_db, res); } return r; } int db_load_rls() { /* this function may be called from mod_init, thus can not work * with DB connection opened from child_init */ db_con_t* rls_db = NULL; /* own database connection handle */ int i, r = 0; rl_subscription_t *s; db_res_t *res = NULL; db_key_t result_cols[] = { "id", "doc_version", "dialog", "expires", "status", "contact", "uri", "package", "w_uri", "xcap_params" }; if (!use_db) return 0; DEBUG_LOG("loading rls from db\n"); /* open own database connection */ if (rls_dbf.init) rls_db = rls_dbf.init(db_url); if (!rls_db) { LOG(L_ERR, "db_load_rls: Error while connecting database\n"); return -1; } if (rls_dbf.use_table(rls_db, rls_table) < 0) { LOG(L_ERR, "rls_load_rls: Error in use_table\n"); return -1; } if (rls_dbf.query (rls_db, NULL, NULL, NULL, result_cols, 0, sizeof(result_cols) / sizeof(db_key_t), 0, &res) < 0) { LOG(L_ERR, "db_load_rls: Error while querying presentity\n"); r = -1; res = NULL; } if (res) { for (i = 0; i < res->n; i++) { db_row_t *row = &res->rows[i]; db_val_t *row_vals = ROW_VALUES(row); str id = STR_NULL; str contact = STR_NULL; str dialog = STR_NULL; str xcap_params = STR_NULL; str uri = STR_NULL; str package = STR_NULL; str w_uri = STR_NULL; subscription_status_t status; time_t expires = 0; int expires_after; dlg_t *dlg = NULL; s = rls_alloc_subscription(rls_external_subscription); if (!s) { r = -1; break; } get_str_val(row_vals[0], id); strcpy(s->dbid, id.s); get_int_val(row_vals[1], s->doc_version); get_blob_val(row_vals[2], dialog); get_time_val(row_vals[3], expires); get_int_val(row_vals[4], status); get_str_val(row_vals[5], contact); get_str_val(row_vals[6], uri); get_str_val(row_vals[7], package); get_str_val(row_vals[8], w_uri); get_blob_val(row_vals[9], xcap_params); if (expires != 0) expires_after = expires - time(NULL); else expires_after = 0; dlg = dlg2str(&dialog); sm_init_subscription_nolock_ex(rls_manager, &s->u.external, dlg, status, &contact, &uri, &package, &w_uri, expires_after, s); DEBUG_LOG(" created RLS to %.*s from %.*s\n", FMT_STR(uri), FMT_STR(w_uri)); if (str2xcap_params(&s->xcap_params, &xcap_params) < 0) { ERR("can't set xcap params\n"); rls_free(s); s = 0; r = -1; break; } /* load virtual subscriptions */ db_load_vs(rls_db, s); } rls_dbf.free_result(rls_db, res); } /* close db connection */ if (rls_dbf.close) rls_dbf.close(rls_db); DEBUG_LOG("rls loaded\n"); return r; } kamailio-4.0.4/obsolete/rls/rlmi_doc.c0000644000000000000000000001303212223032460016344 0ustar rootroot#include "rlmi_doc.h" #include "result_codes.h" #include #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../str.h" static void add_virtual_subscriptions_to_rlmi(dstring_t *doc, rl_subscription_t *s, const char *part_id) { int i, j, ncnt, cnt; virtual_subscription_t *vs; vs_display_name_t dn; char tmp[32]; /* add all list elements */ cnt = ptr_vector_size(&s->vs); for (i = 0; i < cnt; i++) { vs = ptr_vector_get(&s->vs, i); if (!vs) continue; dstr_append_zt(doc, "\turi); dstr_append_zt(doc, "\">\r\n"); /* add display names */ ncnt = vector_size(&vs->display_names); for (j = 0; j < ncnt; j++) { if (vector_get(&vs->display_names, j, &dn) != 0) continue; if (dn.lang.len > 0) { dstr_append_zt(doc, "\t\t"); } else dstr_append_zt(doc, "\t\t"); dstr_append_str(doc, &dn.name); dstr_append_zt(doc, "\r\n"); } sprintf(tmp, "vs%di%d", i, 1); dstr_append_zt(doc, "\t\tstatus) { case subscription_active: dstr_append_zt(doc, "active\""); break; case subscription_pending: dstr_append_zt(doc, "pending\""); break; case subscription_terminated_pending: case subscription_terminated: dstr_append_zt(doc, "terminated\" reason=\"closed\""); break; case subscription_terminated_pending_to: case subscription_terminated_to: dstr_append_zt(doc, "terminated\" reason=\"timeout\""); break; case subscription_uninitialized: dstr_append_zt(doc, "pending\""); /* this is an error ! */ LOG(L_ERR, "generating RLMI for an unitialized virtual subscription!\n"); break; } if (vs->state_document.len > 0) { sprintf(tmp, "%d", i); dstr_append_zt(doc, " cid=\""); dstr_append_zt(doc, part_id); dstr_append_zt(doc, tmp); dstr_append_zt(doc, "\"/>\r\n"); } else dstr_append_zt(doc, "/>\r\n"); dstr_append_zt(doc, "\t\r\n"); } } static void add_virtual_subscriptions_documents(dstring_t *doc, rl_subscription_t *s, const char *boundary_str, const char *part_id) { int i, cnt; virtual_subscription_t *vs; char tmp[32]; /* add all list elements */ cnt = ptr_vector_size(&s->vs); for (i = 0; i < cnt; i++) { vs = ptr_vector_get(&s->vs, i); if (!vs) continue; if (vs->state_document.len < 1) { vs = vs->next; continue; } if (vs->content_type.len < 1) { LOG(L_ERR, "can't send resource status document for unknown type\n"); vs = vs->next; continue; } dstr_append(doc, "--", 2); dstr_append_zt(doc, boundary_str); dstr_append(doc, "\r\n", 2); sprintf(tmp, "%d", i); dstr_append_zt(doc, "Content-Transfer-Encoding: binary\r\nContent-ID: "); dstr_append_zt(doc, part_id); dstr_append_zt(doc, tmp); dstr_append_zt(doc, "\r\nContent-Type: "); dstr_append_str(doc, &vs->content_type); dstr_append_zt(doc, "\r\n\r\n"); dstr_append_str(doc, &vs->state_document); dstr_append(doc, "\r\n", 2); dstr_append(doc, "\r\n", 2); } } int create_rlmi_document(str *dst, str *content_type_dst, rl_subscription_t *s, int full_info) { dstring_t doc, cont; char tmp[32]; char start_str[64]; char boundary_str[64]; char part_id[64]; if ((!s) || (!dst) || (!content_type_dst)) return RES_INTERNAL_ERR; sprintf(start_str, "qwW%dpPdxX%d", rand(), rand()); sprintf(boundary_str, "RewXdpxR%dxA%d", rand(), rand()); sprintf(part_id, "id%di%dx", rand(), rand()); /* --- build NOTIFY body --- */ dstr_init(&doc, 256); dstr_append(&doc, "--", 2); dstr_append_zt(&doc, boundary_str); dstr_append(&doc, "\r\n", 2); dstr_append_zt(&doc, "Content-Transfer-Encoding: binary\r\n" "Content-ID: "); dstr_append_zt(&doc, start_str); dstr_append_zt(&doc, "\r\n"); dstr_append_zt(&doc, "Content-Type: application/rlmi+xml;charset=\"UTF-8\"\r\n"); dstr_append(&doc, "\r\n", 2); /* -- RLMI document -- */ dstr_append_zt(&doc, "\r\n" "doc_version); dstr_append_zt(&doc, tmp); dstr_append_zt(&doc, "\" fullState=\"true\">\r\n"); /* FIXME: as soon as will be finished partial notification document * if (full_info) dstr_append_zt(&doc, "\" fullState=\"true\">\r\n"); else dstr_append_zt(&doc, "\" fullState=\"false\">\r\n"); */ /* add all virtual subscriptions to the RLMI document */ add_virtual_subscriptions_to_rlmi(&doc, s, part_id); dstr_append_zt(&doc, "\r\n\r\n"); /* add all virtual subscriptions status documents */ add_virtual_subscriptions_documents(&doc, s, boundary_str, part_id); dstr_append(&doc, "--", 2); dstr_append_zt(&doc, boundary_str); dstr_append(&doc, "--\r\n", 4); dstr_append(&doc, "\r\n", 2); /* --- build content type --- */ dstr_init(&cont, 256); dstr_append_zt(&cont, "multipart/related;type=\"application/rlmi+xml\";" "start=\""); dstr_append_zt(&cont, start_str); dstr_append_zt(&cont, "\";boundary=\""); dstr_append_zt(&cont, boundary_str); dstr_append_zt(&cont, "\";"); /* --- store output strings --- */ dstr_get_str(&doc, dst); dstr_destroy(&doc); dstr_get_str(&cont, content_type_dst); dstr_destroy(&cont); /* increment version for next NOTIFY document */ s->doc_version++; return RES_OK; } kamailio-4.0.4/obsolete/rls/rls_data.c0000644000000000000000000001173012223032460016350 0ustar rootroot#include "rls_data.h" #include "rls_auth.h" #include #include rls_data_t *rls = NULL; static gen_lock_t *rls_mutex = NULL; static int send_notify_cb(struct _subscription_data_t *s) { if (s) rls_generate_notify((rl_subscription_t *)s->usr_data, 1); return 0; } static int terminate_subscription_cb(struct _subscription_data_t *s) { if (s) { TRACE("destroying RLS subscription %p using timer\n", s); rls_remove((rl_subscription_t*)s->usr_data); } return 0; } static void do_external_notifications() { subscription_data_t *s = NULL; rl_subscription_t *rs; int notified = 0; if (rls_manager) s = rls_manager->first; /* this goes through all EXTERNAL subscriptions only !!! * but internal subscriptions are notified immediately, thus this is what * we want */ /* there can be some logic to handle at most xxx subscriptions ... */ while (s) { rs = (rl_subscription_t*)(s->usr_data); if (rs->changed) { rls_generate_notify(rs, 0); rls->changed_subscriptions -= rs->changed; if (rls->changed_subscriptions <= 0) break; if (++notified >= max_notifications_at_once) { break; } } s = s->next; } if (rls->changed_subscriptions < 0) { ERR("BUG: changed_subscriptions = %d\n", rls->changed_subscriptions); rls->changed_subscriptions = 0; } } static void rls_timer_cb(unsigned int ticks, void *param) { virtual_subscription_t *vs; int cnt = 0; time_t start, stop; mq_message_t *msg; client_notify_info_t *info; PROF_START(rls_timer_cb) start = time(NULL); rls_lock(); /* process all messages for virtual subscriptions */ while (!is_msg_queue_empty(&rls->notify_mq)) { msg = pop_message(&rls->notify_mq); if (!msg) continue; info = (client_notify_info_t *)msg->data; if (info) { vs = (virtual_subscription_t *)get_subscriber_data(info->subscription); if (!vs) { ERR("BUG: empty QSA subscription parameter (vs)\n"); } process_rls_notification(vs, info); cnt++; } free_message(msg); } if (rls->changed_subscriptions > 0) { do_external_notifications(); /* rls->changed_subscriptions is reset (or decremented) from * do_external_notifications() */ } rls_unlock(); stop = time(NULL); if (stop - start > 1) WARN("rls_timer_cb took %d secs\n", (int) (stop - start)); PROF_STOP(rls_timer_cb) } void rls_lock() { /* FIXME: solve locking more efficiently - locking whole RLS in * all cases of manipulating internal structures is not good * solution */ lock_get(rls_mutex); } void rls_unlock() { lock_release(rls_mutex); } int rls_init() { rls = (rls_data_t*)mem_alloc(sizeof(rls_data_t)); if (!rls) { LOG(L_ERR, "rls_init(): memory allocation error\n"); return -1; } /* rls->first = NULL; rls->last = NULL;*/ rls->changed_subscriptions = 0; if (msg_queue_init(&rls->notify_mq) != 0) { ERR("can't initialize message queue for RLS notifications!\n"); return -1; } rls_mutex = lock_alloc(); if (!rls_mutex) { LOG(L_ERR, "rls_init(): Can't initialize mutex\n"); return -1; } lock_init(rls_mutex); rls_manager = sm_create(send_notify_cb, terminate_subscription_cb, rls_authorize_subscription, rls_mutex, rls_min_expiration, /* min expiration time in seconds */ rls_max_expiration, /* max expiration time in seconds */ rls_default_expiration, /* default expiration time in seconds */ rls_expiration_timer_period); /* register timer for handling notify messages */ if (register_timer(rls_timer_cb, NULL, rls_timer_interval) < 0) { LOG(L_ERR, "vs_init(): can't register timer\n"); return -1; } return 0; } int rls_destroy() { DEBUG_LOG("rls_destroy() called\n"); /* FIXME: destroy the whole rl_subscription list */ /* sm_destroy(rls_manager); */ if (rls_mutex) { lock_destroy(rls_mutex); lock_dealloc(rls_mutex); } if (rls) { mem_free(rls); rls = NULL; } return 0; } /* static int process_rls_messages() { int cnt = 0; client_notify_info_t *info; mq_message_t *msg; while (!is_msg_queue_empty(&rls->notify_mq)) { msg = pop_message(&rls->notify_mq); if (!msg) continue; info = (client_notify_info_t *)msg->data; if (info) { process_notify_info(vs, info); cnt++; } free_message(msg); } return cnt; }*/ void destroy_notifications(qsa_subscription_t *s) { /* removes all notifications for given qsa_subscription from message queue * and discards them */ int cnt = 0; int other_cnt = 0; mq_message_t *msg; msg_queue_t tmp; client_notify_info_t *info; msg_queue_init(&tmp); /* process all messages for virtual subscriptions */ while (!is_msg_queue_empty(&rls->notify_mq)) { msg = pop_message(&rls->notify_mq); if (!msg) continue; info = (client_notify_info_t *)msg->data; if (info) { if (s == info->subscription) { cnt++; free_message(msg); } else { push_message(&tmp, msg); other_cnt++; } } else free_message(msg); /* broken message */ } /* move messages back to main queue */ while (!is_msg_queue_empty(&tmp)) { msg = pop_message(&tmp); push_message(&rls->notify_mq, msg); } } kamailio-4.0.4/obsolete/rls/rlmi_doc.h0000644000000000000000000000044712223032460016357 0ustar rootroot#ifndef __RLMI_DOCUMENT_H #define __RLMI_DOCUMENT_H #include "rl_subscription.h" /** Creates new RLMI document from subscription information. It will * be allocated in package memory. */ int create_rlmi_document(str *dst, str *content_type_dst, rl_subscription_t *s, int full_info); #endif kamailio-4.0.4/obsolete/rls/rls_auth.h0000644000000000000000000000034112223032460016401 0ustar rootroot#ifndef __RLS_AUTHORIZE #define __RLS_AUTHORIZE #include "rl_subscription.h" #include "subscription_manager.h" #include "rls_mod.h" authorization_result_t rls_authorize_subscription(struct _subscription_data_t *s); #endif kamailio-4.0.4/obsolete/rls/rls_handler.h0000644000000000000000000000056312223032460017063 0ustar rootroot#ifndef __RLS_HANDLER_H #define __RLS_HANDLER_H #include "../../parser/msg_parser.h" int handle_rls_subscription(struct sip_msg* _m, /*const char *xcap_server,*/ char *send_bad_resp); int query_rls_services(struct sip_msg* _m, char *, char *); int query_resource_list(struct sip_msg* _m, char *, char *); int have_flat_list(struct sip_msg* _m, char *, char *); #endif kamailio-4.0.4/obsolete/rls/rls_handler.c0000644000000000000000000003164212223032460017060 0ustar rootroot#include "rls_mod.h" #include "rls_handler.h" #include "rl_subscription.h" #include #include #include #include "result_codes.h" #include "../../str.h" #include "../../id.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_event.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_content.h" #include "../../data_lump_rpl.h" #include "../../usr_avp.h" #include /* static variables for sharing data loaded from XCAP */ typedef struct { xcap_query_params_t xcap_params; flat_list_t *flat_list; /* may be NULL for empty lists!!! */ int have_flat_list; /* added due to possibility of NULL flat list */ } rls_xcap_query_t; static rls_xcap_query_t query = { xcap_params: { xcap_root: {s: NULL, len: 0} }, flat_list: NULL, have_flat_list: 0 }; /* clears last data stored from one of query_... functions */ static void clear_last_query() { if (query.have_flat_list) { if (query.flat_list) free_flat_list(query.flat_list); query.flat_list = NULL; query.have_flat_list = 0; /* str_clear(&query.params.xcap_root); */ memset(&query.xcap_params, 0, sizeof(query.xcap_params)); } } static int send_reply(struct sip_msg* _m, int code, char *msg) { if (tmb.t_reply(_m, code, msg) == -1) { LOG(L_ERR, "send_reply(): Error while sending %d %s\n", code, msg); return -1; } else return 0; } static int parse_rls_headers(struct sip_msg* _m) { struct hdr_field *acc; if ( (parse_headers(_m, HDR_EOH_T, 0) == -1) || /* we need all Accept headers... */ (_m->from==0)||(_m->to==0)||(_m->event==0) ) { LOG(L_ERR, "parse_rls_headers(): Error while parsing headers\n"); return -1; } /* there is no parse_to_header function (only parse_to) if (parse_to_header(_m) < 0) { LOG(L_ERR, "parse_rls_headers(): To malformed or missing\n"); return -1; }*/ if (parse_from_header(_m) < 0) { LOG(L_ERR, "parse_rls_headers(): From malformed or missing\n"); return -1; } if (_m->expires) { if (parse_expires(_m->expires) < 0) { LOG(L_ERR, "parse_rls_headers(): Error parsing Expires header\n"); return -1; } } if (_m->event) { if (parse_event(_m->event) < 0) { LOG(L_ERR, "parse_rls_headers(): Error while parsing Event header field\n"); return -1; } } acc = _m->accept; while (acc) { /* parse all accept headers */ if (acc->type == HDR_ACCEPT_T) { /* DEBUG_LOG("parsing accept header: %.*s\n", FMT_STR(acc->body)); */ if (parse_accept_body(acc) < 0) { LOG(L_ERR, "parse_rls_headers(): Error while parsing Accept header field\n"); return -1; } } acc = acc->next; } return 0; } static int get_event(struct sip_msg *_m) { int et = 0; event_t *event = NULL; if (_m->event) { event = (event_t*)(_m->event->parsed); et = event->parsed; } else { LOG(L_ERR, "no event package for RLS - using EVENT_PRESENCE\n"); et = EVENT_PRESENCE; } return et; } /* returns 1 if package supported by RLS */ static int verify_event_package(struct sip_msg *m) { int et = get_event(m); switch (et) { case EVENT_PRESENCE: return 0; default: return -1; } return -1; } static int add_response_header(struct sip_msg *_m, char *hdr) { if (!add_lump_rpl(_m, hdr, strlen(hdr), LUMP_RPL_HDR)) return -1; return 0; } static int add_response_min_expires_header(struct sip_msg *_m) { char tmp[64]; sprintf(tmp, "Min-Expires: %d\r\n", rls_min_expiration); if (!add_lump_rpl(_m, tmp, strlen(tmp), LUMP_RPL_HDR)) return -1; return 0; } struct accepted_types { char *mime_txt; int mime; int needed; int found; }; /* marks mime_type as found */ void mark_accepted_type(struct accepted_types *types, int mime_type) { int i; for (i = 0; types[i].mime_txt; i++) if (mime_type == types[i].mime) types[i].found = 1; } static int check_message(struct sip_msg *_m, int send_err) { int *accepts_mimes = NULL; int i; struct hdr_field *acc; struct accepted_types accepts[] = { { "multipart/related", MIMETYPE(MULTIPART,RELATED), 1, 0 }, { "application/rlmi+xml", MIMETYPE(APPLICATION,RLMIXML), 1, 0 }, { "application/pidf+xml", MIMETYPE(APPLICATION, PIDFXML), 1, 0 }, { NULL, 0, 0, 0 } }; if (verify_event_package(_m) != 0) { /* allow only selected packages independently on rls document */ if (send_err) { ERR("unsupported events\n"); add_response_header(_m, "Allow-Events: presence\r\n"); send_reply(_m, 489, "Bad Event"); } return -1; } /* verify Accept: multipart/related, application/rlmi+xml, application/pidf+xml */ acc = _m->accept; while (acc) { /* go through all Accept headers */ if (acc->type == HDR_ACCEPT_T) { /* it MUST be parsed from parse_hdr !!! */ accepts_mimes = acc->parsed; /* go through all in accept mimes and test our */ for (i = 0; accepts_mimes[i]; i++) mark_accepted_type(accepts, accepts_mimes[i]); } acc = acc->next; } for (i = 0; accepts[i].mime_txt; i++) if ((!accepts[i].found) && (accepts[i].needed)) { if (send_err) { ERR("required type %s not in Accept headers\n", accepts[i].mime_txt); send_reply(_m, 400, "Bad Request"); } return -1; } /* verify Supported: eventlist */ return 0; } static int handle_new_subscription(struct sip_msg *m, rls_xcap_query_t *query, int send_error_responses) { rl_subscription_t *s; int res = 0; xcap_query_params_t *params = NULL; if (query) params = &query->xcap_params; rls_lock(); DBG("handle_new_subscription(rls)\n"); /* create a new subscription structure */ res = rls_create_subscription(m, &s, query->flat_list, params); if (res != RES_OK) { rls_unlock(); switch (res) { case RES_PARSE_HEADERS_ERR: if (!send_error_responses) return -1; /* "unprocessed" */ add_response_header(m, "Reason-Phrase: Bad or missing headers\r\n"); send_reply(m, 400, "Bad Request"); break; case RES_SUBSCRIPTION_REJECTED: /* if (!send_error_responses) return -1; */ /* FIXME: authorization is done before XCAP query, so though it is NOT * resource-list subscription it may be marked as rejected !!! */ DEBUG_LOG("subscription rejected\n"); add_response_header(m, "Reason-Phrase: Subscription rejected\r\n"); send_reply(m, 403, "Forbidden"); break; case RES_EXPIRATION_INTERVAL_TOO_SHORT: if (!send_error_responses) return -1; /* "unprocessed" */ add_response_min_expires_header(m); send_reply(m, 423, "Interval too small"); break; case RES_BAD_EVENT_PACKAGE_ERR: if (!send_error_responses) return -1; /* "unprocessed" */ /* TODO: add_response_header(_m, "Allow-Events: \r\n"); */ send_reply(m, 489, "Bad Event"); break; case RES_BAD_GATEWAY_ERR: if (!send_error_responses) return -1; /* "unprocessed" */ send_reply(m, 502, "Bad Gateway"); break; case RES_XCAP_QUERY_ERR: if (!send_error_responses) return -1; /* "unprocessed" */ add_response_header(m, "Reason-Phrase: XCAP query error\r\n"); send_reply(m, 502, "Bad Gateway"); /*send_reply(m, 500, "Internal error"); */ break; case RES_XCAP_PARSE_ERR: if (!send_error_responses) return -1; /* "unprocessed" */ add_response_header(m, "Reason-Phrase: XCAP result parsing error\r\n"); send_reply(m, 500, "Internal error"); break; default: if (!send_error_responses) return -1; /* "unprocessed" */ send_reply(m, 500, "Internal error"); } return 0; /* processed */ } /* send a response */ rls_prepare_subscription_response(s, m); send_reply(m, 200, "OK"); DEBUG_LOG("RLS subscription successfully handled\n"); /* create NOTIFY message * FIXME - this may be a nonsense for polling, because the notifier might not * catch up sent notification */ rls_generate_notify(s, 1); /* free subscription if only polling */ if (sm_subscription_terminated(&s->u.external) == 0) { rls_remove(s); } rls_unlock(); return 0; } static int handle_renew_subscription(struct sip_msg *m, int send_error_responses) { str *from_tag; str *to_tag; str *call_id; rl_subscription_t *s = NULL; int res; to_tag = &((struct to_body*)m->to->parsed)->tag_value; from_tag = &((struct to_body*)m->from->parsed)->tag_value; call_id = NULL; if (m->callid) call_id = &m->callid->body; DBG("handle_renew_subscription(rls)\n"); rls_lock(); res = rls_find_subscription(from_tag, to_tag, call_id, &s); if ((res != RES_OK) || (!s)) { rls_unlock(); if (send_error_responses) { WARN("can't refresh unknown subscription\n"); send_reply(m, 481, "Call/Transaction Does Not Exist"); } return -1; /* "unprocessed" */ } res = rls_refresh_subscription(m, s); if (res != RES_OK) { rls_unlock(); switch (res) { case RES_PARSE_HEADERS_ERR: if (!send_error_responses) return -1; /* "unprocessed" */ add_response_header(m, "Reason-Phrase: Bad or missing headers\r\n"); send_reply(m, 400, "Bad Request"); break; case RES_EXPIRATION_INTERVAL_TOO_SHORT: if (!send_error_responses) return -1; /* "unprocessed" */ add_response_min_expires_header(m); send_reply(m, 423, "Interval too small"); break; case RES_SUBSCRIPTION_TERMINATED: send_reply(m, 481, "Subscription terminated"); break; default: if (!send_error_responses) return -1; /* "unprocessed" */ send_reply(m, 500, "Internal error"); } return 0; /* processed */ } /* send a response */ rls_prepare_subscription_response(s, m); send_reply(m, 200, "OK"); /* create NOTIFY message */ rls_generate_notify(s, 1); /* free subscription if only polling */ if (sm_subscription_terminated(&s->u.external) == 0) { rls_remove(s); } rls_unlock(); return 0; } int handle_rls_subscription(struct sip_msg* _m, char *send_bad_resp) { int res; long send_err = 1; PROF_START(rls_handle_subscription) send_err = (long)send_bad_resp; res = parse_rls_headers(_m); if (res == -1) { LOG(L_INFO, "handle_rls_subscription(): problems parsing headers.\n"); if (send_err) { add_response_header(_m, "Reason-Phrase: Bad or missing headers\r\n"); send_reply(_m, 400, "Bad Request"); } clear_last_query(); PROF_STOP(rls_handle_subscription) return -1; } if (check_message(_m, send_err) != 0) { DBG("check message failed\n"); clear_last_query(); PROF_STOP(rls_handle_subscription) return -1; } if (has_to_tag(_m)) { /* handle SUBSCRIBE for an existing subscription */ res = handle_renew_subscription(_m, send_err); } else { /* handle SUBSCRIBE for a new subscription */ res = handle_new_subscription(_m, &query, send_err); } clear_last_query(); PROF_STOP(rls_handle_subscription) if (res == 0) return 1; else return -1; } /*****************************************************************/ /* XCAP query functions accessible from the CFG script */ /* Get resource-list URI from SUBSCRIBE request */ static int get_dst_uri(struct sip_msg* _m, str* dst_uri) { /* FIXME: get raw request URI? or from TO? * FIXME: skip uri parameters and everything else, leave only * sip:xxx@yyy ???!!! */ str uri; if (_m->new_uri.s) { uri.s = _m->new_uri.s; uri.len = _m->new_uri.len; } else { uri.s = _m->first_line.u.request.uri.s; uri.len = _m->first_line.u.request.uri.len; } if (dst_uri) *dst_uri = uri; return RES_OK; } int query_rls_services(struct sip_msg* _m, char *a, char *b) { str uri; static str package = STR_STATIC_INIT("presence"); /* TODO: take package from Event header or allow packages * given as parameter? */ PROF_START(rls_query_rls_sevices) clear_last_query(); if (fill_xcap_params) fill_xcap_params(_m, &query.xcap_params); if (get_dst_uri(_m, &uri) < 0) { ERR("can't get destination URI\n"); clear_last_query(); PROF_STOP(rls_query_rls_sevices) return -1; } if (xcap_query_rls_services(&query.xcap_params, &uri, &package, &query.flat_list) < 0) { ERR("XCAP query problems for uri %.*s\n", FMT_STR(uri)); clear_last_query(); PROF_STOP(rls_query_rls_sevices) return -1; } query.have_flat_list = 1; PROF_STOP(rls_query_rls_sevices) return 1; } int query_resource_list(struct sip_msg* _m, char *list_name, char *b) { int res; str_t uid; PROF_START(rls_query_resource_list) clear_last_query(); if (fill_xcap_params) fill_xcap_params(_m, &query.xcap_params); if (get_from_uid(&uid, _m) < 0) { ERR("can't get From uid\n"); clear_last_query(); PROF_STOP(rls_query_resource_list) return -1; } res = get_resource_list_from_full_doc(&uid, NULL, /* TODO: filename */ &query.xcap_params, list_name, &query.flat_list); /* TODO: add function for real XCAP server */ if (res < 0) { ERR("XCAP query problems\n"); clear_last_query(); PROF_STOP(rls_query_resource_list) return -1; } query.have_flat_list = 1; PROF_STOP(rls_query_resource_list) return 1; } int have_flat_list(struct sip_msg* _m, char *a, char *b) { PROF_START(rls_have_flat_list) if (query.have_flat_list) { PROF_STOP(rls_have_flat_list) return 1; } else { PROF_STOP(rls_have_flat_list) return -1; } } kamailio-4.0.4/obsolete/rls/ChangeLog0000644000000000000000000000666012223032460016173 0ustar rootroot2006-12-14 * removed CDATA wrapping of XML documents in XMLRPC output 2006-12-01 * allowed standalone documentation generation - bibliograpy appended in this case 2006-07-12 * improved dialog refreshing 2006-05-26 * removed unused xcap_root parameter 2006-05-25 * added Max-Forwards header into NOTIFY requests 2006-05-15 * handling responses on NOTIFY * added parameter ignore_408_on_notify 2006-04-21 * added parameter expiration_timer_period (timer interval for checking expirations) 2006-04-20 * rls_trace function puts XML datafile in CDATA section * rls_trace doesn't put empty strings as separators (more readable using new serctl) * rls_trace has optional number argument - if nonzero it displays stored status documents * added parameter max_list_nesting_level for control over nested list processing 2006-04-18 * added profiling 2006-04-13 * modified optimizations of sent notifications, added parameter max_notifications_at_once 2006-04-11 * changed QSA (reduced memory allocations) 2006-04-10 * changed QSA - corrected content-type propagation 2006-04-06 * xcap queries moved into xcap module 2006-04-04 * sending empty string in NOTIFY bodies - TCP requires content-length and if sending message with NULL body, it is not set 2006-04-03 * using function from common libraries for server contact extraction * RLMI document contains always fullState="true" 2006-03-24 * removed parameter 'mode' from code (it was not needed due to explicit xcap query needs before resource list subscription. There is query functions for simplified usage: query_resource_list) 2006-03-22 * corrected memory leak * reduced sent NOTIFY request count 2006-03-21 * accepts subscription status through QSA 2006-03-20 * multiple message queues (one for each virtual subscription) replaced by only one message queue for QSA notifications shared by all virtual subscriptions * xcap_root for resource list subscription stored into database (needed for subscription recreation) * notification is not propagated if document and content type didn't change (non-internal subscriptions only) -> reduced count of NOTIFY messages to the subscriber 2006-03-17 * changed parameters of XCAP queries 2006-03-16 * internal RLS subscriptions rewritten (i.e. nested lists handling) * added xcap_root and xcap_params into rl_subscription structure (needed for requeries, future XCAP notifications, ...) 2006-03-15 * removed parameter "mode" (now is query done before handle_subscription function and nested subscriptions can not be handled as "simple" subscriptions * added functions query_rls_services, query_resource_list, have_flat_list 2006-03-07 * corrected BUG - improper adding virtual subscriptions into list 2006-02-28 * corrected BUG - one shot timer used for DB initialization was not in shared memory 2006-02-27 * corrected BUGs - mostly used dstr_get_str instead of dstr_get_data because there were some cases when was allocated memory buffers of zero length (this consumes memory using shm_malloc), but this memory was NOT freed then 2006-02-16 * removed parts of code prepared for doing "remote" subscriptions - all is solved using internal (QSA) subscriptions now (to PA or PRESENCE_B2B or RLS itself) * UID used instead of username when mode=simple (i.e. "simple" list URIs have to be in the form sip:-list@domain.com) * reduced trace messages 2006-02-14 * separated B2B UA for presence into standalone module presence_b2b kamailio-4.0.4/obsolete/rls/Makefile0000644000000000000000000000126112223032460016051 0ustar rootroot# $Id$ # # Registrar Presence User Agent # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME = rls.so # if using libcds, the directive SER must be defined ! # and root ser directory must be in include directories DEFS += -DSER INCLUDES += -I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include -I../../lib -I../.. LIBS += -L$(LOCALBASE)/lib -L/usr/pkg/lib -lxml2 SERLIBPATH=../../lib SER_LIBS=$(SERLIBPATH)/cds/ser_cds $(SERLIBPATH)/presence/ser_presence \ $(SERLIBPATH)/xcap/ser_xcap DEFS+=-DSER_MOD_INTERFACE SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/dispatcher/0000755000000000000000000000000012223032460015737 5ustar rootrootkamailio-4.0.4/obsolete/dispatcher/ds_rpc.h0000644000000000000000000000237712223032460017373 0ustar rootroot/** * $Id$ * * dispatcher module -- stateless load balancing * * Copyright (C) 2004-2006 FhG Fokus * Copyright (C) 2005-2008 Hendrik Scholz * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _DS_RPC_H_ #define _DS_RPC_H_ #include "../../rpc.h" extern rpc_export_t rpc_methods[]; #endif /* _DS_RPC_H_ */ kamailio-4.0.4/obsolete/dispatcher/ds_backend.c0000644000000000000000000005510212223032460020163 0ustar rootroot/** * $Id$ * * dispatcher module * * Copyright (C) 2004-2006 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include "../../trim.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../dset.h" #include "dispatcher.h" /****************************************************************************** * * ds_destroy_lists() * * free all memory occupied by dispatcher module * *****************************************************************************/ int ds_destroy_lists() { extern int *ds_activelist; extern char ***ds_setp_a, ***ds_setp_b; extern int *ds_setlen_a, *ds_setlen_b; int set, node; /* assume the whole structure has been allocated if ds_activelist * is non-NULL * hint: when using ser -c memory wasn't allocated */ if (ds_activelist == NULL) return 0; /* free sets and nodes */ for (set = 0; set < DS_MAX_SETS; set++) { for (node = 0; node < DS_MAX_NODES; node++) { shm_free(ds_setp_a[set][node]); shm_free(ds_setp_b[set][node]); } shm_free(ds_setp_a[set]); shm_free(ds_setp_b[set]); } /* free counters */ shm_free(ds_setlen_a); shm_free(ds_setlen_b); /* eventually, free ds_activelist */ shm_free(ds_activelist); return 0; } /****************************************************************************** * * ds_get_hash() * * obtain hash from given strings * *****************************************************************************/ unsigned int ds_get_hash(str *x, str *y) { char* p; register unsigned v; register unsigned h; if(!x && !y) return 0; h=0; if(x) { p=x->s; if (x->len>=4){ for (;p<=(x->s+x->len-4); p+=4){ v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; h+=v^(v>>3); } }; v=0; for (;p<(x->s+x->len); p++) { v<<=8; v+=*p; } h+=v^(v>>3); } if(y) { p=y->s; if (y->len>=4){ for (;p<=(y->s+y->len-4); p+=4){ v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; h+=v^(v>>3); } }; v=0; for (;p<(y->s+y->len); p++) { v<<=8; v+=*p; } h+=v^(v>>3); } h=((h)+(h>>11))+((h>>13)+(h>>23)); return (h)?h:1; } /* * gets the part of the uri we will use as a key for hashing * params: key1 - will be filled with first part of the key * (uri user or "" if no user) * key2 - will be filled with the second part of the key * (uri host:port) * uri - str with the whole uri * parsed_uri - struct sip_uri pointer with the parsed uri * (it must point inside uri). It can be null * (in this case the uri will be parsed internally). * flags - if & DS_HASH_USER_ONLY, only the user part of the uri * will be used * returns: -1 on error, 0 on success */ static inline int get_uri_hash_keys(str* key1, str* key2, str* uri, struct sip_uri* parsed_uri, int flags) { struct sip_uri tmp_p_uri; /* used only if parsed_uri==0 */ if (parsed_uri==0){ if (parse_uri(uri->s, uri->len, &tmp_p_uri)<0){ LOG(L_ERR, "DISPATCHER: get_uri_hash_keys: invalid uri %.*s\n", uri->len, uri->len?uri->s:""); goto error; } parsed_uri=&tmp_p_uri; } /* uri sanity checks */ if (parsed_uri->host.s==0){ LOG(L_ERR, "DISPATCHER: get_uri_hash_keys: invalid uri, no host" "present: %.*s\n", uri->len, uri->len?uri->s:""); goto error; } /* we want: user@host:port if port !=5060 * user@host if port==5060 * user if the user flag is set*/ *key1=parsed_uri->user; key2->s=0; key2->len=0; if ((!(flags & (DS_HASH_USER_ONLY | DS_HASH_USER_OR_HOST))) || ((key1->s==0) && (flags & DS_HASH_USER_OR_HOST))){ /* key2=host */ *key2=parsed_uri->host; /* add port if needed */ if (parsed_uri->port.s!=0){ /* uri has a port */ /* skip port if == 5060 or sips and == 5061 */ if (parsed_uri->port_no != ((parsed_uri->type==SIPS_URI_T)?SIPS_PORT:SIP_PORT)) key2->len+=parsed_uri->port.len+1 /* ':' */; } } if (key1->s==0 && (flags & DS_HASH_USER_ONLY)){ LOG(L_WARN, "DISPATCHER: get_uri_hash_keys: empty username in:" " %.*s\n", uri->len, uri->len?uri->s:""); } return 0; error: return -1; } /** * */ int ds_hash_fromuri(struct sip_msg *msg, unsigned int *hash) { str from; str key1; str key2; if(msg==NULL || hash == NULL) { LOG(L_ERR, "DISPATCHER:ds_hash_fromuri: bad parameters\n"); return -1; } if(parse_from_header(msg)==-1) { LOG(L_ERR, "DISPATCHER:ds_hash_fromuri:ERROR cannot parse From hdr\n"); return -1; } if(msg->from==NULL || get_from(msg)==NULL) { LOG(L_ERR, "DISPATCHER:ds_hash_fromuri:ERROR cannot get From uri\n"); return -1; } from = get_from(msg)->uri; trim(&from); if (get_uri_hash_keys(&key1, &key2, &from, 0, ds_flags)<0) return -1; *hash = ds_get_hash(&key1, &key2); return 0; } /** * */ int ds_hash_touri(struct sip_msg *msg, unsigned int *hash) { str to; str key1; str key2; if(msg==NULL || hash == NULL) { LOG(L_ERR, "DISPATCHER:ds_hash_touri: bad parameters\n"); return -1; } if ((msg->to==0) && ((parse_headers(msg, HDR_TO_F, 0)==-1) || (msg->to==0))) { LOG(L_ERR, "DISPATCHER:ds_hash_touri:ERROR cannot parse To hdr\n"); return -1; } to = get_to(msg)->uri; trim(&to); if (get_uri_hash_keys(&key1, &key2, &to, 0, ds_flags)<0) return -1; *hash = ds_get_hash(&key1, &key2); return 0; } /** * */ int ds_hash_callid(struct sip_msg *msg, unsigned int *hash) { str cid; if(msg==NULL || hash == NULL) { LOG(L_ERR, "DISPATCHER:ds_hash_callid: bad parameters\n"); return -1; } if(msg->callid==NULL && ((parse_headers(msg, HDR_CALLID_F, 0)==-1) || (msg->callid==NULL)) ) { LOG(L_ERR, "DISPATCHER:ds_hash_callid:ERROR cannot parse Call-Id\n"); return -1; } cid.s = msg->callid->body.s; cid.len = msg->callid->body.len; trim(&cid); *hash = ds_get_hash(&cid, NULL); return 0; } int ds_hash_ruri(struct sip_msg *msg, unsigned int *hash) { str* uri; str key1; str key2; if(msg==NULL || hash == NULL) { LOG(L_ERR, "DISPATCHER:ds_hash_ruri: bad parameters\n"); return -1; } if (parse_sip_msg_uri(msg)<0){ LOG(L_ERR, "DISPATCHER: ds_hash_ruri: ERROR: bad request uri\n"); return -1; } uri=GET_RURI(msg); if (get_uri_hash_keys(&key1, &key2, uri, &msg->parsed_uri, ds_flags)<0) return -1; *hash = ds_get_hash(&key1, &key2); return 0; } /* from dispatcher */ static int set_new_uri_simple(struct sip_msg *msg, str *uri) { if (msg->new_uri.s) { pkg_free(msg->new_uri.s); msg->new_uri.len=0; } msg->parsed_uri_ok=0; msg->new_uri.s = (char*)pkg_malloc(uri->len+1); if (msg->new_uri.s==0) { ERR("no more pkg memory\n"); return -1; } memcpy(msg->new_uri.s, uri->s, uri->len); msg->new_uri.s[uri->len]=0; msg->new_uri.len=uri->len; ruri_mark_new(); return 0; } /* from dispatcher */ static int set_new_uri_with_user(struct sip_msg *msg, str *uri, str *user) { struct sip_uri dst; int start_len, stop_len; if (parse_uri(uri->s, uri->len, &dst) < 0) { ERR("can't parse destination URI\n"); return -1; } if ((!dst.host.s) || (dst.host.len <= 0)) { ERR("destination URI host not set\n"); return -1; } if (dst.user.s && (dst.user.len > 0)) { DBG("user already exists\n"); /* don't replace the user */ return set_new_uri_simple(msg, uri); } if (msg->new_uri.s) { pkg_free(msg->new_uri.s); msg->new_uri.len=0; } start_len = dst.host.s - uri->s; stop_len = uri->len - start_len; msg->parsed_uri_ok=0; msg->new_uri.s = (char*)pkg_malloc(uri->len+1+user->len+1); if (msg->new_uri.s==0) { ERR("no more pkg memory\n"); return -1; } memcpy(msg->new_uri.s, uri->s, start_len); memcpy(msg->new_uri.s + start_len, user->s, user->len); *(msg->new_uri.s + start_len + user->len) = '@'; memcpy(msg->new_uri.s + start_len + user->len + 1, dst.host.s, stop_len); msg->new_uri.len=uri->len + user->len + 1; msg->new_uri.s[msg->new_uri.len]=0; ruri_mark_new(); return 0; } static int set_new_uri(struct sip_msg *msg, str *uri) { struct to_body* to; struct sip_uri to_uri; /* we need to leave original user */ to = get_to(msg); if (to) { if (parse_uri(to->uri.s, to->uri.len, &to_uri) >= 0) { if (to_uri.user.s && (to_uri.user.len > 0)) { return set_new_uri_with_user(msg, uri, &to_uri.user); } } } return set_new_uri_simple(msg, uri); } /****************************************************************************** * * ds_select_dst_impl() * * use requested algorithm to calculate hash and pull an URI off the * active dispatcher list. * set dst_uri or new_uri accordingly * Attention: if specific hash algorithms fail the module falls back * to a hash on the Call-ID * *****************************************************************************/ int ds_select_dst_impl(struct sip_msg *msg, char *set_, char *alg_, int set_new) { extern int *ds_activelist; extern char ***ds_setp_a, ***ds_setp_b; extern int *ds_setlen_a, *ds_setlen_b; int set, alg; unsigned int hash; str uri; if(msg==NULL) { LOG(L_ERR, "DISPATCHER:ds_select_dst: bad parameters\n"); return -1; } if ( get_int_fparam(&set, msg, (fparam_t*)set_) < 0 ) { LOG(L_ERR, "DISPATCHER:ds_select_dst: bad set value (%d)\n", set); return -1; } if ( get_int_fparam(&alg, msg, (fparam_t*)alg_) < 0 ) { LOG(L_ERR, "DISPATCHER:ds_select_dst: bad algorithm (%d)\n", alg); return -1; } if ((set < 0) || (set >= DS_MAX_SETS)) { LOG(L_ERR, "DISPATCHER:ds_select_dst: bad set offset (%d)\n", set); return -1; } if ((alg < 0) || (alg > 4)) { LOG(L_ERR, "DISPATCHER:ds_select_dst: invalid algorithm\n"); return -1; } if (((*ds_activelist == 0) ? ds_setlen_a[set] : ds_setlen_b[set]) <= 0) { LOG(L_ERR, "DISPATCHER:ds_select_dst: empty destination set\n"); return -1; } if (msg->dst_uri.s != NULL || msg->dst_uri.len > 0) { if (msg->dst_uri.s) pkg_free(msg->dst_uri.s); msg->dst_uri.s = NULL; msg->dst_uri.len = 0; } /* get hash */ hash = 0; switch (alg) { /* see bottom for case '0' */ case 1: /* hash from uri */ if (ds_hash_fromuri(msg, &hash) != 0) { if (ds_hash_callid(msg, &hash) != 0) { LOG(L_ERR, "DISPATCHER:ds_select_dst: cannot determine from uri hash\n"); return -1; } } break; case 2: /* hash to uri */ if (ds_hash_touri(msg, &hash) != 0) { if (ds_hash_callid(msg, &hash) != 0) { LOG(L_ERR, "DISPATCHER:ds_select_dst: cannot determine from uri hash\n"); return -1; } } break; case 3: /* hash Request uri */ if (ds_hash_ruri(msg, &hash) != 0) { if (ds_hash_callid(msg, &hash) != 0) { LOG(L_ERR, "DISPATCHER:ds_select_dst: cannot determine from uri hash\n"); return -1; } } break; case 4: /* Call ID hash, shifted right once to skip low bit * This should allow for even distribution when using * Call ID hash twice (i.e. fe + be) */ if (ds_hash_callid(msg, &hash) != 0) { LOG(L_ERR, "DISPATCHER:ds_select_dst: cannot determine callid hash\n"); hash = 0; /* bad default, just to be sure */ return -1; } hash = hash >> 4; /* should be enough for even more backends */ break; case 0: /* hash call id */ /* fall-through */ default: if (ds_hash_callid(msg, &hash) != 0) { LOG(L_ERR, "DISPATCHER:ds_select_dst: cannot determine callid hash\n"); hash = 0; /* bad default, just to be sure */ return -1; } break; /* make gcc happy */ } DBG("DISPATCHER:ds_select_dst: hash: [%u]\n", hash); /* node list offset from hash */ if (*ds_activelist == 0) { hash = hash % ds_setlen_a[set]; uri.s = ds_setp_a[set][hash]; uri.len = strlen(ds_setp_a[set][hash]); } else { hash = hash % ds_setlen_b[set]; uri.s = ds_setp_b[set][hash]; uri.len = strlen(ds_setp_b[set][hash]); } if (!set_new) { if (set_dst_uri(msg, &uri) < 0) { LOG(L_ERR, "DISPATCHER:dst_select_dst: Error while setting dst_uri\n"); return -1; } /* dst_uri changed, so it makes sense to re-use the current uri for forking */ ruri_mark_new(); /* re-use uri for serial forking */ DBG("DISPATCHER:ds_select_dst: selected [%d-%d-%d] <%.*s>\n", alg, set, hash, msg->dst_uri.len, msg->dst_uri.s); } else { if (set_new_uri(msg, &uri) < 0) { LOG(L_ERR, "DISPATCHER:dst_select_dst: Error while setting new_uri\n"); return -1; } DBG("DISPATCHER:ds_select_dst: selected [%d-%d-%d] <%.*s>\n", alg, set, hash, msg->new_uri.len, msg->dst_uri.s); } return 1; } /** * from dispatcher */ int ds_select_dst(struct sip_msg *msg, char *set, char *alg) { return ds_select_dst_impl(msg, set, alg, 0); } /** * from dispatcher */ int ds_select_new(struct sip_msg *msg, char *set, char *alg) { return ds_select_dst_impl(msg, set, alg, 1); } /****************************************************************************** * * ds_init_memory() * * init memory structure * * - ds_activelist: active list (0 or 1) * - ds_setp_a/ds_setp_b: each active config has up to DS_MAX_SETS so * called sets. Each set references to a node list with DS_MAX_NODES * slots that hold SIP URIs up to DS_MAX_URILEN-1 Bytes * *****************************************************************************/ #define MALLOC_ERR LOG(L_ERR, \ "ERROR:DISPATCHER:init_dispatcher_mem: shm_malloc() failed\n"); int ds_init_memory() { extern int *ds_activelist; extern char ***ds_setp_a, ***ds_setp_b; extern int *ds_setlen_a, *ds_setlen_b; int set, node; /* active list */ ds_activelist = (int *) shm_malloc(sizeof(int)); if (ds_activelist == NULL) { MALLOC_ERR; return -1; } *ds_activelist = 0; ds_setp_a = (char ***) shm_malloc(sizeof(char **) * DS_MAX_SETS); if (ds_setp_a == NULL) { MALLOC_ERR; return -1; } /* attach node list to each set */ for (set = 0; set < DS_MAX_SETS; set++) { ds_setp_a[set] = (char **) shm_malloc(sizeof(char *) * DS_MAX_NODES); if (ds_setp_a[set] == NULL) { MALLOC_ERR; return -1; } /* init each node */ for (node = 0; node < DS_MAX_NODES; node++) { ds_setp_a[set][node] = (char *) shm_malloc(sizeof(char) * DS_MAX_URILEN); if (ds_setp_a[set][node] == NULL) { MALLOC_ERR; return -1; } *ds_setp_a[set][node] = '\0'; } } ds_setp_b = (char ***) shm_malloc(sizeof(char **) * DS_MAX_SETS); if (ds_setp_b == NULL) { MALLOC_ERR; return -1; } /* attach node list to each set */ for (set = 0; set < DS_MAX_SETS; set++) { ds_setp_b[set] = (char **) shm_malloc(sizeof(char *) * DS_MAX_NODES); if (ds_setp_b[set] == NULL) { MALLOC_ERR; return -1; } /* init each node */ for (node = 0; node < DS_MAX_NODES; node++) { ds_setp_b[set][node] = (char *) shm_malloc(sizeof(char) * DS_MAX_URILEN); if (ds_setp_b[set][node] == NULL) { MALLOC_ERR; return -1; } *ds_setp_b[set][node] = '\0'; } } /* set length counters */ ds_setlen_a = (int *) shm_malloc(sizeof(int) * DS_MAX_SETS); if (ds_setlen_a == NULL) { MALLOC_ERR; return -1; } ds_setlen_b = (int *) shm_malloc(sizeof(int) * DS_MAX_SETS); if (ds_setlen_b == NULL) { MALLOC_ERR; return -1; } for (set = 0; set < DS_MAX_SETS; set++) { ds_setlen_a[set] = 0; ds_setlen_b[set] = 0; } return 0; } /****************************************************************************** * * ds_clean_list() * * empty the in-active config so we can reload a new one * this does not free() memory! * *****************************************************************************/ void ds_clean_list(void) { extern int *ds_activelist; extern char ***ds_setp_a, ***ds_setp_b; extern int *ds_setlen_a, *ds_setlen_b; int set, node; for (set = 0; set < DS_MAX_SETS; set++) { for (node = 0; node < DS_MAX_NODES; node++) { if (*ds_activelist == 0) { *ds_setp_b[set][node] = '\0'; } else { *ds_setp_a[set][node] = '\0'; } } if (*ds_activelist == 0) { ds_setlen_b[set] = 0; } else { ds_setlen_a[set] = 0; } } return; } /****************************************************************************** * * ds_load_list() * * (re)load dispatcher module config file using the dispatcher module * parser. * Save config in the in-active shared memory section * Attention: We do not bail out with an error if we try to store more * sets/nodes than we have room for since we might be running * live and don't want SER to stop processing packets, do we? * *****************************************************************************/ int ds_load_list (char *lfile) { /* storage */ extern int *ds_activelist; extern char ***ds_setp_a, ***ds_setp_b; extern int *ds_setlen_a, *ds_setlen_b; /* parser related */ char line[MAX_LINE_LEN], *p; int set; str uri; struct sip_uri puri; FILE *f = NULL; DBG("ds_load_list() invoked\n"); /* clean up temporary list before saving updated config */ (void) ds_clean_list(); if (lfile == NULL || strlen(lfile) <= 0) { LOG(L_ERR, "DISPATCHER:ds_load_list: cannot open list file [%s]\n", lfile); return -1; } f = fopen(lfile, "r"); if (f == NULL) { LOG(L_ERR, "DISPATCHER:ds_load_list: cannot open list file [%s]\n", lfile); return -1; } p = fgets(line, MAX_LINE_LEN-1, f); while (p) { /* eat all white spaces */ while (*p && (*p==' ' || *p=='\t' || *p=='\r' || *p=='\n')) p++; if (*p=='\0' || *p=='#') goto next_line; /* get set id */ set = 0; while(*p>='0' && *p<='9') { set = set*10+ (*p-'0'); p++; } /* eat all white spaces */ while(*p && (*p==' ' || *p=='\t' || *p=='\r' || *p=='\n')) p++; if(*p=='\0' || *p=='#') { LOG(L_ERR, "DISPATCHER:ds_load_list: bad line [%s]\n", line); goto error; } /* get uri */ uri.s = p; while(*p && *p!=' ' && *p!='\t' && *p!='\r' && *p!='\n' && *p!='#') p++; uri.len = p-uri.s; /* check uri */ if(parse_uri(uri.s, uri.len, &puri)!=0) { LOG(L_ERR, "DISPATCHER:ds_load_list: bad uri [%.*s]\n", uri.len, uri.s); goto next_line; } /* now we have set id and get uri -> save to shared mem */ if ((set > DS_MAX_SETS-1) || (uri.len > DS_MAX_URILEN)) { LOG(L_ERR, "DISPATCHER:ds_load_list: increase DS_MAX_SETS or DS_MAX_URILEN ...\n"); goto next_line; } /* save correct line from config file */ DBG("content: set %d, str: %.*s\n", set, uri.len, uri.s); if (*ds_activelist == 0) { DBG("[%d] active nodes in this set so far: %d\n", *ds_activelist, ds_setlen_b[set]); if (ds_setlen_b[set] >= (DS_MAX_NODES-1)) { LOG(L_ERR, "DISPATCHER:ds_load_list: increase DS_MAX_NODES!\n"); goto next_line; } snprintf((char *)ds_setp_b[set][ds_setlen_b[set]], DS_MAX_URILEN-1, "%.*s", uri.len, uri.s); ds_setlen_b[set]++; DBG("[%d] active nodes in this set now: %d\n", *ds_activelist, ds_setlen_b[set]); DBG("node now contains: %s\n", (char *)ds_setp_b[set][ds_setlen_b[set]-1]); } else { DBG("[%d] active nodes in this set so far: %d\n", *ds_activelist, ds_setlen_a[set]); if (ds_setlen_a[set] >= (DS_MAX_NODES-1)) { LOG(L_ERR, "DISPATCHER:ds_load_list: increase DS_MAX_NODES!\n"); goto next_line; } snprintf((char *)ds_setp_a[set][ds_setlen_a[set]], DS_MAX_URILEN-1, "%.*s", uri.len, uri.s); ds_setlen_a[set]++; DBG("[%d] active nodes in this set now: %d\n", *ds_activelist, ds_setlen_a[set]); DBG("node now contains: %s\n", (char *)ds_setp_a[set][ds_setlen_a[set]-1]); } next_line: p = fgets(line, MAX_LINE_LEN-1, f); } if (f != NULL) fclose(f); /* see if there are any active sets at all */ if (*ds_activelist == 0) { int found = 0; int i; for (i = 0; i < DS_MAX_SETS; i++) { if (ds_setlen_b[i] > 0) found++; } if (!found) return -1; } else { int found = 0; int i; for (i = 0; i < DS_MAX_SETS; i++) { if (ds_setlen_a[i] > 0) found++; } if (!found) return -1; } return 0; error: if (f != NULL) fclose(f); return -1; } kamailio-4.0.4/obsolete/dispatcher/doc/0000755000000000000000000000000012223032460016504 5ustar rootrootkamailio-4.0.4/obsolete/dispatcher/doc/dispatcher.cfg0000644000000000000000000000171412223032460021316 0ustar rootroot# $Id$ # sample config file for dispatcher module # debug=9 # debug level (cmd line: -dddddddddd) fork=no log_stderror=yes # (cmd line: -E) children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 # for more info: sip_router -h # ------------------ module loading ---------------------------------- loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/sl/sl.so" # loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/dispatcher/dispatcher.so" # ----------------- setting module-specific parameters --------------- # -- dispatcher params -- modparam("dispatcher", "list_file", "../etc/dispatcher.list") # modparam("dispatcher", "force_dst", 1) route{ if ( !mf_process_maxfwd_header("10") ) { sl_send_reply("483","To Many Hops"); drop(); }; ds_select_dst("2", "0"); forward(uri:host, uri:port); # t_relay(); } kamailio-4.0.4/obsolete/dispatcher/doc/dispatcher.list0000644000000000000000000000024612223032460021531 0ustar rootroot# $Id$ # dispatcher destination sets # # proxies 2 sip:127.0.0.1:5080 2 sip:127.0.0.1:5082 # gateways 1 sip:127.0.0.1:7070 1 sip:127.0.0.1:7072 1 sip:127.0.0.1:7074kamailio-4.0.4/obsolete/dispatcher/doc/functions.xml0000644000000000000000000000317712223032460021246 0ustar rootroot
Functions
<function>ds_select_dst(set, alg)</function> The method selects a destination from addresses set. Meaning of the parameters is as follows: set - the id of the set from where to pick up destination address. It is the first column in destination list file. alg - the algorithm used to select the destination address. "0" - hash over callid "1" - hash over from URI. "2" - hash over to URI. "3" - hash over the Request-URI. "X" - if the algorithm is not implemented, the first entry in set is chosen. <function>ds_select_dst</function> usage ... ds_select_dst("1", "0"); ...
kamailio-4.0.4/obsolete/dispatcher/doc/dispatcher.xml0000644000000000000000000000370312223032460021357 0ustar rootroot
Daniel-Constantin Mierla FhG FOKUS
mierla@fokus.fraunhofer.de
2004 FhG FOKUS
Dispatcher Module
Overview This modules implements a dispatcher for destination addresses. It computes hashes over parts of the request and selects an address from a destination set. The selected address is used then as outbound proxy. The module can be used as a stateless load balancer, having no guarantee of fair distribution. The dispatcher module offers reloads on the fly using sercmd.
Installation And Running Destination List File Each destination point must be on one line. First token is the set id and next is destination address. The set id must be an integer value. Destination address must be a valid SIP URI. Empty lines or lines starting with "#" are ignored. SER Configuration File
kamailio-4.0.4/obsolete/dispatcher/doc/fifo.xml0000644000000000000000000000375212223032460020160 0ustar rootroot
FIFO Interface The module allows dumping the current configuration as well as dispatcher list reloading via the FIFO interface. There are two FIFO commands to use with dispatcher. dispatcher.dump - dump the current dispatcher sets dispatcher.reload - reload the dispatcher list text file
Dumping the current configuration The command dispatcher.dump can be used to dump the currently deployed dispatcher list in the SER internal notation. dumping the active dispatcher list # sercmd dispatcher.dump flags: DI_MAX_SETS: 16 DI_MAX_NODES: 16 DI_MAX_URILEN: 256 Active dispatcher list: 0 Set '0' node 0 sip:10.1.1.1:5060 node 1 sip:10.1.1.2:5060 node 2 sip:10.1.1.3:5060 node 3 sip:10.1.1.4:5060 Set '1' is empty Set '2' is empty Set '3' is empty Set '4' is empty Set '5' is empty Set '6' is empty Set '7' is empty Set '8' is empty Set '9' is empty Set '10' is empty Set '11' is empty Set '12' is empty Set '13' is empty Set '14' is empty Set '15' is empty End of dispatcher list #
Reloading the dispatcher list The command dispatcher.reload can be used to update the dispatcher list while running SER. Reloading the dispatcher list # sercmd fifo dispatcher.reload dispatcher list 1 activated #
kamailio-4.0.4/obsolete/dispatcher/doc/Makefile0000644000000000000000000000013312223032460020141 0ustar rootrootdocs = dispatcher.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/dispatcher/doc/params.xml0000644000000000000000000000407012223032460020512 0ustar rootroot
Parameters
<varname>list_file</varname> (string) Path to the file with destination sets. Default value is "/etc/ser/dispatcher.list" or "/usr/local/etc/ser/dispatcher.list". Set the "list_file" parameter ... modparam("dispatcher", "list_file", "/var/run/ser/dispatcher.list") ...
<varname>force_dst</varname> (int) If set to 1, force overwriting of destination address when that is already set. Default value is "0". Set the "force_dst" parameter ... modparam("dispatcher", "force_dst", 1) ...
<varname>flags</varname> (int) Various flags that affect the hashing behaviour. For now only the flag 1 and 2 are defined. If flag 1 is set only the username part of the uri will be used when computing an uri based hash. If flag 2 is set the username part of the uri will be used and if no username part is present the hostname part will be used. If no flags are set the username, hostname and port will be used The port is used only if different from 5060 (normal sip uri) or 5061 (in the sips case). Default value is "0". Set the "flags" parameter ... modparam("dispatcher", "flags", 1) ...
kamailio-4.0.4/obsolete/dispatcher/README0000644000000000000000000001272512223032460016626 0ustar rootroot1. Dispatcher Module Daniel-Constantin Mierla FhG FOKUS Copyright © 2004 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. Parameters 1.2.1. list_file (string) 1.2.2. force_dst (int) 1.2.3. flags (int) 1.3. Functions 1.3.1. ds_select_dst(set, alg) 1.4. FIFO Interface 1.4.1. Dumping the current configuration 1.4.2. Reloading the dispatcher list 1.5. Installation And Running 1.1. Overview This modules implements a dispatcher for destination addresses. It computes hashes over parts of the request and selects an address from a destination set. The selected address is used then as outbound proxy. The module can be used as a stateless load balancer, having no guarantee of fair distribution. The dispatcher module offers reloads on the fly using sercmd. 1.2. Parameters 1.2.1. list_file (string) Path to the file with destination sets. Default value is "/etc/ser/dispatcher.list" or "/usr/local/etc/ser/dispatcher.list". Example 1. Set the "list_file" parameter ... modparam("dispatcher", "list_file", "/var/run/ser/dispatcher.list") ... 1.2.2. force_dst (int) If set to 1, force overwriting of destination address when that is already set. Default value is "0". Example 2. Set the "force_dst" parameter ... modparam("dispatcher", "force_dst", 1) ... 1.2.3. flags (int) Various flags that affect the hashing behaviour. For now only the flag 1 and 2 are defined. If flag 1 is set only the username part of the uri will be used when computing an uri based hash. If flag 2 is set the username part of the uri will be used and if no username part is present the hostname part will be used. If no flags are set the username, hostname and port will be used The port is used only if different from 5060 (normal sip uri) or 5061 (in the sips case). Default value is "0". Example 3. Set the "flags" parameter ... modparam("dispatcher", "flags", 1) ... 1.3. Functions 1.3.1. ds_select_dst(set, alg) The method selects a destination from addresses set. Meaning of the parameters is as follows: * set - the id of the set from where to pick up destination address. It is the first column in destination list file. * alg - the algorithm used to select the destination address. + "0" - hash over callid + "1" - hash over from URI. + "2" - hash over to URI. + "3" - hash over the Request-URI. + "X" - if the algorithm is not implemented, the first entry in set is chosen. Example 4. ds_select_dst usage ... ds_select_dst("1", "0"); ... 1.4. FIFO Interface The module allows dumping the current configuration as well as dispatcher list reloading via the FIFO interface. There are two FIFO commands to use with dispatcher. * dispatcher.dump - dump the current dispatcher sets * dispatcher.reload - reload the dispatcher list text file 1.4.1. Dumping the current configuration The command dispatcher.dump can be used to dump the currently deployed dispatcher list in the SER internal notation. Example 5. dumping the active dispatcher list # sercmd dispatcher.dump flags: DI_MAX_SETS: 16 DI_MAX_NODES: 16 DI_MAX_URILEN: 256 Active dispatcher list: 0 Set '0' node 0 sip:10.1.1.1:5060 node 1 sip:10.1.1.2:5060 node 2 sip:10.1.1.3:5060 node 3 sip:10.1.1.4:5060 Set '1' is empty Set '2' is empty Set '3' is empty Set '4' is empty Set '5' is empty Set '6' is empty Set '7' is empty Set '8' is empty Set '9' is empty Set '10' is empty Set '11' is empty Set '12' is empty Set '13' is empty Set '14' is empty Set '15' is empty End of dispatcher list # 1.4.2. Reloading the dispatcher list The command dispatcher.reload can be used to update the dispatcher list while running SER. Example 6. Reloading the dispatcher list # sercmd fifo dispatcher.reload dispatcher list 1 activated # 1.5. Installation And Running Example 7. Destination List File Each destination point must be on one line. First token is the set id and next is destination address. The set id must be an integer value. Destination address must be a valid SIP URI. Empty lines or lines starting with "#" are ignored. # $Id$ # dispatcher destination sets # # proxies 2 sip:127.0.0.1:5080 2 sip:127.0.0.1:5082 # gateways 1 sip:127.0.0.1:7070 1 sip:127.0.0.1:7072 1 sip:127.0.0.1:7074 Example 8. SER Configuration File # $Id$ # sample config file for dispatcher module # debug=9 # debug level (cmd line: -dddddddddd) fork=no log_stderror=yes # (cmd line: -E) children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 # for more info: sip_router -h # ------------------ module loading ---------------------------------- loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/sl/sl.so" # loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/dispatcher/dispatcher.so" # ----------------- setting module-specific parameters --------------- # -- dispatcher params -- modparam("dispatcher", "list_file", "../etc/dispatcher.list") # modparam("dispatcher", "force_dst", 1) route{ if ( !mf_process_maxfwd_header("10") ) { sl_send_reply("483","To Many Hops"); drop(); }; ds_select_dst("2", "0"); forward(uri:host, uri:port); # t_relay(); } kamailio-4.0.4/obsolete/dispatcher/ds_rpc.c0000644000000000000000000000764212223032460017366 0ustar rootroot/** * $Id$ * * dispatcher module -- stateless load balancing * * Copyright (C) 2004-2006 FhG Fokus * Copyright (C) 2005-2008 Hendrik Scholz * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "../../sr_module.h" #include "dispatcher.h" #include "ds_rpc.h" /****************************************************************************** * * dispatcher_dump() * * fifo command handler for 'dispatcher_dump' command * dumps the active dispatcher config to the pipe * *****************************************************************************/ void rpc_dump(rpc_t *rpc, void *c) { extern int *ds_activelist; extern char ***ds_setp_a, ***ds_setp_b; extern int **ds_setlen_a, **ds_setlen_b; int set, node; if (rpc->printf(c, "flags: DS_MAX_SETS: %d DS_MAX_NODES: %d DS_MAX_URILEN: %d", DS_MAX_SETS, DS_MAX_NODES, DS_MAX_URILEN) < 0) return; if (rpc->printf(c, "Active dispatcher list: %d", *ds_activelist) < 0) return; if (*ds_activelist == 0) { for (set = 0; set < DS_MAX_SETS; set++) { if (ds_setlen_a[set] == 0) { if (rpc->printf(c, "Set %2d is empty", set) < 0) return; } else { if (rpc->printf(c, "Set %2d:", set) < 0) return; for (node = 0; node < (long int) ds_setlen_a[set]; node++) { if (rpc->printf(c, " node %3d %s", node, ds_setp_a[set][node]) < 0) return; } } } } else { for (set = 0; set < DS_MAX_SETS; set++) { if (ds_setlen_b[set] == 0) { if (rpc->printf(c, "Set %2d is empty", set) < 0) return; } else { if (rpc->printf(c, "Set %2d:", set) < 0) return; for (node = 0; node < (long int) ds_setlen_b[set]; node++) { if (rpc->printf(c, " node %3d %s", node, ds_setp_b[set][node]) < 0) return; } } } } rpc->printf(c, "End of dispatcher list"); return; } /****************************************************************************** * * dispatcher_reload() * * reload a dispatcher list and activate if successful * *****************************************************************************/ void rpc_reload(rpc_t *rpc, void *c) { extern char *dslistfile; extern int *ds_activelist; LOG(L_ERR, "DISPATCHER module reloading\n"); if (ds_load_list(dslistfile) == 0) { DS_SWITCH_ACTIVE_LIST rpc->printf(c, "dispatcher list %d activated", *ds_activelist); } else { rpc->printf(c, "dispatcher list reload failed"); } return ; } /* rpc function titles */ static const char *rpc_dump_doc[2] = { "Dump dispatcher set configuration", 0 }; static const char *rpc_reload_doc[2] = { "Reload dispatcher list from file", 0 }; rpc_export_t rpc_methods[] = { {"dispatcher.dump", rpc_dump, rpc_dump_doc, 0}, {"dispatcher.reload", rpc_reload, rpc_reload_doc, 0}, {0, 0, 0, 0} }; kamailio-4.0.4/obsolete/dispatcher/dispatcher.c0000644000000000000000000000750412223032460020237 0ustar rootroot/** * $Id$ * * dispatcher module -- stateless load balancing * * Copyright (C) 2004-2006 FhG Fokus * Copyright (C) 2005-2008 Hendrik Scholz * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../ut.h" #include "../../mem/mem.h" #include "dispatcher.h" #include "ds_rpc.h" MODULE_VERSION /** parameters */ char *dslistfile = CFG_DIR"dispatcher.list"; int force_dst = 0; int ds_flags = 0; char *ds_activelist; char ***ds_setp_a, ***ds_setp_b; int **ds_setlen_a, **ds_setlen_b; /** module functions */ static int mod_init(void); static int child_init(int); static int w_ds_select_dst(struct sip_msg*, char*, char*); static int w_ds_select_new(struct sip_msg*, char*, char*); static void destroy(void); static cmd_export_t cmds[]={ {"ds_select_dst", w_ds_select_dst, 2, fixup_var_int_12, REQUEST_ROUTE|FAILURE_ROUTE}, {"ds_select_new", w_ds_select_new, 2, fixup_var_int_12, REQUEST_ROUTE|FAILURE_ROUTE}, {0,0,0,0,0} }; static param_export_t params[]={ {"list_file", PARAM_STRING, &dslistfile}, {"force_dst", PARAM_INT, &force_dst}, {"flags", PARAM_INT, &ds_flags}, {0,0,0} }; /** module exports */ struct module_exports exports= { "dispatcher", cmds, rpc_methods, /* RPC methods */ params, mod_init, /* module initialization function */ (response_function) 0, (destroy_function) destroy, 0, child_init /* per-child init function */ }; /****************************************************************************** * init module function * * - init memory * - clear both 'memory pages' * - load dispatcher lists from file * - activate list * ******************************************************************************/ static int mod_init(void) { DBG("DISPATCHER: initializing ...\n"); if(ds_init_memory() != 0) { LOG(L_ERR, "DISPATCHER:mod_init:ERROR -- memory allocation error\n"); return -1; } /* clean both lists */ ds_clean_list(); DS_SWITCH_ACTIVE_LIST ds_clean_list(); if(ds_load_list(dslistfile)!=0) { LOG(L_ERR, "DISPATCHER:mod_init:ERROR -- couldn't load list file\n"); return -1; } /* switch active list since we had the offline one prepared */ DS_SWITCH_ACTIVE_LIST return 0; } /** * Initialize children */ static int child_init(int rank) { DBG("DISPATCHER:init_child #%d / pid <%d>\n", rank, getpid()); return 0; } /** * */ static int w_ds_select_dst(struct sip_msg* msg, char* set, char* alg) { if(msg==NULL) return -1; return ds_select_dst(msg, set, alg); } /** * */ static int w_ds_select_new(struct sip_msg* msg, char* set, char* alg) { if(msg==NULL) return -1; return ds_select_new(msg, set, alg); } /** * destroy function */ static void destroy(void) { DBG("DISPATCHER: destroy module ...\n"); ds_destroy_lists(); } kamailio-4.0.4/obsolete/dispatcher/dispatch.h0000644000000000000000000000316612223032460017715 0ustar rootroot/** * $Id$ * * dispatcher module * * Copyright (C) 2004-2006 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /** * History * ------- * 2004-07-31 first version, by dcm * */ #ifndef _DISPATCH_H_ #define _DISPATCH_H_ #include "../../parser/msg_parser.h" #define DS_HASH_USER_ONLY 1 /* use only the uri user part for hashing */ #define DS_HASH_USER_OR_HOST 2 /* use user part of uri for hashing with fallback to host */ extern int ds_flags; int ds_set_hash_f(int n); int ds_load_list(char *lfile); int ds_destroy_list(); int ds_select_dst(struct sip_msg *msg, char *set, char *alg); int ds_select_new(struct sip_msg *msg, char *set, char *alg); #endif kamailio-4.0.4/obsolete/dispatcher/dispatcher.h0000644000000000000000000000436112223032460020242 0ustar rootroot/** * $Id$ * * dispatcher module * * Copyright (C) 2004-2006 FhG Fokus * Copyright (C) 2005-2008 Hendrik Scholz * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _DISPATCHER_H_ #define _DISPATCHER_H_ #include "../../sr_module.h" /* these are tunable * * if you increase no of sets or nodes to >99 output in di_rpc.c * might get garbled (increase %2d and %3d respectively) */ #define DS_MAX_SETS 32 /* maximum number of dispatcher sets */ #define DS_MAX_NODES 32 /* max number of nodes per set */ #define DS_MAX_URILEN 256 /* max SIP uri length */ /* no service needed below */ #define DS_HASH_USER_ONLY 1 /* use only the uri user part for hashing */ #define DS_HASH_USER_OR_HOST 2 /* use user part of uri for hashing with fallback to host */ /* MAX_LINE_LEN should be >= DS_MAX_URI_LEN to prevent overflow in * config file parser */ #define MAX_LINE_LEN DS_MAX_URILEN extern int ds_flags; /* prototypes */ int ds_load_list(char *lfile); int ds_destroy_lists(); int ds_select_dst(struct sip_msg *msg, char *set, char *alg); int ds_select_new(struct sip_msg *msg, char *set, char *alg); int ds_init_memory(void); void ds_clean_list(void); #define DS_SWITCH_ACTIVE_LIST *ds_activelist = (0 == *ds_activelist) ? 1 : 0; #endif /* _DISPATCHER_H_ */ kamailio-4.0.4/obsolete/dispatcher/Makefile0000644000000000000000000000036212223032460017400 0ustar rootroot# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=dispatcher.so LIBS= DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/bdb/0000755000000000000000000000000012223032460014340 5ustar rootrootkamailio-4.0.4/obsolete/bdb/bdb_val.c0000644000000000000000000002167312223032460016106 0ustar rootroot/* $Id$ * * Copyright (C) 2006-2007 Sippy Software, Inc. * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "bdb.h" int bdb_set_row(db_con_t* _h, bdb_urow_p u_r, bdb_val_p _v, bdb_row_p _r) { bdb_val_p v, nv; bdb_uval_p uv; int c_idx; int found; /* filling data into row */ for (v = _v, c_idx = 0; v != NULL; v = v->next, c_idx++) { nv = pkg_malloc(sizeof(*nv)); memset(nv, 0, sizeof(*nv)); bdb_push_field(_r, nv); found = 0; for (uv = u_r->fields; uv != NULL; uv = uv->next) { if (uv->c_idx == c_idx) { found = 1; break; } } if (found) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_set_row: need to update column #%0d\n", c_idx); #endif if (bdb_field_db2bdb(nv, &(uv->v)) < 0) { return -1; } } else { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_set_row: no need to update column #%0d\n", c_idx); #endif if (bdb_field_db2bdb(nv, &(v->v)) < 0) { return -1; } } bdb_push_data(_r, nv); }; bdb_merge_tail(_r); return 0; }; int bdb_row_db2bdb(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n, bdb_row_p *_r) { bdb_row_p r; bdb_table_p t; bdb_column_p c; int found; int use_key, found_key, key_idx; int i; bdb_val_p v; *_r = NULL; if ((t = bdb_find_table(CON_TABLE(_h))) == NULL) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_row_db2bdb: table: no table in use\n"); #endif return -1; }; key_idx = -1; use_key = -1; /* check if all columns exist */ for (i = 0; i < _n; i++) { found = 0; /* key column is always first one */ for (c = t->cols, found_key = 1; c != NULL; c = c->next, found_key = 0) { if (!strcmp(_k[i], c->name.s)) { found = 1; break; } } if (found_key == 1) { key_idx = i; use_key++; /* set to 0 if used in clause only once */ } if (!found) { LOG(L_ERR, "BDB:bdb_row_db2bdb: column '%s' does not exist\n", _k[i]); return -1; } } if (use_key < 0) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_row_db2bdb: primary key value must be supplied\n"); #endif return -1; } if (use_key > 0) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_row_db2bdb: primary key value must be supplied only once\n"); #endif return -1; } r = pkg_malloc(sizeof(*r)); memset(r, 0, sizeof(*r)); /* filling data into row */ for (c = t->cols; c != NULL; c = c->next) { v = pkg_malloc(sizeof(*v)); memset(v, 0, sizeof(*v)); VAL_NULL(&(v->v)) = 1; /* default value is NULL */ bdb_push_field(r, v); for (i = 0; i < _n; i++) { if (!strcmp(_k[i], c->name.s)) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_row_db2bdb: filling column '%.*s'\n", c->name.len, c->name.s); #endif if (bdb_field_db2bdb(v, &_v[i]) < 0) { bdb_free_row(r); return -1; }; if (i == key_idx) { if (bdb_set_key(r, v) < 0) { bdb_free_row(r); return -1; }; } break; } } bdb_push_data(r, v); }; bdb_merge_tail(r); *_r = r; return 0; }; void bdb_merge_tail(bdb_row_p _r) { if (_r->tail.len > 0) { _r->data.data = pkg_realloc(_r->data.data, _r->data.size + _r->tail.len); memcpy(_r->data.data + _r->data.size, _r->tail.s, _r->tail.len); _r->data.size += _r->tail.len; } }; void bdb_push_data(bdb_row_p _r, bdb_val_p _v) { if (_r->data.size == 0) { _r->data.data = pkg_malloc(sizeof(*_v)); } else { _r->data.data = pkg_realloc(_r->data.data, _r->data.size + sizeof(*_v)); } memcpy(_r->data.data + _r->data.size, _v, sizeof(*_v)); _r->data.size += sizeof(*_v); if (!VAL_NULL(&(_v->v))) { switch (VAL_TYPE(&(_v->v))) { case DB_STRING: case DB_STR: case DB_BLOB: if (_r->tail.len == 0) { _r->tail.s = pkg_malloc(VAL_STR(&(_v->v)).len); } else { _r->tail.s = pkg_realloc(_r->tail.s, _r->tail.len + VAL_STR(&(_v->v)).len); } memcpy(_r->tail.s + _r->tail.len, VAL_STR(&(_v->v)).s, VAL_STR(&(_v->v)).len); _r->tail.len += VAL_STR(&(_v->v)).len; break; default: break; } } }; void bdb_push_field(bdb_row_p _r, bdb_val_p _v) { bdb_val_p f; if (_r->fields == NULL) { _r->fields = _v; return; } f = _r->fields; while (f->next != NULL) { f = f->next; } f->next = _v; }; void bdb_free_field(bdb_val_p _v) { if (!VAL_NULL(&(_v->v))) { if (VAL_TYPE(&(_v->v)) == DB_STR || VAL_TYPE(&(_v->v)) == DB_STRING || VAL_TYPE(&(_v->v)) == DB_BLOB) { pkg_free(VAL_STR(&(_v->v)).s); } } pkg_free(_v); }; void bdb_free_field_list(bdb_val_p _v) { bdb_val_p curr, next; for (curr = _v; curr != NULL;) { next = curr->next; bdb_free_field(curr); curr = next; } }; void bdb_free_row(bdb_row_p _r) { if (_r->fields != NULL) { bdb_free_field_list(_r->fields); } if (_r->data.size > 0) { pkg_free(_r->data.data); } if (_r->tail.len > 0) { pkg_free(_r->tail.s); } pkg_free(_r); }; void bdb_free_row_list(bdb_row_p _r) { bdb_row_p curr, next; for (curr = _r; curr != NULL;) { next = curr->next; bdb_free_row(curr); curr = next; } }; int bdb_field_db2bdb(bdb_val_p v, db_val_t* _v) { char *s; VAL_NULL(&(v->v)) = VAL_NULL(_v); VAL_TYPE(&(v->v)) = VAL_TYPE(_v); if (!VAL_NULL(&(v->v))) { switch (VAL_TYPE(_v)) { case DB_INT: VAL_INT(&(v->v)) = VAL_INT(_v); break; case DB_FLOAT: VAL_FLOAT(&(v->v)) = VAL_FLOAT(_v); break; case DB_DATETIME: VAL_TIME(&(v->v)) = VAL_TIME(_v); break; case DB_BLOB: s = pkg_malloc(VAL_BLOB(_v).len); memcpy(s, VAL_BLOB(_v).s, VAL_BLOB(_v).len); VAL_BLOB(&(v->v)).s = s; VAL_BLOB(&(v->v)).len = VAL_BLOB(_v).len; break; case DB_DOUBLE: VAL_DOUBLE(&(v->v)) = VAL_DOUBLE(_v); break; case DB_STRING: VAL_STR(&(v->v)).len = strlen(VAL_STRING(_v)) + 1; s = pkg_malloc(VAL_STR(&(v->v)).len); strcpy(s, VAL_STRING(_v)); VAL_STRING(&(v->v)) = s; break; case DB_STR: s = pkg_malloc(VAL_STR(_v).len); memcpy(s, VAL_STR(_v).s, VAL_STR(_v).len); VAL_STR(&(v->v)).s = s; VAL_STR(&(v->v)).len = VAL_STR(_v).len; break; case DB_BITMAP: VAL_BITMAP(&(v->v)) = VAL_BITMAP(_v); break; default: LOG(L_ERR, "BDB:bdb_field_db2bdb: unknown column type: %0X\n", VAL_TYPE(_v)); return -1; break; } } return 0; }; int bdb_get_db_row(db_con_t* _h, DBT* _data, bdb_val_p* _v) { bdb_val_p v, prev; void *p, *tail; int l; if (!_data || !_data->size) { LOG(L_ERR, "BDB:bdb_get_db_row: invalid data\n"); *_v = NULL; return -1; } *_v = (bdb_val_p)_data->data; prev = NULL; p = _data->data; l = 0; tail = p + sizeof(*v) * BDB_CON_COL_NUM(_h); while (l < sizeof(*v) * BDB_CON_COL_NUM(_h)) { v = (bdb_val_p)p; p += sizeof(*v); l += sizeof(*v); v->next = NULL; if (prev) { prev->next = v; prev = v; } else { prev = v; } if (!VAL_NULL(&(v->v))) { switch (VAL_TYPE(&(v->v))) { case DB_BLOB: case DB_STRING: case DB_STR: VAL_STR(&(v->v)).s = tail; tail += VAL_STR(&(v->v)).len; break; default: break; } } } return 0; }; int bdb_set_key(bdb_row_p _r, bdb_val_p _v) { /* NULL is not allowed for primary key */ if (VAL_NULL(&(_v->v))) { LOG(L_ERR, "BDB:bdb_set_key: NULL is not allowed for primary key\n"); return -1; } switch (VAL_TYPE(&(_v->v))) { case DB_INT: _r->key.data = &VAL_INT(&(_v->v)); _r->key.size = sizeof(VAL_INT(&(_v->v))); break; case DB_FLOAT: _r->key.data = &VAL_FLOAT(&(_v->v)); _r->key.size = sizeof(VAL_FLOAT(&(_v->v))); break; case DB_DATETIME: _r->key.data = &VAL_TIME(&(_v->v)); _r->key.size = sizeof(VAL_TIME(&(_v->v))); break; case DB_BLOB: _r->key.data = VAL_BLOB(&(_v->v)).s; _r->key.size = VAL_BLOB(&(_v->v)).len; break; case DB_DOUBLE: _r->key.data = &VAL_DOUBLE(&(_v->v)); _r->key.size = sizeof(VAL_DOUBLE(&(_v->v))); break; case DB_STRING: _r->key.data = (void *)VAL_STRING(&(_v->v)); _r->key.size = strlen(VAL_STRING(&(_v->v))) + 1; break; case DB_STR: _r->key.data = VAL_STR(&(_v->v)).s; _r->key.size = VAL_STR(&(_v->v)).len; break; case DB_BITMAP: _r->key.data = &VAL_BITMAP(&(_v->v)); _r->key.size = sizeof(VAL_BITMAP(&(_v->v))); break; default: LOG(L_ERR, "BDB:bdb_set_skey: unknown column type: %0X\n", VAL_TYPE(&(_v->v))); return -1; break; } return 0; }; kamailio-4.0.4/obsolete/bdb/bdb_rval.c0000644000000000000000000000450412223032460016262 0ustar rootroot/* $Id$ * * Copyright (C) 2006-2007 Sippy Software, Inc. * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "bdb.h" int bdb_rrow_db2bdb(db_con_t* _h, db_key_t* _k, int _n, bdb_rrow_p *_r) { bdb_rrow_p r; bdb_table_p t; bdb_column_p c; int found; int i; int c_idx; *_r = NULL; if ((t = bdb_find_table(CON_TABLE(_h))) == NULL) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_rrow_db2bdb: no table in use\n"); #endif return -1; }; i = (_n == 0) ? BDB_CON_COL_NUM(_h) : _n; r = pkg_malloc(sizeof(*r) * i); memset(r, 0, sizeof(*r) * i); if (_n > 0) { for (i = 0; i < _n; i++) { found = 0; for (c = t->cols, c_idx = 0; c != NULL; c = c->next, c_idx++) { if (!strcmp(_k[i], c->name.s)) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_rrow_db2bdb: filling column '%.*s', c_idx = %0d\n", c->name.len, c->name.s, c_idx); #endif r[i] = c_idx; found = 1; break; } } if (!found) { LOG(L_ERR, "BDB:bdb_rrow_db2bdb: column '%s' does not exist\n", _k[i]); bdb_free_rrow(r); return -1; } } } else { /* return all columns */ for (c = t->cols, c_idx = 0; c != NULL; c = c->next, c_idx++) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_rrow_db2bdb: filling column '%.*s', c_idx = %0d\n", c->name.len, c->name.s, c_idx); #endif r[c_idx] = c_idx; } } *_r = r; return 0; }; void bdb_free_rrow(bdb_rrow_p _r) { pkg_free(_r); }; kamailio-4.0.4/obsolete/bdb/bdb_api.h0000644000000000000000000000375412223032460016102 0ustar rootroot/* $Id$ * * Copyright (C) 2006-2007 Sippy Software, Inc. * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _BDB_API_H_ #define _BDB_API_H_ int bdb_open_table(db_con_t* _h, const char* _t); int bdb_use_table(db_con_t* _h, const char* _t); int bdb_close_table(db_con_t* _h); int bdb_describe_table(modparam_t type, void* val); bdb_table_p bdb_find_table(const char* _t); void bdb_push_table(bdb_table_p _t); void bdb_free_table(bdb_table_p _t); void bdb_free_table_list(bdb_table_p _t); bdb_column_p bdb_find_column(bdb_table_p _t, const char* _c); void bdb_push_column(bdb_table_p _t, bdb_column_p _c); void bdb_free_column(bdb_column_p _c); void bdb_free_column_list(bdb_column_p _c); int bdb_query_table(db_con_t* _h, bdb_srow_p s_r, bdb_rrow_p r_r, int _n, db_res_t** _r); int bdb_row_match(db_con_t* _h, bdb_val_p _v, bdb_srow_p s_r); int bdb_push_res_row(db_con_t* _h, db_res_t** _r, bdb_rrow_p _r_r, int _n, bdb_val_p _v); int bdb_update_table(db_con_t* _h, bdb_srow_p s_r, bdb_urow_p u_r); int bdb_delete_table(db_con_t* _h, bdb_srow_p s_r); #endif kamailio-4.0.4/obsolete/bdb/bdb_sval.c0000644000000000000000000001447612223032460016274 0ustar rootroot/* $Id$ * * Copyright (C) 2006-2007 Sippy Software, Inc. * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "bdb.h" int bdb_srow_db2bdb(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n, bdb_srow_p *_r) { bdb_srow_p r; bdb_table_p t; bdb_column_p c; int found; int use_key, found_key, key_idx; int i; int c_idx; bdb_sval_p v; *_r = NULL; if ((t = bdb_find_table(CON_TABLE(_h))) == NULL) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_srow_db2bdb: no table in use\n"); #endif return -1; }; key_idx = -1; use_key = 0; /* check if all columns exist */ for (i = 0; i < _n; i++) { found = 0; /* key column is always first one */ for (c = t->cols, found_key = 1; c != NULL; c = c->next, found_key = 0) { if (!strcmp(_k[i], c->name.s)) { found = 1; break; } } if (found_key == 1) { /* use key only if it is used in clause and operator is '=' */ if (!use_key && (_op == NULL || (_op != NULL && !strcmp(_op[i], OP_EQ)))) { key_idx = i; use_key = 1; } } if (!found) { LOG(L_ERR, "BDB:bdb_srow_db2bdb: column '%s' does not exist\n", _k[i]); return -1; } } r = pkg_malloc(sizeof(*r)); memset(r, 0, sizeof(*r)); /* filling data into row */ for (c = t->cols, c_idx = 0; c != NULL; c = c->next, c_idx++) { for (i = 0; i < _n; i++) { if (!strcmp(_k[i], c->name.s)) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_srow_db2bdb: filling column '%.*s', c_idx = %0d\n", c->name.len, c->name.s, c_idx); #endif v = pkg_malloc(sizeof(*v)); memset(v, 0, sizeof(*v)); v->c_idx = c_idx; bdb_push_sfield(r, v); if (bdb_sfield_db2bdb(v, &_v[i], (!_op) ? NULL : _op[i]) < 0) { bdb_free_srow(r); return -1; }; if (use_key && i == key_idx) { bdb_set_skey(r, v); } } } }; *_r = r; return 0; }; void bdb_free_srow(bdb_srow_p _r) { if (_r->fields != NULL) { bdb_free_sfield_list(_r->fields); } pkg_free(_r); }; void bdb_free_sfield(bdb_sval_p _v) { if (!VAL_NULL(&(_v->v))) { if (VAL_TYPE(&(_v->v)) == DB_STR || VAL_TYPE(&(_v->v)) == DB_STRING || VAL_TYPE(&(_v->v)) == DB_BLOB) { pkg_free(VAL_STR(&(_v->v)).s); } } pkg_free(_v); }; void bdb_free_sfield_list(bdb_sval_p _v) { bdb_sval_p curr, next; for (curr = _v; curr != NULL;) { next = curr->next; bdb_free_sfield(curr); curr = next; } }; void bdb_push_sfield(bdb_srow_p _r, bdb_sval_p _v) { bdb_sval_p f; if (_r->fields == NULL) { _r->fields = _v; return; } f = _r->fields; while (f->next != NULL) { f = f->next; } f->next = _v; }; void bdb_set_skey(bdb_srow_p _r, bdb_sval_p _v) { /* NULL is not allowed for primary key */ if (VAL_NULL(&(_v->v))) return; switch (VAL_TYPE(&(_v->v))) { case DB_INT: _r->key.data = &VAL_INT(&(_v->v)); _r->key.size = sizeof(VAL_INT(&(_v->v))); break; case DB_FLOAT: _r->key.data = &VAL_FLOAT(&(_v->v)); _r->key.size = sizeof(VAL_FLOAT(&(_v->v))); break; case DB_DATETIME: _r->key.data = &VAL_TIME(&(_v->v)); _r->key.size = sizeof(VAL_TIME(&(_v->v))); break; case DB_BLOB: _r->key.data = VAL_BLOB(&(_v->v)).s; _r->key.size = VAL_BLOB(&(_v->v)).len; break; case DB_DOUBLE: _r->key.data = &VAL_DOUBLE(&(_v->v)); _r->key.size = sizeof(VAL_DOUBLE(&(_v->v))); break; case DB_STRING: _r->key.data = (void *)VAL_STRING(&(_v->v)); _r->key.size = strlen(VAL_STRING(&(_v->v))) + 1; break; case DB_STR: _r->key.data = VAL_STR(&(_v->v)).s; _r->key.size = VAL_STR(&(_v->v)).len; break; case DB_BITMAP: _r->key.data = &VAL_BITMAP(&(_v->v)); _r->key.size = sizeof(VAL_BITMAP(&(_v->v))); break; default: LOG(L_ERR, "BDB:bdb_set_skey: unknown column type: %0X\n", VAL_TYPE(&(_v->v))); break; } #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_set_skey: use key '%.*s' (%d bytes)\n", _r->key.size, (char *)_r->key.data, _r->key.size); #endif }; int bdb_sfield_db2bdb(bdb_sval_p v, db_val_t* _v, db_op_t _op) { char *s; VAL_NULL(&(v->v)) = VAL_NULL(_v); VAL_TYPE(&(v->v)) = VAL_TYPE(_v); if (!VAL_NULL(&(v->v))) { switch (VAL_TYPE(_v)) { case DB_INT: VAL_INT(&(v->v)) = VAL_INT(_v); break; case DB_FLOAT: VAL_FLOAT(&(v->v)) = VAL_FLOAT(_v); break; case DB_DATETIME: VAL_TIME(&(v->v)) = VAL_TIME(_v); break; case DB_BLOB: s = pkg_malloc(VAL_BLOB(_v).len); memcpy(s, VAL_BLOB(_v).s, VAL_BLOB(_v).len); VAL_BLOB(&(v->v)).s = s; VAL_BLOB(&(v->v)).len = VAL_BLOB(_v).len; break; case DB_DOUBLE: VAL_DOUBLE(&(v->v)) = VAL_DOUBLE(_v); break; case DB_STRING: VAL_STR(&(v->v)).len = strlen(VAL_STRING(_v)) + 1; s = pkg_malloc(VAL_STR(&(v->v)).len); strcpy(s, VAL_STRING(_v)); VAL_STRING(&(v->v)) = s; break; case DB_STR: s = pkg_malloc(VAL_STR(_v).len); memcpy(s, VAL_STR(_v).s, VAL_STR(_v).len); VAL_STR(&(v->v)).s = s; VAL_STR(&(v->v)).len = VAL_STR(_v).len; break; case DB_BITMAP: VAL_BITMAP(&(v->v)) = VAL_BITMAP(_v); break; default: LOG(L_ERR, "BDB:bdb_sfield_db2bdb: unknown column type: %0X\n", VAL_TYPE(_v)); return -1; break; } if (!_op || !strcmp(_op, OP_EQ)) { v->op = BDB_OP_EQ; } else if (!strcmp(_op, OP_LT)) { v->op = BDB_OP_LT; } else if (!strcmp(_op, OP_GT)) { v->op = BDB_OP_GT; } else if (!strcmp(_op, OP_LEQ)) { v->op = BDB_OP_LEQ; } else if (!strcmp(_op, OP_GEQ)) { v->op = BDB_OP_GEQ; } else { LOG(L_ERR, "BDB:sbdb_field_db2bdb: unknown operator: %s\n", _op); return -1; } } return 0; }; kamailio-4.0.4/obsolete/bdb/doc/0000755000000000000000000000000012223032460015105 5ustar rootrootkamailio-4.0.4/obsolete/bdb/doc/bdb.xml0000644000000000000000000002357012223032460016365 0ustar rootroot
Sippy Software, Inc.
devel@sippysoft.com http://www.sippysoft.com
2006 Sippy Software, Inc.
BDB Module
Overview The SER (SIP Express Router) supports several different persistent storage backends (flatfile, dbtext and several SQL servers). However, in certain cases those existing backends don't satisfy conditions. Particularly, SQL server-based backends typically provide good performance and scalability, but at the same time require considerable efforts to configure and manage and also have significant memory and on-disk footprint, while simpler storage backends (flatfile, dbtext) are lighter and simpler to setup and manage, but scale poorly and don't provide sufficient performance. For certain types of applications (i.e. embedded SIP applications, SIP load balancing farms etc), different solution that would combine some of the non-overlapping properties of those two existing classes of backends is necessary. Berkeley DB is widely regarded as industry-leading open source, embeddable database engine that provides developers with fast, reliable, local persistence with almost zero administration.
Design of DBD Engine The dbtext database system architecture: A database is represented by a directory in the local file system where BDB environment is located. When using BDB driver in SER, the database URL for modules must be the path to the directory where the BDB environment is located, prefixed by "bdb://", e.g., "bdb:///var/db/ser". If there is no "/" after "bdb://" then "CFG_DIR/" (the OS-specific path defined at SER's compile time) is inserted at the beginning of the database path automatically. So that, either an absolute path to database directory, or one relative to "CFG_DIR" directory should be provided in the URL. The individual tables internaly are represented by binary files inside environment directory. The BDB driver uses BTree access method. On-disk storage format has been developed to be as simple as possible, while meeting performance requirements set forth above. It is not compatible with on-disk format of any other BDB-based DB engine (e.g. MySQL's BDB table handler).
Dependencies
External libraries or applications The next libraries or applications must be installed before running SER with this module: Berkeley DB 4.X
Exported parameters
<varname>describe_table</varname> (string) Define the table and its structure. Each table that will be used by other modules has to be defined. The format of the parameter is: table_name:column_name_1(column_type_1)[column_name_2(column_type_2)[... column_name_N(column_type_N)]] The names of table and columns must not include white spaces. The first column in definition is used as index. Between name of table and name of first column must be ":". Between two columns definitions must be exactly one white space. Type of column has to be enclosed into parentheses. Supported column types are: int float double string str datetime blob bitmap
Constrains and limitations Use of indexes: Effective SELECT, UPDATE and DELETE operations on a structured storage that contains any more or less decent number of records are impossible without using some kind of indexing scheme. Since Berkley DB is relatively simple storage engine it provides only basic support for indexing, nearly not as rich as usually expected by an average SQL user. Therefore, it has been decided to limit number of indexes supported to one per table. This is sufficient for most of the uses in the SER (for example: usrloc, auth_db etc). SELECT/UPDATE/DELETE records matching criteria. Due to its simplicity, Berkley DB only supports exact match on indexed field. In order to support <, >, <= and >= operations mandated by the SIP DB API it is necessary to fall back to sequental scan of the index values, which obviously has significant negative impact on performance. Fortunately those advanced records matching criterias are not required neither by the usrloc module nor by auth_db module. BDB driver uses index only if key column appears in search clause at least once and records matching operator associated with it is '='. It is not allowed to set index value to NULL or an empty string. It is not allowed to update index value. The DELETE followed by INSERT should be used instead. BDB driver does not support db_raw_query() method. BDB driver does not support ORDER BY clause of db_query() method.
Installation and running Compile the module and load it instead of mysql or other DB modules. Load the bdb module ... loadmodule "/path/to/ser/modules/dbb.so" ... modparam("module_name", "db_url", "bdb:///path/to/bdb/database") ... definition of the standard version table ... modparam("bdb", "describe_table", "version:table_name(str) table_version(int)") ...
Using BDB With Basic SER Configuration Here are the definitions for tables used by usrloc module as well as a part of basic configuration file to use BDB driver with SER. The table structures may change in future releases, so that some adjustment to example below may be necessary. That example corresponds to SER v0.9.4 According to the configuration file below, the table files will be placed in the //var/db/ser/ directory. The table version should be populated manually before the SER is started. To do this version.dump file located in the directotry of BDB driver sources and db_load utility from Berkeley BD distribution should be used as follows: Population of version table $ db_load -h /var/db/ser -f version.dump version Configuration file # ---------- global configuration parameters ------------------------ # [skip] # ---------- module loading ----------------------------------------- loadmodule "/usr/local/lib/ser/modules/usrloc.so" loadmodule "/usr/local/lib/ser/modules/bdb.so" # ---------- module-specific configuration parameteres -------------- modparam("usrloc", "db_mode", 2) modparam("usrloc", "timer_interval", 3) modparam("usrloc", "db_url", "bdb:///var/db/ser") modparam("bdb", "describe_table", "version:table_name(str) table_version(int)") modparam("bdb", "describe_table", "location:username(str) domain(str) contact(str) i_env(int) expires(datetime) q(double) callid(str) cseq(int) method(str) flags(int) user_agent(str) received(str)") modparam("bdb", "describe_table", "aliases:username(str) domain(str) contact(str) i_env(int) expires(datetime) q(double) callid(str) cseq(int) method(str) flags(int) user_agent(str) received(str)") # ---------- request routing logic ---------------------------------- # [skip]
kamailio-4.0.4/obsolete/bdb/doc/Makefile0000644000000000000000000000012412223032460016542 0ustar rootrootdocs = bdb.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/bdb/bdb_base.c0000644000000000000000000001601612223032460016231 0ustar rootroot/* $Id$ * * Copyright (C) 2006-2007 Sippy Software, Inc. * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "bdb.h" #define BDB_ID "bdb://" #define BDB_ID_LEN (sizeof(BDB_ID)-1) #define BDB_PATH_LEN MAXPATHLEN /* * Initialize database connection */ db_con_t* bdb_init(const char* _sqlurl) { db_con_t* _res; char *p; char bdb_path[BDB_PATH_LEN]; int ret; if (!_sqlurl) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_init: Invalid parameter value\n"); #endif return NULL; } p = (char *)_sqlurl; if(strncmp(p, BDB_ID, sizeof(BDB_ID) - 1)) { LOG(L_ERR, "BDB:bdb_init: invalid database URL - should be:" " <%s[/]path/to/directory>\n", BDB_ID); return NULL; } p += BDB_ID_LEN; if(p[0] != '/') { if(sizeof(CFG_DIR) + strlen(p) + 2 > BDB_PATH_LEN) { LOG(L_ERR, "BDB:bdb_init: path to database is too long\n"); return NULL; } strcpy(bdb_path, CFG_DIR); bdb_path[sizeof(CFG_DIR) - 1] = '/'; strcpy(&bdb_path[sizeof(CFG_DIR)], p); p = bdb_path; } #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_init: bdb_path = %s\n", p); #endif _res = pkg_malloc(sizeof(db_con_t) + sizeof(bdb_con_t)); if (!_res) { LOG(L_ERR, "BDB:bdb_init: No memory left\n"); return NULL; } memset(_res, 0, sizeof(db_con_t) + sizeof(bdb_con_t)); _res->tail = (unsigned long)((char*)_res + sizeof(bdb_con_t)); ret = db_env_create(&BDB_CON_DBENV(_res), 0); if (ret != 0) { LOG(L_ERR, "BDB:bdb_init: unable to db_env_create(): %s\n", db_strerror(ret)); pkg_free(_res); return NULL; } ret = BDB_CON_DBENV(_res)->open(BDB_CON_DBENV(_res), p, DB_CREATE | DB_INIT_MPOOL | DB_INIT_CDB, 0); if (ret != 0) { LOG(L_ERR, "BDB:bdb_init: unable to open environment: %s\n", db_strerror(ret)); pkg_free(_res); return NULL; } return _res; } /* * Close a database connection */ void bdb_close(db_con_t* _h) { if (!_h) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_close: Invalid parameter value\n"); #endif return; } if (BDB_CON_DB(_h) != NULL) { bdb_close_table(_h); } if (BDB_CON_DBENV(_h) != NULL) { BDB_CON_DBENV(_h)->close(BDB_CON_DBENV(_h), 0); } pkg_free(_h); return; } /* * Raw SQL query -- is not the case to have this method */ int bdb_raw_query(db_con_t* _h, char* _s, db_res_t** _r) { *_r = NULL; LOG(L_ERR, "BDB:bdb_raw_query: method is not supported.\n"); return -1; } /* * Query table for specified rows * _h: structure representing database connection * _k: key names * _op: operators * _v: values of the keys that must match * _c: column names to return * _n: number of key=values pairs to compare * _nc: number of columns to return * _o: order by the specified column */ int bdb_query(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc, db_key_t _o, db_res_t** _r) { bdb_srow_p s_r; /* search keys row */ bdb_rrow_p r_r; /* return keys row */ int ret; *_r = NULL; s_r = NULL; r_r = NULL; if (_o) { LOG(L_ERR, "BDB:bdb_query: ORDER BY is not supported.\n"); return -1; } if ((ret = bdb_srow_db2bdb(_h, _k, _op, _v, _n, &s_r)) < 0) { return -1; }; if ((ret = bdb_rrow_db2bdb(_h, _c, _nc, &r_r)) < 0) { bdb_free_srow(s_r); return -1; }; if ((ret = bdb_query_table(_h, s_r, r_r, _nc, _r)) < 0) { bdb_free_rrow(r_r); bdb_free_srow(s_r); return -1; }; bdb_free_rrow(r_r); bdb_free_srow(s_r); return 0; } /* * Insert a row into table */ int bdb_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n) { bdb_row_p r; DBC *cursorp; int ret; if ((ret = bdb_row_db2bdb(_h, _k, _v, _n, &r)) < 0) { return -1; }; if (r->key.size == 0) { LOG(L_ERR, "BDB:bdb_insert: no primary key specified\n"); bdb_free_row(r); return -1; } BDB_CON_DB(_h)->cursor(BDB_CON_DB(_h), NULL, &cursorp, DB_WRITECURSOR); ret = cursorp->c_put(cursorp, &(r->key), &(r->data), DB_KEYLAST); if (ret < 0) { LOG(L_ERR, "BDB:bdb_insert: unable to insert record: %s\n", db_strerror(ret)); } if (cursorp != NULL) { cursorp->c_close(cursorp); } bdb_free_row(r); return ret; } /* * Delete a row from table */ int bdb_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n) { bdb_srow_p s_r; /* search keys row */ int ret; s_r = NULL; if ((ret = bdb_srow_db2bdb(_h, _k, _o, _v, _n, &s_r)) < 0) { return -1; }; if ((ret = bdb_delete_table(_h, s_r)) < 0) { bdb_free_srow(s_r); return -1; }; bdb_free_srow(s_r); return 0; } /* * Update a row in table */ int bdb_update(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un) { bdb_srow_p s_r; /* search keys row */ bdb_urow_p u_r; /* update row */ int ret; s_r = NULL; u_r = NULL; if ((ret = bdb_srow_db2bdb(_h, _k, _o, _v, _n, &s_r)) < 0) { return -1; }; if ((ret = bdb_urow_db2bdb(_h, _uk, _uv, _un, &u_r)) < 0) { bdb_free_srow(s_r); return -1; }; if ((ret = bdb_update_table(_h, s_r, u_r)) < 0) { bdb_free_urow(u_r); bdb_free_srow(s_r); return -1; }; bdb_free_urow(u_r); bdb_free_srow(s_r); return 0; } /* * Free all memory allocated by get_result */ int bdb_free_result(db_con_t* _h, db_res_t* _r) { db_row_t* r; db_val_t* v; int i, j; if (!_r) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_free_result: NULL pointer\n"); #endif return 0; } for (i = 0; i < RES_ROW_N(_r); i++) { r = &(RES_ROWS(_r)[i]); for (j = 0; j < RES_COL_N(_r); j++) { v = &(ROW_VALUES(r)[j]); if (VAL_TYPE(v) == DB_STRING || VAL_TYPE(v) == DB_STR || VAL_TYPE(v) == DB_BLOB) { free(VAL_STR(v).s); } } free(ROW_VALUES(r)); } free(RES_ROWS(_r)); for (i = 0; i < RES_COL_N(_r); i++) { pkg_free((void *)RES_NAMES(_r)[i]); } pkg_free(RES_NAMES(_r)); pkg_free(RES_TYPES(_r)); pkg_free(_r); return 0; } kamailio-4.0.4/obsolete/bdb/bdb_res.h0000644000000000000000000000515712223032460016121 0ustar rootroot/* $Id$ * * Copyright (C) 2006-2007 Sippy Software, Inc. * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _BDB_RES_H_ #define _BDB_RES_H_ typedef struct _bdb_con { DB_ENV *dbenvp; /* Env structure handle */ DB *dbp; /* DB structure handle */ int col_num; /* number of columns */ } bdb_con_t, *bdb_con_p; #define BDB_CON_DBENV(db_con) (((bdb_con_p)((db_con)->tail))->dbenvp) #define BDB_CON_DB(db_con) (((bdb_con_p)((db_con)->tail))->dbp) #define BDB_CON_COL_NUM(db_con) (((bdb_con_p)((db_con)->tail))->col_num) /* * */ typedef struct _bdb_val_t { db_val_t v; struct _bdb_val_t *next; } bdb_val_t, *bdb_val_p; typedef struct _bdb_row { DBT key; DBT data; str tail; bdb_val_p fields; struct _bdb_row *next; } bdb_row_t, *bdb_row_p; /* * */ typedef struct _bdb_uval_t { int c_idx; /* column index number */ db_val_t v; struct _bdb_uval_t *next; } bdb_uval_t, *bdb_uval_p; typedef struct _bdb_urow { bdb_uval_p fields; } bdb_urow_t, *bdb_urow_p; /* * */ typedef struct _bdb_sval_t { int c_idx; /* column index number */ db_val_t v; int op; #define BDB_OP_EQ 0 #define BDB_OP_LT 1 #define BDB_OP_GT 2 #define BDB_OP_LEQ 3 #define BDB_OP_GEQ 4 struct _bdb_sval_t *next; } bdb_sval_t, *bdb_sval_p; typedef struct _bdb_srow { DBT key; bdb_sval_p fields; } bdb_srow_t, *bdb_srow_p; /* * */ typedef int bdb_rrow_t, *bdb_rrow_p; /* * */ typedef struct _bdb_column { str name; int type; struct _bdb_column *next; } bdb_column_t, *bdb_column_p; typedef struct _bdb_table { str name; int col_num; /* number of columns */ bdb_column_p cols; struct _bdb_table *next; } bdb_table_t, *bdb_table_p; #endif kamailio-4.0.4/obsolete/bdb/README0000644000000000000000000001644012223032460015225 0ustar rootroot1. BDB Module Sippy Software, Inc. Copyright © 2006 Sippy Software, Inc. __________________________________________________________________ 1.1. Overview 1.1.1. Design of DBD Engine 1.2. Dependencies 1.2.1. External libraries or applications 1.3. Exported parameters 1.3.1. describe_table (string) 1.4. Constrains and limitations 1.5. Installation and running 1.5.1. Using BDB With Basic SER Configuration 1.1. Overview The SER (SIP Express Router) supports several different persistent storage backends (flatfile, dbtext and several SQL servers). However, in certain cases those existing backends don't satisfy conditions. Particularly, SQL server-based backends typically provide good performance and scalability, but at the same time require considerable efforts to configure and manage and also have significant memory and on-disk footprint, while simpler storage backends (flatfile, dbtext) are lighter and simpler to setup and manage, but scale poorly and don't provide sufficient performance. For certain types of applications (i.e. embedded SIP applications, SIP load balancing farms etc), different solution that would combine some of the non-overlapping properties of those two existing classes of backends is necessary. Berkeley DB is widely regarded as industry-leading open source, embeddable database engine that provides developers with fast, reliable, local persistence with almost zero administration. 1.1.1. Design of DBD Engine The dbtext database system architecture: * A database is represented by a directory in the local file system where BDB environment is located. Note When using BDB driver in SER, the database URL for modules must be the path to the directory where the BDB environment is located, prefixed by "bdb://", e.g., "bdb:///var/db/ser". If there is no "/" after "bdb://" then "CFG_DIR/" (the OS-specific path defined at SER's compile time) is inserted at the beginning of the database path automatically. So that, either an absolute path to database directory, or one relative to "CFG_DIR" directory should be provided in the URL. * The individual tables internaly are represented by binary files inside environment directory. Note The BDB driver uses BTree access method. On-disk storage format has been developed to be as simple as possible, while meeting performance requirements set forth above. It is not compatible with on-disk format of any other BDB-based DB engine (e.g. MySQL's BDB table handler). 1.2. Dependencies 1.2.1. External libraries or applications The next libraries or applications must be installed before running SER with this module: * Berkeley DB 4.X 1.3. Exported parameters 1.3.1. describe_table (string) Define the table and its structure. Each table that will be used by other modules has to be defined. The format of the parameter is: table_name:column_name_1(column_type_1)[column_name_2(column_type_2)[.. . column_name_N(column_type_N)]] The names of table and columns must not include white spaces. The first column in definition is used as index. Between name of table and name of first column must be ":". Between two columns definitions must be exactly one white space. Type of column has to be enclosed into parentheses. Supported column types are: * int * float * double * string * str * datetime * blob * bitmap 1.4. Constrains and limitations Use of indexes: * Effective SELECT, UPDATE and DELETE operations on a structured storage that contains any more or less decent number of records are impossible without using some kind of indexing scheme. Since Berkley DB is relatively simple storage engine it provides only basic support for indexing, nearly not as rich as usually expected by an average SQL user. Therefore, it has been decided to limit number of indexes supported to one per table. This is sufficient for most of the uses in the SER (for example: usrloc, auth_db etc). * SELECT/UPDATE/DELETE records matching criteria. Due to its simplicity, Berkley DB only supports exact match on indexed field. In order to support <, >, <= and >= operations mandated by the SIP DB API it is necessary to fall back to sequental scan of the index values, which obviously has significant negative impact on performance. Fortunately those advanced records matching criterias are not required neither by the usrloc module nor by auth_db module. * BDB driver uses index only if key column appears in search clause at least once and records matching operator associated with it is '='. * It is not allowed to set index value to NULL or an empty string. * It is not allowed to update index value. The DELETE followed by INSERT should be used instead. BDB driver does not support db_raw_query() method. BDB driver does not support ORDER BY clause of db_query() method. 1.5. Installation and running Compile the module and load it instead of mysql or other DB modules. Example 1. Load the bdb module ... loadmodule "/path/to/ser/modules/dbb.so" ... modparam("module_name", "db_url", "bdb:///path/to/bdb/database") ... Example 2. definition of the standard version table ... modparam("bdb", "describe_table", "version:table_name(str) table_version(int)") ... 1.5.1. Using BDB With Basic SER Configuration Here are the definitions for tables used by usrloc module as well as a part of basic configuration file to use BDB driver with SER. The table structures may change in future releases, so that some adjustment to example below may be necessary. That example corresponds to SER v0.9.4 According to the configuration file below, the table files will be placed in the //var/db/ser/ directory. The table version should be populated manually before the SER is started. To do this version.dump file located in the directotry of BDB driver sources and db_load utility from Berkeley BD distribution should be used as follows: Example 3. Population of version table $ db_load -h /var/db/ser -f version.dump version Example 4. Configuration file # ---------- global configuration parameters ------------------------ # [skip] # ---------- module loading ----------------------------------------- loadmodule "/usr/local/lib/ser/modules/usrloc.so" loadmodule "/usr/local/lib/ser/modules/bdb.so" # ---------- module-specific configuration parameteres -------------- modparam("usrloc", "db_mode", 2) modparam("usrloc", "timer_interval", 3) modparam("usrloc", "db_url", "bdb:///var/db/ser") modparam("bdb", "describe_table", "version:table_name(str) table_version(int)") modparam("bdb", "describe_table", "location:username(str) domain(str) contact(st r) i_env(int) expires(datetime) q(double) callid(str) cseq(int) method(str) flag s(int) user_agent(str) received(str)") modparam("bdb", "describe_table", "aliases:username(str) domain(str) contact(str ) i_env(int) expires(datetime) q(double) callid(str) cseq(int) method(str) flags (int) user_agent(str) received(str)") # ---------- request routing logic ---------------------------------- # [skip] kamailio-4.0.4/obsolete/bdb/bdb_vals.h0000644000000000000000000000504312223032460016267 0ustar rootroot/* $Id$ * * Copyright (C) 2006-2007 Sippy Software, Inc. * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _BDB_VALS_H_ #define _BDB_VALS_H_ /* table row */ int bdb_row_db2bdb(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n, bdb_row_p *_r); void bdb_free_row(bdb_row_p _r); void bdb_free_row_list(bdb_row_p _r); int bdb_set_key(bdb_row_p _r, bdb_val_p _v); void bdb_push_field(bdb_row_p _r, bdb_val_p _v); void bdb_free_field(bdb_val_p _v); void bdb_free_field_list(bdb_val_p _v); void bdb_push_data(bdb_row_p _r, bdb_val_p _v); void bdb_merge_tail(bdb_row_p _r); int bdb_field_db2bdb(bdb_val_p v, db_val_t* _v); /* search row */ int bdb_srow_db2bdb(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n, bdb_srow_p *_r); void bdb_free_srow(bdb_srow_p _r); void bdb_set_skey(bdb_srow_p _r, bdb_sval_p _v); void bdb_push_sfield(bdb_srow_p _r, bdb_sval_p _v); void bdb_free_sfield(bdb_sval_p _v); void bdb_free_sfield_list(bdb_sval_p _v); int bdb_sfield_db2bdb(bdb_sval_p v, db_val_t* _v, db_op_t _op); /* result row */ int bdb_rrow_db2bdb(db_con_t* _h, db_key_t* _k, int _n, bdb_rrow_p *_r); void bdb_free_rrow(bdb_rrow_p _r); /* update row */ int bdb_urow_db2bdb(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n, bdb_urow_p *_r); void bdb_free_urow(bdb_urow_p _r); void bdb_push_ufield(bdb_urow_p _r, bdb_uval_p _v); void bdb_free_ufield(bdb_uval_p _v); void bdb_free_ufield_list(bdb_uval_p _v); int bdb_ufield_db2bdb(bdb_uval_p v, db_val_t* _v); int bdb_set_row(db_con_t* _h, bdb_urow_p u_r, bdb_val_p _v, bdb_row_p _r); /* query */ int bdb_get_db_row(db_con_t* _h, DBT* _data, bdb_val_p *_v); #endif kamailio-4.0.4/obsolete/bdb/bdb.h0000644000000000000000000000422112223032460015237 0ustar rootroot/* $Id$ * * Copyright (C) 2006-2007 Sippy Software, Inc. * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _BDB_H_ #define _BDB_H_ #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../str.h" #include "../../timer.h" #include "../../lib/srdb2/db_con.h" #include "../../lib/srdb2/db_res.h" #include "../../db/db_key.h" #include "../../db/db_op.h" #include "../../db/db_val.h" #include "bdb_res.h" #include "bdb_api.h" #include "bdb_vals.h" extern bdb_table_p bdb_tables; db_con_t* bdb_init(const char* _sqlurl); void bdb_close(db_con_t* _h); int bdb_free_result(db_con_t* _h, db_res_t* _r); int bdb_query(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc, db_key_t _o, db_res_t** _r); int bdb_raw_query(db_con_t* _h, char* _s, db_res_t** _r); int bdb_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n); int bdb_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n); int bdb_update(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un); #endif kamailio-4.0.4/obsolete/bdb/bdb.c0000644000000000000000000000533612223032460015242 0ustar rootroot/* $Id$ * * Copyright (C) 2006-2007 Sippy Software, Inc. * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "bdb.h" MODULE_VERSION static int mod_init(void); static int child_init(int); static void mod_destroy(void); /* * Exported functions */ static cmd_export_t cmds[] = { {"db_use_table", (cmd_function)bdb_use_table, 2, 0, 0}, {"db_init", (cmd_function)bdb_init, 1, 0, 0}, {"db_close", (cmd_function)bdb_close, 2, 0, 0}, {"db_query", (cmd_function)bdb_query, 2, 0, 0}, {"db_raw_query", (cmd_function)bdb_raw_query, 2, 0, 0}, {"db_free_result", (cmd_function)bdb_free_result, 2, 0, 0}, {"db_insert", (cmd_function)bdb_insert, 2, 0, 0}, {"db_delete", (cmd_function)bdb_delete, 2, 0, 0}, {"db_update", (cmd_function)bdb_update, 2, 0, 0}, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"describe_table", STR_PARAM|USE_FUNC_PARAM, (void*)bdb_describe_table}, {0, 0, 0} }; struct module_exports exports = { "bdb", cmds, /* Exported functions */ 0, /* RPC method */ params, /* Exported parameters */ mod_init, /* module initialization function */ 0, /* response function*/ mod_destroy, /* destroy function */ 0, /* oncancel function */ child_init /* per-child init function */ }; bdb_table_p bdb_tables = NULL; static int mod_init(void) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:init...\n"); #endif return 0; } static int child_init(int rank) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:child_init: rank = %d\n", rank); #endif return 0; } static void mod_destroy(void) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:destroy...\n"); #endif if (bdb_tables != NULL) { bdb_free_table_list(bdb_tables); bdb_tables = NULL; } } kamailio-4.0.4/obsolete/bdb/Makefile0000644000000000000000000000076112223032460016004 0ustar rootroot# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile # extra debug messages #DEFS+=-DBDB_EXTRA_DEBUG include ../../Makefile.defs auto_gen= NAME=bdb.so # db.h locations #DEFS += -I$(LOCALBASE)/include/db41 #LIBS = -L$(LOCALBASE)/lib -ldb41 DEFS += -I$(LOCALBASE)/include/db44 LIBS = -L$(LOCALBASE)/lib -ldb-4.4 DEFS+=-DSER_MOD_INTERFACE SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/bdb/version.dump0000644000000000000000000000054712223032460016722 0ustar rootrootVERSION=3 format=print type=btree duplicates=1 db_pagesize=16384 HEADER=END aliases \04\00\00\00\00\00\00\00\1cB\13\08\07\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\09\00\00\00\00\00\00\00\00\00\00\00aliases location \04\00\00\00\00\00\00\00\d0A\13\08\08\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\09\00\00\00\00\00\00\00\00\00\00\00location DATA=END kamailio-4.0.4/obsolete/bdb/bdb_api.c0000644000000000000000000004762312223032460016100 0ustar rootroot/* $Id$ * * Copyright (C) 2006-2007 Sippy Software, Inc. * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "bdb.h" int bdb_close_table(db_con_t* _h) { /* close DB */ if (BDB_CON_DB(_h) == NULL) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_close_table: no need to close\n"); #endif return 0; #ifdef BDB_EXTRA_DEBUG } else { LOG(L_NOTICE, "BDB:bdb_close_table: '%s'\n", CON_TABLE(_h)); #endif }; BDB_CON_DB(_h)->close(BDB_CON_DB(_h), 0); CON_TABLE(_h) = NULL; BDB_CON_DB(_h) = NULL; return 0; }; int bdb_open_table(db_con_t* _h, const char* _t) { int ret; bdb_table_p t; if ((t = bdb_find_table(_t)) == NULL) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_open_table: table: '%s' has not been described\n", _t); #endif return -1; } BDB_CON_COL_NUM(_h) = t->col_num; CON_TABLE(_h) = t->name.s; #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_open_table: '%s'\n", CON_TABLE(_h)); #endif ret = db_create(&BDB_CON_DB(_h), BDB_CON_DBENV(_h), 0); if (ret != 0) { LOG(L_ERR, "BDB:bdb_open_table: unable to db_create(): %s\n", db_strerror(ret)); return -1; }; ret = BDB_CON_DB(_h)->set_flags(BDB_CON_DB(_h), DB_DUP); if (ret != 0) { LOG(L_ERR, "BDB:bdb_open_table: unable to set_flags(): %s\n", db_strerror(ret)); return -1; } ret = BDB_CON_DB(_h)->open(BDB_CON_DB(_h), NULL, CON_TABLE(_h), NULL, DB_BTREE, DB_CREATE, 0); if (ret != 0) { LOG(L_ERR, "BDB:bdb_open_table: unable to open database '%s': %s\n", CON_TABLE(_h), db_strerror(ret)); return -1; }; return 0; }; int bdb_use_table(db_con_t* _h, const char* _t) { int ret; if ((!_h) || (!_t)) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_use_table: Invalid parameter value\n"); #endif return -1; } if (CON_TABLE(_h) != NULL && !strcmp(CON_TABLE(_h), _t)) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_use_table: table '%s' has been already opened\n", _t); #endif return 0; } /* close table if one was already opened */ if (CON_TABLE(_h) != NULL) { bdb_close_table(_h); } ret = bdb_open_table(_h, _t); return ret; } int bdb_describe_table(modparam_t type, void* val) { char *s, *p; bdb_table_p t; bdb_column_p c; #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_describe_table: input string: '%s'\n", (char*)val); #endif s = (char*) val; p = strchr(s, ':'); *p = 0; if (bdb_find_table(s) != NULL) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_describe_table: table: '%s' already has been described\n", s); #endif return -1; } t = pkg_malloc(sizeof(*t)); memset(t, 0, sizeof(*t)); t->name.s = pkg_malloc(strlen(s) + 1); memcpy(t->name.s, s, strlen(s) + 1); t->name.len = strlen(s) + 1; #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_describe_table: table: '%.*s'\n", t->name.len, t->name.s); #endif bdb_push_table(t); s = p + 1; while ((p = strchr(s, '(')) != NULL) { *p = 0; if (bdb_find_column(t, s) != NULL) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_describe_table: table: '%.*s': dublicated column: '%s', \n", t->name.len, t->name.s, s); #endif return -1; } c = pkg_malloc(sizeof(*c)); memset(c, 0, sizeof(*c)); c->name.s = pkg_malloc(strlen(s) + 1); memcpy(c->name.s, s, strlen(s) + 1); c->name.len = strlen(s) + 1; #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_describe_table: column '%.*s'", c->name.len, c->name.s); #endif bdb_push_column(t, c); t->col_num++; s = ++p; p = strchr(s, ')'); *p = 0; #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, ", type: '%s'\n", s); #endif if (!strncmp("int", s, strlen(s))) { c->type = DB_INT; } else if (!strncmp("float", s, strlen(s))) { c->type = DB_FLOAT; } else if (!strncmp("double", s, strlen(s))) { c->type = DB_DOUBLE; } else if (!strncmp("string", s, strlen(s))) { c->type = DB_STRING; } else if (!strncmp("str", s, strlen(s))) { c->type = DB_STR; } else if (!strncmp("datetime", s, strlen(s))) { c->type = DB_DATETIME; } else if (!strncmp("blob", s, strlen(s))) { c->type = DB_BLOB; } else if (!strncmp("bitmap", s, strlen(s))) { c->type = DB_BITMAP; } else { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_describe_table: bad column type: '%s'\n", s); #endif return -1; } s = ++p; if ((p = strchr(s, ' ')) != NULL) s++; } return 0; }; void bdb_push_table(bdb_table_p _t) { bdb_table_p t; if (bdb_tables == NULL) { bdb_tables = _t; return; } t = bdb_tables; while (t->next != NULL) { t = t->next; } t->next = _t; }; void bdb_free_table(bdb_table_p _t) { if (_t->name.s) { pkg_free(_t->name.s); } if (_t->cols != NULL) { bdb_free_column_list(_t->cols); } pkg_free(_t); }; void bdb_free_table_list(bdb_table_p _t) { bdb_table_p curr, next; #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_free_table_list\n"); #endif for (curr = _t; curr != NULL;) { next = curr->next; bdb_free_table(curr); curr = next; } }; bdb_table_p bdb_find_table(const char* _t) { bdb_table_p t; for (t = bdb_tables; t != NULL; t = t->next) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_find_table: search for '%s', found '%s'\n", _t, t->name.s); #endif if (!strcmp(_t, t->name.s)) return t; } return NULL; }; void bdb_free_column(bdb_column_p _c) { if (_c->name.s) { pkg_free(_c->name.s); } pkg_free(_c); }; void bdb_free_column_list(bdb_column_p _c) { bdb_column_p curr, next; for (curr = _c; curr != NULL;) { next = curr->next; bdb_free_column(curr); curr = next; } }; bdb_column_p bdb_find_column(bdb_table_p _t, const char* _c) { bdb_column_p c; for (c = _t->cols; c != NULL; c = c->next) { if (!strcmp(_c, c->name.s)) return c; } return NULL; }; void bdb_push_column(bdb_table_p _t, bdb_column_p _c) { bdb_column_p c; if (_t->cols == NULL) { _t->cols = _c; return; } c = _t->cols; while (c->next != NULL) { c = c->next; } c->next = _c; }; int bdb_update_table(db_con_t* _h, bdb_srow_p s_r, bdb_urow_p u_r) { DBC *cursorp; DBT key, *keyp, data; u_int32_t flags, nflags; bdb_val_p v; bdb_row_p r; int ret; if (s_r->key.size > 0) { keyp = &(s_r->key); flags = DB_SET; nflags = DB_NEXT_DUP; } else { memset(&key, 0, sizeof(DBT)); keyp = &key; flags = DB_NEXT; nflags = DB_NEXT; } memset(&data, 0, sizeof(DBT)); r = pkg_malloc(sizeof(*r)); memset(r, 0, sizeof(*r)); BDB_CON_DB(_h)->cursor(BDB_CON_DB(_h), NULL, &cursorp, DB_WRITECURSOR); ret = cursorp->c_get(cursorp, keyp, &data, flags); while (ret == 0) { if ((ret = bdb_get_db_row(_h, &data, &v)) < 0) { if (cursorp != NULL) cursorp->c_close(cursorp); bdb_free_row(r); return -1; }; /* row content now in v */ ret = bdb_row_match(_h, v, s_r); if (ret < 0) { if (cursorp != NULL) cursorp->c_close(cursorp); bdb_free_row(r); return -1; } else if (ret) { /* match */ if (bdb_set_row(_h, u_r, v, r) < 0) { if (cursorp != NULL) cursorp->c_close(cursorp); bdb_free_row(r); return -1; }; ret = cursorp->c_put(cursorp, keyp, &(r->data), DB_CURRENT); if (ret != 0) { LOG(L_ERR, "BDB:bdb_update_table: c_put(): %s\n", db_strerror(ret)); if (cursorp != NULL) cursorp->c_close(cursorp); bdb_free_row(r); return -1; } if (r->data.data != NULL) { pkg_free(r->data.data); } if (r->tail.s != NULL) { pkg_free(r->tail.s); } if (r->fields != NULL) { bdb_free_field_list(r->fields); } memset(r, 0, sizeof(*r)); #ifdef BDB_EXTRA_DEBUG } else { LOG(L_NOTICE, "BDB:bdb_update_table: does not match\n"); #endif }; ret = cursorp->c_get(cursorp, keyp, &data, nflags); } if (ret != DB_NOTFOUND) { LOG(L_ERR, "BDB:bdb_update_table: %s\n", db_strerror(ret)); if (cursorp != NULL) cursorp->c_close(cursorp); bdb_free_row(r); return -1; } if (cursorp != NULL) cursorp->c_close(cursorp); bdb_free_row(r); return 0; }; int bdb_query_table(db_con_t* _h, bdb_srow_p s_r, bdb_rrow_p r_r, int _n, db_res_t** _r) { bdb_table_p t; bdb_column_p c; int i, j; DBC *cursorp; DBT key, *keyp, data; u_int32_t flags, nflags; bdb_val_p v; db_res_t *res; int ret; if (s_r->key.size > 0) { keyp = &(s_r->key); flags = DB_SET; nflags = DB_NEXT_DUP; } else { memset(&key, 0, sizeof(DBT)); keyp = &key; flags = DB_NEXT; nflags = DB_NEXT; } memset(&data, 0, sizeof(DBT)); /* prepare result */ res = pkg_malloc(sizeof(*res)); memset(res, 0, sizeof(*res)); *_r = res; res->col.n = (_n == 0) ? BDB_CON_COL_NUM(_h) : _n; t = bdb_find_table(CON_TABLE(_h)); if (_n == 0) { /* return all columns */ res->col.names = pkg_malloc(sizeof(db_key_t) * t->col_num); res->col.types = pkg_malloc(sizeof(db_type_t) * t->col_num); for (c = t->cols, i = 0; c != NULL; c = c->next, i++) { res->col.names[i] = pkg_malloc(c->name.len); memcpy((void *)res->col.names[i], (void *)c->name.s, c->name.len); res->col.types[i] = c->type; } } else { res->col.names = pkg_malloc(sizeof(db_key_t) * _n); res->col.types = pkg_malloc(sizeof(db_type_t) * _n); for (i = 0; i < _n; i++) { for (c = t->cols, j = 0; j < r_r[i]; c = c->next, j++); res->col.names[i] = pkg_malloc(c->name.len); memcpy((void *)res->col.names[i], (void *)c->name.s, c->name.len); res->col.types[i] = c->type; } } BDB_CON_DB(_h)->cursor(BDB_CON_DB(_h), NULL, &cursorp, 0); ret = cursorp->c_get(cursorp, keyp, &data, flags); while (ret == 0) { if ((ret = bdb_get_db_row(_h, &data, &v)) < 0) { if (cursorp != NULL) cursorp->c_close(cursorp); bdb_free_result(_h, *_r); return -1; }; /* row content now in v */ ret = bdb_row_match(_h, v, s_r); if (ret < 0) { if (cursorp != NULL) cursorp->c_close(cursorp); bdb_free_result(_h, *_r); return -1; } else if (ret) { /* match */ if (bdb_push_res_row(_h, _r, r_r, _n, v) < 0) { if (cursorp != NULL) cursorp->c_close(cursorp); bdb_free_result(_h, *_r); return -1; }; #ifdef BDB_EXTRA_DEBUG } else { LOG(L_NOTICE, "BDB:bdb_query_table: does not match\n"); #endif }; ret = cursorp->c_get(cursorp, keyp, &data, nflags); } if (ret != DB_NOTFOUND) { LOG(L_ERR, "BDB:bdb_query_table: %s\n", db_strerror(ret)); if (cursorp != NULL) cursorp->c_close(cursorp); bdb_free_result(_h, *_r); return -1; } if (cursorp != NULL) cursorp->c_close(cursorp); return 0; } int bdb_row_match(db_con_t* _h, bdb_val_p _v, bdb_srow_p s_r) { bdb_sval_p s_v; db_val_t *v, *v2; int op, l; s_v = s_r->fields; while (s_v != NULL) { v = &(s_v->v); /* row field value*/ v2 = &(_v[s_v->c_idx].v); /* compared value */ op = s_v->op; /* expression operator */ if (VAL_TYPE(v) != VAL_TYPE(v2)) { LOG(L_ERR, "BDB:bdb_row_match: types mismatch: %d vs %d\n", VAL_TYPE(v), VAL_TYPE(v2)); return -1; }; if (VAL_NULL(v) && VAL_NULL(v) == VAL_NULL(v2)) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_row_match: NULL == NULL\n"); #endif return 1; }; if (VAL_NULL(v) != VAL_NULL(v2)) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_row_match: NULL != NULL\n"); #endif return 0; }; switch (VAL_TYPE(v)) { case DB_INT: #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_row_match: %d vs %d\n", VAL_INT(v), VAL_INT(v2)); #endif switch (op) { case BDB_OP_EQ: if (VAL_INT(v) != VAL_INT(v2)) return 0; break; case BDB_OP_LT: if (VAL_INT(v) >= VAL_INT(v2)) return 0; break; case BDB_OP_GT: if (VAL_INT(v) <= VAL_INT(v2)) return 0; break; case BDB_OP_LEQ: if (VAL_INT(v) > VAL_INT(v2)) return 0; break; case BDB_OP_GEQ: if (VAL_INT(v) < VAL_INT(v2)) return 0; break; } break; case DB_FLOAT: #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_row_match: %f vs %f\n", VAL_FLOAT(v), VAL_FLOAT(v2)); #endif switch (op) { case BDB_OP_EQ: if (VAL_FLOAT(v) != VAL_FLOAT(v2)) return 0; break; case BDB_OP_LT: if (VAL_FLOAT(v) >= VAL_FLOAT(v2)) return 0; break; case BDB_OP_GT: if (VAL_FLOAT(v) <= VAL_FLOAT(v2)) return 0; break; case BDB_OP_LEQ: if (VAL_FLOAT(v) > VAL_FLOAT(v2)) return 0; break; case BDB_OP_GEQ: if (VAL_FLOAT(v) < VAL_FLOAT(v2)) return 0; break; } break; case DB_DATETIME: #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_row_match: %d vs %d\n", VAL_TIME(v), VAL_TIME(v2)); #endif switch (op) { case BDB_OP_EQ: if (VAL_TIME(v) != VAL_TIME(v2)) return 0; break; case BDB_OP_LT: if (VAL_TIME(v) >= VAL_TIME(v2)) return 0; break; case BDB_OP_GT: if (VAL_TIME(v) <= VAL_TIME(v2)) return 0; break; case BDB_OP_LEQ: if (VAL_TIME(v) > VAL_TIME(v2)) return 0; break; case BDB_OP_GEQ: if (VAL_TIME(v) < VAL_TIME(v2)) return 0; break; } break; case DB_DOUBLE: #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_row_match: %f vs %f\n", VAL_DOUBLE(v), VAL_DOUBLE(v2)); #endif switch (op) { case BDB_OP_EQ: if (VAL_DOUBLE(v) != VAL_DOUBLE(v2)) return 0; break; case BDB_OP_LT: if (VAL_DOUBLE(v) >= VAL_DOUBLE(v2)) return 0; break; case BDB_OP_GT: if (VAL_DOUBLE(v) <= VAL_DOUBLE(v2)) return 0; break; case BDB_OP_LEQ: if (VAL_DOUBLE(v) > VAL_DOUBLE(v2)) return 0; break; case BDB_OP_GEQ: if (VAL_DOUBLE(v) < VAL_DOUBLE(v2)) return 0; break; } break; case DB_BITMAP: #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_row_match: %0X vs %0X\n", VAL_BITMAP(v), VAL_BITMAP(v2)); #endif switch (op) { case BDB_OP_EQ: if (VAL_BITMAP(v) != VAL_BITMAP(v2)) return 0; break; case BDB_OP_LT: if (VAL_BITMAP(v) >= VAL_BITMAP(v2)) return 0; break; case BDB_OP_GT: if (VAL_BITMAP(v) <= VAL_BITMAP(v2)) return 0; break; case BDB_OP_LEQ: if (VAL_BITMAP(v) > VAL_BITMAP(v2)) return 0; break; case BDB_OP_GEQ: if (VAL_BITMAP(v) < VAL_BITMAP(v2)) return 0; break; } break; case DB_STRING: #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_row_match: %s vs %s\n", VAL_STRING(v), VAL_STRING(v2)); #endif switch (op) { case BDB_OP_EQ: if (strcmp(VAL_STRING(v), VAL_STRING(v2))) return 0; break; case BDB_OP_LT: if (strcmp(VAL_STRING(v), VAL_STRING(v2)) >= 0) return 0; break; case BDB_OP_GT: if (strcmp(VAL_STRING(v), VAL_STRING(v2)) <= 0) return 0; break; case BDB_OP_LEQ: if (strcmp(VAL_STRING(v), VAL_STRING(v2)) > 0) return 0; break; case BDB_OP_GEQ: if (strcmp(VAL_STRING(v), VAL_STRING(v2)) < 0) return 0; break; } break; case DB_STR: #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_row_match: %.*s vs %.*s\n", VAL_STR(v).len, VAL_STR(v).s, VAL_STR(v2).len, VAL_STR(v2).s); #endif l = VAL_STR(v).len > VAL_STR(v2).len ? VAL_STR(v).len : VAL_STR(v2).len; switch (op) { case BDB_OP_EQ: if (strncmp(VAL_STR(v).s, VAL_STR(v2).s, l)) return 0; break; case BDB_OP_LT: if (strncmp(VAL_STR(v).s, VAL_STR(v2).s, l) >= 0) return 0; break; case BDB_OP_GT: if (strncmp(VAL_STR(v).s, VAL_STR(v2).s, l) <= 0) return 0; break; case BDB_OP_LEQ: if (strncmp(VAL_STR(v).s, VAL_STR(v2).s, l) > 0) return 0; break; case BDB_OP_GEQ: if (strncmp(VAL_STR(v).s, VAL_STR(v2).s, l) < 0) return 0; break; } break; case DB_BLOB: #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_row_match: %.*s (len = %d) vs %.*s (len = %d)\n", VAL_BLOB(v).len, VAL_BLOB(v).s, VAL_BLOB(v).len, VAL_BLOB(v2).len, VAL_BLOB(v2).s, VAL_BLOB(v2).len); #endif l = VAL_BLOB(v).len > VAL_BLOB(v2).len ? VAL_BLOB(v).len : VAL_BLOB(v2).len; switch (op) { case BDB_OP_EQ: if (memcmp(VAL_BLOB(v).s, VAL_BLOB(v2).s, l)) return 0; break; case BDB_OP_LT: if (memcmp(VAL_BLOB(v).s, VAL_BLOB(v2).s, l) >= 0) return 0; break; case BDB_OP_GT: if (memcmp(VAL_BLOB(v).s, VAL_BLOB(v2).s, l) <= 0) return 0; break; case BDB_OP_LEQ: if (memcmp(VAL_BLOB(v).s, VAL_BLOB(v2).s, l) > 0) return 0; break; case BDB_OP_GEQ: if (memcmp(VAL_BLOB(v).s, VAL_BLOB(v2).s, l) < 0) return 0; break; } break; default: return -1; /* is it possible here? */ break; } s_v = s_v->next; } #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_row_match: match\n"); #endif return 1; /* match */ }; int bdb_push_res_row(db_con_t* _h, db_res_t** _r, bdb_rrow_p _r_r, int _n, bdb_val_p _v) { db_res_t *r; db_row_t *row; db_val_t *v; int i, n; char *s; r = *_r; /* * use system malloc() to allocate memory for RES_ROWS array * due to pkg_malloc() memory pool fragmentation problem */ if (RES_ROW_N(r) == 0) { if ((row = malloc(sizeof(*(RES_ROWS(r))))) == NULL) { LOG(L_ERR, "BDB:bdb_push_res_row: unable to allocate %d bytes\n", sizeof(*(RES_ROWS(r)))); return -1; }; } else { if ((row = realloc(RES_ROWS(r), sizeof(*(RES_ROWS(r))) * (RES_ROW_N(r) + 1))) == NULL) { LOG(L_ERR, "BDB:bdb_push_res_row: unable to reallocate %d bytes\n", sizeof(*(RES_ROWS(r))) * (RES_ROW_N(r) + 1)); return -1; }; } RES_ROWS(r) = row; row = &RES_ROWS(r)[RES_ROW_N(r)]; RES_ROW_N(r)++; n = (_n == 0) ? BDB_CON_COL_NUM(_h) : _n; ROW_VALUES(row) = malloc(sizeof(*(ROW_VALUES(row))) * n); if (ROW_VALUES(row) == NULL) { LOG(L_ERR, "BDB:bdb_push_res_row: unable to allocate %d bytes for ROW_VALUES\n", sizeof(*(ROW_VALUES(row))) * n); return -1; } for (i = 0; i < n; i++) { v = &ROW_VALUES(row)[i]; memcpy(v, &_v[_r_r[i]].v, sizeof(*v)); if (VAL_TYPE(v) == DB_STRING || VAL_TYPE(v) == DB_STR || VAL_TYPE(v) == DB_BLOB) { s = malloc(VAL_STR(v).len + 1); if (s == NULL) { LOG(L_ERR, "BDB:bdb_push_res_row: unable to allocate %d bytes for VAL_STR\n", VAL_STR(v).len); free(ROW_VALUES(row)); return -1; } memcpy(s, VAL_STR(v).s, VAL_STR(v).len); /* some code expect STR value to be NULL terminated */ s[VAL_STR(v).len] = 0; VAL_STR(v).s = s; } } ROW_N(row) = n; return 0; }; int bdb_delete_table(db_con_t* _h, bdb_srow_p s_r) { DBC *cursorp; DBT key, *keyp, data; u_int32_t flags, nflags; bdb_val_p v; int ret; if (s_r->key.size > 0) { keyp = &(s_r->key); flags = DB_SET; nflags = DB_NEXT_DUP; } else { memset(&key, 0, sizeof(DBT)); keyp = &key; flags = DB_NEXT; nflags = DB_NEXT; } memset(&data, 0, sizeof(DBT)); BDB_CON_DB(_h)->cursor(BDB_CON_DB(_h), NULL, &cursorp, DB_WRITECURSOR); ret = cursorp->c_get(cursorp, keyp, &data, flags); while (ret == 0) { if ((ret = bdb_get_db_row(_h, &data, &v)) < 0) { return -1; }; /* row content now in v */ ret = bdb_row_match(_h, v, s_r); if (ret < 0) { if (cursorp != NULL) cursorp->c_close(cursorp); return -1; } else if (ret) { /* match */ ret = cursorp->c_del(cursorp, 0); if (ret != 0) { LOG(L_ERR, "BDB:bdb_delete_table: c_del(): %s\n", db_strerror(ret)); if (cursorp != NULL) cursorp->c_close(cursorp); return -1; } #ifdef BDB_EXTRA_DEBUG } else { LOG(L_NOTICE, "BDB:bdb_delete_table: does not match\n"); #endif }; ret = cursorp->c_get(cursorp, keyp, &data, nflags); } if (ret != DB_NOTFOUND) { if (cursorp != NULL) cursorp->c_close(cursorp); LOG(L_ERR, "BDB:bdb_delete_table: %s\n", db_strerror(ret)); return -1; } if (cursorp != NULL) cursorp->c_close(cursorp); return 0; } kamailio-4.0.4/obsolete/bdb/bdb_uval.c0000644000000000000000000001150412223032460016263 0ustar rootroot/* $Id$ * * Copyright (C) 2006-2007 Sippy Software, Inc. * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "bdb.h" int bdb_urow_db2bdb(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n, bdb_urow_p *_r) { bdb_urow_p r; bdb_table_p t; bdb_column_p c; int found, found_key; int i, j; int c_idx; bdb_uval_p v; *_r = NULL; if (_n <= 0) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_urow_db2bdb: no defined keys to be updated\n"); #endif return -1; } if ((t = bdb_find_table(CON_TABLE(_h))) == NULL) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_urow_db2bdb: no table in use\n"); #endif return -1; }; /* check for dublicates in update set */ for (i = 1; i < _n; i++) { for (j = 0; j < i; j++) { if (!strcmp(_k[i], _k[j])) { #ifdef BDB_EXTRA_DEBUG LOG(L_ERR, "BDB:bdb_urow_db2bdb: dublicates keys in update set: '%s' and '%s'\n", _k[i], _k[j]); #endif return -1; } } } /* check if all columns exist */ for (i = 0; i < _n; i++) { found = 0; /* key column is always first one */ for (c = t->cols, found_key = 1; c != NULL; c = c->next, found_key = 0) { if (!strcmp(_k[i], c->name.s)) { found = 1; break; } } if (found_key == 1) { LOG(L_ERR, "BDB:bdb_urow_db2bdb: unable to update primary key value\n"); return -1; } if (!found) { LOG(L_ERR, "BDB:bdb_urow_db2bdb: column '%s' does not exist\n", _k[i]); return -1; } } r = pkg_malloc(sizeof(*r)); memset(r, 0, sizeof(*r)); /* filling data into row */ for (c = t->cols, c_idx = 0; c != NULL; c = c->next, c_idx++) { for (i = 0; i < _n; i++) { if (!strcmp(_k[i], c->name.s)) { #ifdef BDB_EXTRA_DEBUG LOG(L_NOTICE, "BDB:bdb_urow_db2bdb: filling column '%.*s', c_idx = %0d\n", c->name.len, c->name.s, c_idx); #endif v = pkg_malloc(sizeof(*v)); memset(v, 0, sizeof(*v)); v->c_idx = c_idx; bdb_push_ufield(r, v); if (bdb_ufield_db2bdb(v, &_v[i]) < 0) { bdb_free_urow(r); return -1; }; } } }; *_r = r; return 0; }; void bdb_free_urow(bdb_urow_p _r) { if (_r->fields != NULL) { bdb_free_ufield_list(_r->fields); } pkg_free(_r); }; void bdb_free_ufield(bdb_uval_p _v) { if (!VAL_NULL(&(_v->v))) { if (VAL_TYPE(&(_v->v)) == DB_STR || VAL_TYPE(&(_v->v)) == DB_STRING || VAL_TYPE(&(_v->v)) == DB_BLOB) { pkg_free(VAL_STR(&(_v->v)).s); } } pkg_free(_v); }; void bdb_free_ufield_list(bdb_uval_p _v) { bdb_uval_p curr, next; for (curr = _v; curr != NULL;) { next = curr->next; bdb_free_ufield(curr); curr = next; } }; void bdb_push_ufield(bdb_urow_p _r, bdb_uval_p _v) { bdb_uval_p f; if (_r->fields == NULL) { _r->fields = _v; return; } f = _r->fields; while (f->next != NULL) { f = f->next; } f->next = _v; }; int bdb_ufield_db2bdb(bdb_uval_p v, db_val_t* _v) { char *s; VAL_NULL(&(v->v)) = VAL_NULL(_v); VAL_TYPE(&(v->v)) = VAL_TYPE(_v); if (!VAL_NULL(&(v->v))) { switch (VAL_TYPE(_v)) { case DB_INT: VAL_INT(&(v->v)) = VAL_INT(_v); break; case DB_FLOAT: VAL_FLOAT(&(v->v)) = VAL_FLOAT(_v); break; case DB_DATETIME: VAL_TIME(&(v->v)) = VAL_TIME(_v); break; case DB_BLOB: s = pkg_malloc(VAL_BLOB(_v).len); memcpy(s, VAL_BLOB(_v).s, VAL_BLOB(_v).len); VAL_BLOB(&(v->v)).s = s; VAL_BLOB(&(v->v)).len = VAL_BLOB(_v).len; break; case DB_DOUBLE: VAL_DOUBLE(&(v->v)) = VAL_DOUBLE(_v); break; case DB_STRING: VAL_STR(&(v->v)).len = strlen(VAL_STRING(_v)) + 1; s = pkg_malloc(VAL_STR(&(v->v)).len); strcpy(s, VAL_STRING(_v)); VAL_STRING(&(v->v)) = s; break; case DB_STR: s = pkg_malloc(VAL_STR(_v).len); memcpy(s, VAL_STR(_v).s, VAL_STR(_v).len); VAL_STR(&(v->v)).s = s; VAL_STR(&(v->v)).len = VAL_STR(_v).len; break; case DB_BITMAP: VAL_BITMAP(&(v->v)) = VAL_BITMAP(_v); break; default: LOG(L_ERR, "BDB:bdb_ufield_db2bdb: unknown column type: %0X\n", VAL_TYPE(_v)); return -1; break; } } return 0; }; kamailio-4.0.4/obsolete/unixsock/0000755000000000000000000000000012223032460015454 5ustar rootrootkamailio-4.0.4/obsolete/unixsock/README0000644000000000000000000000005712223032460016336 0ustar rootrootThis module is considered obsolete 2009-10-23 kamailio-4.0.4/obsolete/unixsock/unixsock_server.c0000644000000000000000000001701412223032460021054 0ustar rootroot/* * $Id$ * * UNIX Domain Socket Server * * Copyright (C) 2001-2004 FhG Fokus * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* History: * created by janakj * 2004-03-03 added tcp init code (andrei) * 2004-04-29 added chmod(sock_perm) & chown(sock_user,sock_group) (andrei) */ #include #include #include #include #include #include #include #include #include #include #include #include "../../config.h" #include "../../ut.h" #include "../../globals.h" #include "../../trim.h" #include "../../pt.h" #include "../../sr_module.h" #include "../../mem/mem.h" #include "unixsock_server.h" #include "../../tsend.h" /* AF_LOCAL is not defined on solaris */ #if !defined(AF_LOCAL) #define AF_LOCAL AF_UNIX #endif #if !defined(PF_LOCAL) #define PF_LOCAL PF_UNIX #endif /* solaris doesn't have SUN_LEN */ #ifndef SUN_LEN #define SUN_LEN(sa) ( strlen((sa)->sun_path) + \ (size_t)(((struct sockaddr_un*)0)->sun_path) ) #endif #define UNIXSOCK_BUF_SIZE BUF_SIZE char* unixsock_name = 0; char* unixsock_user = 0; char* unixsock_mode = 0; char* unixsock_group = 0; unsigned int unixsock_children = 1; unsigned int unixsock_tx_timeout = 2000; /* Timeout for sending replies in milliseconds */ static int rx_sock, tx_sock; static char reply_buf[UNIXSOCK_BUF_SIZE]; static str reply_pos; static struct sockaddr_un reply_addr; static unsigned int reply_addr_len; static int parse_cmd(str* cmd, str* buffer) { return 1; } /* * Create and bind local socket */ int init_unixsock_socket(void) { struct sockaddr_un addr; int len, flags; if (unixsock_name == 0) { DBG("No unix domain socket will be opened\n"); return 1; } len = strlen(unixsock_name); if (len == 0) { DBG("Unix domain socket server disabled\n"); return 1; } else if (len > 107) { ERR("Socket name too long\n"); return -1; } DBG("Initializing Unix domain socket server @ %s\n", unixsock_name); if (unlink(unixsock_name) == -1) { if (errno != ENOENT) { ERR(L_ERR, "Error while unlinking " "old socket (%s): %s\n", unixsock_name, strerror(errno)); return -1; } } rx_sock = socket(PF_LOCAL, SOCK_DGRAM, 0); if (rx_sock == -1) { ERR("Cannot create listening socket: %s\n", strerror(errno)); return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = PF_LOCAL; memcpy(addr.sun_path, unixsock_name, len); if (bind(rx_sock, (struct sockaddr*)&addr, SUN_LEN(&addr)) == -1) { ERR("bind: %s\n", strerror(errno)); goto err_rx; } /* try to change permissions */ if (sock_mode){ /* sock_mode==0 doesn't make sense, nobody can read/write*/ if (chmod(unixsock_name, sock_mode)<0){ ERR("Failed to change the" " permissions for %s to %04o: %s[%d]\n", unixsock_name, sock_mode, strerror(errno), errno); goto err_rx; } } /* try to change the ownership */ if ((sock_uid!=-1) || (sock_gid!=-1)){ if (chown(unixsock_name, sock_uid, sock_gid)<0){ ERR("Failed to change the" " owner/group for %s to %d.%d; %s[%d]\n", unixsock_name, sock_uid, sock_gid, strerror(errno), errno); goto err_rx; } } tx_sock = socket(PF_LOCAL, SOCK_DGRAM, 0); if (tx_sock == -1) { ERR("Cannot create sending socket:" " %s\n", strerror(errno)); goto err_rx; } /* Turn non-blocking mode on */ flags = fcntl(tx_sock, F_GETFL); if (flags == -1){ ERR("fcntl failed: %s\n", strerror(errno)); goto err_both; } if (fcntl(tx_sock, F_SETFL, flags | O_NONBLOCK) == -1) { ERR("fcntl: set non-blocking failed: %s\n", strerror(errno)); goto err_both; } return 1; err_both: close(tx_sock); err_rx: close(rx_sock); return -1; } static void unix_server_loop(void) { int ret; str cmd, buffer; static char buf[UNIXSOCK_BUF_SIZE]; struct unixsock_cmd* c = NULL; while(1) { reply_addr_len = sizeof(reply_addr); ret = recvfrom(rx_sock, buf, UNIXSOCK_BUF_SIZE, 0, (struct sockaddr*)&reply_addr, &reply_addr_len); if (ret == -1) { ERR("recvfrom: (%d) %s\n", errno, strerror(errno)); if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == ECONNREFUSED)) { DBG("Got %d (%s), going on\n", errno, strerror(errno)); continue; } ERR("BUG: unexpected recvfrom error\n"); continue; } buffer.s = buf; buffer.len = ret; unixsock_reply_reset(); if (parse_cmd(&cmd, &buffer) < 0) { /* unixsock_reply_asciiz("400 First line malformed\n"); */ unixsock_reply_send(); continue; } buffer.s = cmd.s + cmd.len + 1; buffer.len -= cmd.len + 1 + 1; /* c = lookup_cmd(&cmd); */ if (c == 0) { ERR("Could not find " "command '%.*s'\n", cmd.len, ZSW(cmd.s)); /* unixsock_reply_printf("500 Command %.*s not found\n", cmd.len, ZSW(cmd.s)); */ unixsock_reply_send(); continue; } /* ret = c->f(&buffer); */ if (ret < 0) { ERR("Command '%.*s' failed with " "return value %d\n", cmd.len, ZSW(cmd.s), ret); /* Note that we do not send reply here, the * function is supposed to do so, it knows the * reason of the failure better than us */ } } } /* * Spawn listeners */ int init_unixsock_children(void) { int i; pid_t pid; if (!unixsock_name || *unixsock_name == '\0') { return 1; } for(i = 0; i < unixsock_children; i++) { pid = fork_process(PROC_UNIXSOCK,"unix domain socket",1); if (pid < 0) { ERR("Unable to fork: %s\n", strerror(errno)); close(rx_sock); close(tx_sock); return -1; } else if (pid == 0) { /* child */ unix_server_loop(); /* Never returns */ } /* Parent */ } DBG("Unix domain socket server successfully initialized @ %s\n", unixsock_name); return 1; } /* * Clean up */ void close_unixsock_server(void) { close(rx_sock); close(tx_sock); } /* * Send a reply */ ssize_t unixsock_reply_send(void) { return tsend_dgram(tx_sock, reply_buf, reply_pos.s - reply_buf, (struct sockaddr*)&reply_addr, reply_addr_len, unixsock_tx_timeout); } /* * Send a reply */ ssize_t unixsock_reply_sendto(struct sockaddr_un* to) { if (!to) { ERR("Invalid parameter value\n"); return -1; } return tsend_dgram(tx_sock, reply_buf, reply_pos.s - reply_buf, (struct sockaddr*)to, SUN_LEN(to), unixsock_tx_timeout); } /* * Reset the reply buffer -- start to write * at the beginning */ void unixsock_reply_reset(void) { reply_pos.s = reply_buf; reply_pos.len = UNIXSOCK_BUF_SIZE; } /* * Return the address of the sender */ struct sockaddr_un* unixsock_sender_addr(void) { return &reply_addr; } kamailio-4.0.4/obsolete/unixsock/unixsock_server.h0000644000000000000000000000367612223032460021072 0ustar rootroot/* * $Id$ * * UNIX Domain Socket Server * * Copyright (C) 2001-2004 FhG Fokus * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _UNIXSOCK_SERVER_H #define _UNIXSOCK_SERVER_H #include #include #include #include "../../str.h" extern char* unixsock_name; extern char* unixsock_user; extern char* unixsock_mode; extern char* unixsock_group; extern unsigned int unixsock_children; extern unsigned int unixsock_tx_timeout; /* * Initialize Unix domain socket server */ int init_unixsock_socket(void); /* * Initialize Unix domain socket server */ int init_unixsock_children(void); /* * Clean up */ void close_unixsock_server(void); /* * Send the reply */ ssize_t unixsock_reply_send(void); /* * Send the reply to the given destination */ ssize_t unixsock_reply_sendto(struct sockaddr_un* to); /* * Return the address of the sender */ struct sockaddr_un* unixsock_sender_addr(void); void unixsock_reply_reset(void); #endif /* _UNIXSOCK_SERVER_H */ kamailio-4.0.4/obsolete/unixsock/Makefile0000644000000000000000000000032312223032460017112 0ustar rootroot# $Id$ # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=unixsock.so LIBS= DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/unixsock/unixsock.c0000644000000000000000000000504712223032460017471 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../../str.h" #include "../../sr_module.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../parser/msg_parser.h" #include "../../ut.h" #include "../../dprint.h" #include "../../pt.h" #include "unixsock_server.h" MODULE_VERSION static int mod_init(void); static int child_init(int rank); /* * Exported functions */ static cmd_export_t cmds[] = { {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"socket", PARAM_STRING, &unixsock_name }, {"children", PARAM_INT, &unixsock_children }, {"send_timeout", PARAM_INT, &unixsock_tx_timeout}, {"socket_user", PARAM_STRING, &unixsock_user }, {"socket_mode", PARAM_STRING, &unixsock_mode }, {"socket_group", PARAM_STRING, &unixsock_group }, {0, 0, 0} }; struct module_exports exports = { "unixsock", cmds, /* Exported commands */ 0, /* Exported RPC methods */ params, /* Exported parameters */ mod_init, /* module initialization function */ 0, /* response function*/ 0, /* destroy function */ 0, /* oncancel function */ child_init /* per-child init function */ }; static int mod_init(void) { if (init_unixsock_socket() < 0) return -1; /* Signal to the core that we will be * creating additional processes */ if (unixsock_name) register_procs(unixsock_children); return 0; } static int child_init(int rank) { if (rank == PROC_MAIN) { if (init_unixsock_children() < 0) return -1; } return 0; } kamailio-4.0.4/obsolete/osp/0000755000000000000000000000000012223032460014412 5ustar rootrootkamailio-4.0.4/obsolete/osp/usage.h0000644000000000000000000000525612223032460015677 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _OSP_MOD_USAGE_H_ #define _OSP_MOD_USAGE_H_ #include #include "../../parser/msg_parser.h" /* This module reports originating and terminating call set up and duration usage * for OSP transactions. * * Call set-up usage is reported based on the osp_dest structures stored as AVPs. * It includes OSP transaction id, response codes, start time, alert time, * connect time, etc. * * Duration usage is reported based on the OSP cooky recorded into the route set * (using add_rr_param) after requesting routing/authorization on the originating * side, and validating authorization on the terminating side. It include * OSP transaction id, duration, stop time, etc. * * Actual conversation duration maybe calculated using connect time (from the call * set up usage) and stop time (from the duration usage). */ void ospRecordOrigTransaction(struct sip_msg* msg, unsigned long long transid, char* uac, char* from, char* to, time_t authtime, unsigned destinationCount); void ospRecordTermTransaction(struct sip_msg* msg, unsigned long long transid, char* uac, char* from, char* to, time_t authtime); void ospReportOrigSetupUsage(void); void ospReportTermSetupUsage(void); int ospReportUsage(struct sip_msg* msg, char* ignore1, char* ignore2); #endif /* _OSP_MOD_USAGE_H_ */ kamailio-4.0.4/obsolete/osp/globals.c0000644000000000000000000000513412223032460016204 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "osp_mod.h" unsigned int _osp_sp_number; char* _osp_sp_uris[OSP_DEF_SPS]; unsigned long _osp_sp_weights[OSP_DEF_SPS] = { OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT }; char* _osp_device_ip = NULL; char* _osp_device_port = NULL; unsigned char* _osp_private_key = NULL; unsigned char* _osp_local_certificate = NULL; unsigned char* _osp_ca_certificate = NULL; int _osp_crypto_hw = OSP_DEF_HW; int _osp_validate_callid = OSP_DEF_CALLID; int _osp_token_format = OSP_DEF_TOKEN; int _osp_ssl_lifetime = OSP_DEF_SSLLIFE; int _osp_persistence = OSP_DEF_PERSISTENCE; int _osp_retry_delay = OSP_DEF_DELAY; int _osp_retry_limit = OSP_DEF_RETRY; int _osp_timeout = OSP_DEF_TIMEOUT; int _osp_max_dests = OSP_DEF_DESTS; int _osp_use_rpid = OSP_DEF_USERPID; int _osp_redir_uri = OSP_DEF_REDIRURI; char _osp_PRIVATE_KEY[OSP_KEYBUF_SIZE]; char _osp_LOCAL_CERTIFICATE[OSP_KEYBUF_SIZE]; char _osp_CA_CERTIFICATE[OSP_KEYBUF_SIZE]; OSPTPROVHANDLE _osp_provider = -1; kamailio-4.0.4/obsolete/osp/etc/0000755000000000000000000000000012223032460015165 5ustar rootrootkamailio-4.0.4/obsolete/osp/etc/sample-osp-ser.cfg0000644000000000000000000004004612223032460020521 0ustar rootroot debug=3 # debug level (cmd line: -dddddddddd) fork=yes log_stderror=no # (cmd line: -E) sip_warning=no /* Uncomment these lines to enter debugging mode # fork=no # log_stderror=yes */ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=20 # ------------------ module loading ---------------------------------- loadmodule "/usr/local/lib/ser/modules/sl.so" loadmodule "/usr/local/lib/ser/modules/tm.so" loadmodule "/usr/local/lib/ser/modules/maxfwd.so" loadmodule "/usr/local/lib/ser/modules/rr.so" loadmodule "/usr/local/lib/ser/modules/textops.so" loadmodule "/usr/local/lib/ser/modules/ctl.so" loadmodule "/usr/local/lib/ser/modules/usrloc.so" loadmodule "/usr/local/lib/ser/modules/registrar.so" loadmodule "/usr/local/lib/ser/modules/avpops.so" loadmodule "/usr/local/lib/ser/modules/fifo.so" loadmodule "/usr/local/lib/ser/modules/options.so" loadmodule "/usr/local/lib/ser/modules/xprint.so" # Load OSP module loadmodule "/usr/local/lib/ser/modules/osp.so" # ----------------- setting module-specific parameters --------------- # # PEERING PARAMETERS: # =================== # This section contains OSP parameters that users may need to configure for multi-lateral # peering. (sp1_uri must be configured.) Additional detail on OSP Module parameters and # functions is provided in the "OSP Module for Secure, Multi-Lateral Peering" document # located at:http://developer.berlios.de/docman/?group_id=3799 # # Configure Peering Servers: # ========================== # SER can be configured to query two peering servers for routing information and peering # authorization tokens using the sp1_uri and sp2_uri parameters. A configuration for sp1_uri # is required, configuring sp2_uri is optional. The peering server address should be # configured as a standard URL beginning with either http:// or https:// followed by the # domain name of the OSP server or the IP address enclosed in brackets. The domain name # or IP address should be followed by the peering server TCP port number and uniform # resource identifier. Below are example configurations. # modparam("osp", "sp1_uri", "http://osptestserver.transnexus.com:1080/osp") # modparam("osp", "sp2_uri", "https://[1.2.3.4]:1443/osp") # # SER IP Address # ============== # device_ip is a recommended parameter that explicitly defines the IP address of SER in # a peering request message (as SourceAlternate type=transport). The IP address must # be in brackets as shown in the example below. # # modparam("osp", "device_ip", "[1.1.1.1]") # # Peering Token Validation # ======================== # When SER receives a SIP INVITE with a peering token, the OSP Module will validate the token to # determine whether or not the call has been authorized by a peering server. Peering tokens may, # or may not, be digitally signed. This parameter defines if SER will validate signed or unsigned # tokens or both. The values for "token format" are defined below. The default value is 2. # # 0 - Validate only signed tokens. Calls with valid signed tokens are allowed. # 1 - Validate only unsigned tokens. Calls with valid unsigned tokens are allowed. # 2 - Validate both signed and unsigned tokens are allowed. Calls with valid tokens are allowed. # # modparam("osp", "token_format", 2) # # Crypto files from Peering Server Enrollment # =========================================== # These parameters identify crypto files used for validating peering authorization tokens # and establishing a secure channel between SER and a peering server using SSL. The files are # generated using the 'Enroll' utility from the OSP toolkit. By default, the proxy will look # for pkey.pem, localcert.pem, and cacart_0.pem in the default configuration directory. # The default config directory is set at compile time using CFG_DIR and defaults to # /usr/local/etc/ser/. The files may be copied to the expected file location or the # parameters below may be changed. # # If the default CFG_DIR value was used at compile time, the files will be loaded from: # modparam("osp", "private_key", "/usr/local/etc/ser/pkey.pem") # modparam("osp", "local_certificate", "/usr/local/etc/ser/localcert.pem") # modparam("osp", "ca_certificates", "/usr/local/etc/ser/cacert_0.pem") # # Use Remote-Party-ID for calling number # =========================================== # This parameter is used to tell OSP module if the calling number should be obtained from RPID header. # The default value is 1. # # 0 - OSP module will use the calling number in From header. # 1 - OSP module will use the calling number in RPID header if a RPID header exists. # # modparam("osp", "use_rpid_for_calling_number", 1) # # URI Format for Redirection Messages # =========================================== # This parameter is used to tell OSP module which URI format should be used for redirection messages. # The default value is 0. # # 0 - "xxxxxxxxxx@xxx.xxx.xxx.xxx" # 1 - "". This is for Cisco 2600 IP-IP gateway. # # modparam("osp", "redirection_uri_format", 0) # listen on the "standard" fifo for backward compatibility modparam("ctl", "fifo", "fifo:/tmp/ser_fifo") # -- usrloc params -- modparam("usrloc", "db_mode", 0) # -- rr params -- avpflags dialog_cookie; # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # enable append_fromtag, request's from-tag is appended to record-route; # that's useful for understanding whether subsequent requests (such as BYE) come from # caller (route's from-tag==BYE's from-tag) or callee (route's from-tag==BYE's to-tag) modparam("rr", "append_fromtag", 1) # -- tm params -- # Timer which hits if no final reply for a request or ACK for a # negative INVITE reply arrives (in seconds). For example - UA server is off-line. # In other words, if the proxy does not receive a response to an Invite before this # timer expires, the proxy will retry the call and send an Invite to the next VoIP # destination in the routing list. modparam("tm", "fr_timer", 5000) # Timer which hits if no final reply for an INVITE arrives after # a provisional message was received (in seconds). # For example - user is not picking up the phone modparam("tm", "fr_inv_timer", 30000) # ------------------------- request routing logic ------------------- # main routing logic route{ xplog("L_INFO", "----ROUTE: Route IN - M=%rm RURI=%ru F=%fu T=%tu IP=%si ID=$ci\n"); # initial sanity checks if (!mf_process_maxfwd_header("10")) { xplog("L_WARN", "----ROUTE: Too many hops, $rm from '%fu' to '%tu'\n"); sl_send_reply("483", "Too Many Hops"); break; }; if (msg:len >= max_len) { xplog("L_WARN", "----ROUTE: Message too big, $rm from '%fu' to '%tu'\n"); sl_send_reply("513", "Message Too Big"); break; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (method!="INVITE") { record_route(); } # loose-route processing if (loose_route()) { if (method=="INVITE") { log(2, "----ROUTE: Relay re-INVITE\n"); # send it out now; use stateful forwarding as it works reliably even for UDP2TCP if (!t_relay()) { sl_reply_error(); } break; } else if (method=="ACK") { log(2, "----ROUTE: Relay ACK\n"); # send it out now; use stateful forwarding as it works reliably even for UDP2TCP if (!t_relay()) { sl_reply_error(); } break; } } else { if (method=="BYE") { xplog("L_WARN", "----ROUTE: Processing BYE without route header - F=$fu T=$tu IP=$si ID=$ci\n"); if (t_lookup_request()) { log(2, "----ROUTE: Duplicated BYE\n"); } else { # NOTE - don't t_relay before reporting usage if (!reportospusage()) { xplog("L_WARN", "----ROUTE: failed to report usage, from '%fu' to '%tu'\n"); } sl_send_reply("400", "Bad Request - no route header in BYE message"); } break; } } if (method=="INVITE") { log(2, "----ROUTE: Processing INVITE\n"); # Stop retransmission sl_send_reply("100", "Trying"); if (t_lookup_request()) { log(2, "----ROUTE: Duplicated INVITE\n"); break; } # Authentication log(3, "OSP authorization validation logic\n"); # This function looks for OSP peering token in the message. It will fail # if the token is not present if (checkospheader()) { log(3, "With OSP token, validate it\n"); # The function validates OSP tokens. It will fail # if the token is not valid or has expired if (validateospheader()) { # Authorization is valid. The proxy can now use its own database of # registered users for routing information. # The proxy could also issue another OSP peering authorization and # routing request by calling route(1) function. log(3, "OSP authorization valid\n"); # Remove the OSP peering token from the received message # Otherwise it will be forwarded on to the next hop remove_hf("P-OSP-Auth-Token"); } else { log(3, "OSP authorization invalid\n"); sl_send_reply("401", "Unauthorized"); break; }; } else { log(3, "Without OSP token, apply different authentication strategy\n"); log(3, "Go ahead, everyone is welcomed\n"); # # Implement authentication strategy here or simply add the # # statements below to block all invites without OSP peering tokens # sl_send_reply("401", "Unauthorized"); # break; } log(2, "----ROUTE: Authentication passed\n"); # Routing # if (lookup("location")) { # log(2, "----ROUTE: Registered user, forward the message\n"); # append_hf("P-hint: usrloc\r\n"); # record_route(); # t_relay(); # } else { # log(2, "----ROUTE: Unregistered user, use OSP to get routing\n"); # route(2); # } log(2, "----ROUTE: Use OSP to get routing\n"); route(2); } else if (method=="ACK") { log(2, "----ROUTE: Processing ACK\n"); if (t_lookup_request()) { log(2, "----ROUTE: Relay E2E ACK\n"); t_relay(); } else { log(2, "----ROUTE: Not to relay ACK"); } } else if (method=="BYE") { log(2, "----ROUTE: Processing BYE\n"); if (t_lookup_request()) { log(2, "----ROUTE: Duplicated BYE\n"); break; } # NOTE - don't t_relay before reporting usage if (!reportospusage()) { xplog("L_WARN", "----ROUTE: failed to report usage, from '%fu' to '%tu'\n"); } t_relay(); } else if (method=="CANCEL") { log(2, "----ROUTE: Processing CANCEL\n"); if (t_lookup_request()) { t_relay(); } else { xplog("L_WARN", "----ROUTE: CANCEL without matching transaction, from '$fu' to '$tu'\n"); } } else if ((method=="OPTIONS") && (uri==myself)) { log(2, "----ROUTE: Processing OPTIONS\n"); options_reply(); } else if (method=="PRACK") { log(2, "----ROUTE: Processing PRACK\n"); t_relay(); } else if (method=="INFO") { log(2, "----ROUTE: Processing INFO\n"); t_relay(); } else if (method=="UPDATE") { log(2, "----ROUTE: Processing UPDATE\n"); t_relay(); # } else if (method=="REGISTER") { # log(2, "----ROUTE: Processing REGISTER\n"); # # # Stop retransmission # sl_send_reply("100", "Trying"); # # if (uri==myself) { # log(2, "----ROUTE: Registered\n"); # save("location"); # } else { # log(2, "----ROUTE: Register from outside domain rejected\n"); # sl_send_reply("488", "Unknown Domain"); # } } else { xplog("L_WARN", "----ROUTE: Unsupported message, $rm from '%fu' to '%tu'\n"); sl_send_reply("500", "Unsupported Message"); } log(3, "----ROUTE: Route OUT\n"); } # OSP Authorization and Routing route[2] { log(3, "OSP authorization and routing logic\n"); # Is request to a phone number? # A phone number consists of digits (0 through 9) # and can begin with + # if (uri=~"sip:[+,0-9][0-9]*@") { # Requesting OSP peering routing and authorization # The request may fail if: # o OSP peering servers are not available # o Authentication failed # o There is no route to destination or the route is blocked log(3, "Requesting OSP authorization and routing\n"); requestosprouting(); if ($retcode == 1) { log(3, "Response received\n"); setavpflag("fr._osp_orig_cookie_", dialog_cookie); setavpflag("fr._osp_term_cookie_", dialog_cookie); record_route(); # Now we have 3 options. # o route(3) - sends a redirect to all available routes # o route(4) - fork off to all available routes # o route(5) in conjunction with failure_route(1) - sequentially tries all routes # route(3); # route(4); route(5); } else if ($retcode == -403) { xplog("L_WARN", "----ROUTE: Call to '%tu' from source device '%si' is blocked on OSP Server.\n"); sl_send_reply("403", "Forbidden - Call is blocked"); } else if ($retcode == -404) { xplog("L_WARN", "----ROUTE: No route on OSP server for call to '%tu' from source device '%si'.\n"); sl_send_reply("404", "Route Not Found"); } else if ($retcode == -500) { log(3, "----ROUTE: Internal Server Error\n"); sl_send_reply("500", "Internal Server Error"); } else { log(3, "----ROUTE: OSP Authorization failed\n"); sl_send_reply("503", "Service Not Available"); } # } else { # log(3, "Wrong phone number\n"); # sl_send_reply("401", "Not Phone Number"); # } } route[3] { log(3, "Prepare all routes and redirect\n"); if (prepareallosproutes()) { sl_send_reply("300", "Redirect"); } else { log(3, "Failed to prepare all routes\n"); sl_send_reply("500", "Internal Server Error"); } } route[4] { log(3, "Prepare all routes and fork-off\n"); if (prepareallosproutes()) { t_relay(); } else { log(3, "Failed to prepare all routes\n"); sl_send_reply("500", "Internal Server Error"); } } route[5] { log(3, "Try the 1st route\n"); if (prepareospfirstroute()) { t_on_branch("1"); t_on_failure("1"); t_relay(); } else { log(3, "Could not use the 1st route\n"); sl_send_reply("500", "Internal Server Error"); } } failure_route[1] { if (t_check_status("487")) { log(3, "Call canceled (status 487)\n"); break; } if (t_check_status("486")) { log(3, "User busy (status 486)\n"); break; } # tm's t_local_replied has not been implemented in SER yet. # if (t_check_status("408")) { # if (!t_local_replied("last")) { # log(3, "User unavailable (status 408)\n"); # break; # } # } log(3, "Try the next route\n"); if (prepareospnextroute()) { t_on_branch("1"); t_on_failure("1"); t_relay(); } else { xplog("L_WARN", "----ROUTE: All destinations attempted for call ID '%ci'. Call cannot be completed.\n"); t_reply("503", "Service Not Available - Call cannot be completed"); } } branch_route[1] { log(3, "Prepare route specific OSP information\n"); appendospheaders(); } kamailio-4.0.4/obsolete/osp/etc/pkey.pem0000644000000000000000000000075512223032460016647 0ustar rootroot-----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBAK8t5l+PUbTC4lvwlNxV5lpl+2dwSZGW46dowTe6y133XyVEwNii Rma2YNk3xKs/TJ3Wl9Wpns2SYEAJsFfSTukCAwEAAQJAPz13vCm2GmZ8Zyp74usT xLCqSJZNyMRLHQWBM0g44Iuy4wE3vpi7Wq+xYuSOH2mu4OddnxswCP4QhaXVQavT AQIhAOBVCKXtppEw9UaOBL4vW0Ed/6EA/1D8hDW6St0h7EXJAiEAx+iRmZKhJD6V T84dtX5ZYNVk3j3dAcIOovpzUj9a0CECIEduTCapmZQ5xqAEsLXuVlxRtQgLTUD4 ZxDElPn8x0MhAiBE2HlcND0+qDbvtwJQQOUzDgqg5xk3w8capboVdzAlQQIhAMC+ lDL7+gDYkNAft5Mu+NObJmQs4Cr+DkDFsKqoxqrm -----END RSA PRIVATE KEY----- kamailio-4.0.4/obsolete/osp/etc/cacert_0.pem0000644000000000000000000000104412223032460017347 0ustar rootroot-----BEGIN CERTIFICATE----- MIIBYDCCAQoCAQEwDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNl cnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTAyMDIw NDE4MjU1MloXDTEyMDIwMzE4MjU1MlowOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZl ci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMFwwDQYJKoZIhvcN AQEBBQADSwAwSAJBAPGeGwV41EIhX0jEDFLRXQhDEr50OUQPq+f55VwQd0TQNts0 6BP29+UiNdRW3c3IRHdZcJdC1Cg68ME9cgeq0h8CAwEAATANBgkqhkiG9w0BAQQF AANBAGkzBSj1EnnmUxbaiG1N4xjIuLAWydun7o3bFk2tV8dBIhnuh445obYyk1En Q27kI7eACCILBZqi2MHDOIMnoN0= -----END CERTIFICATE----- kamailio-4.0.4/obsolete/osp/etc/localcert.pem0000644000000000000000000000110212223032460017632 0ustar rootroot-----BEGIN CERTIFICATE----- MIIBeTCCASMCEHqkOHVRRWr+1COq3CR/xsowDQYJKoZIhvcNAQEEBQAwOzElMCMG A1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQ U2VydmVyMB4XDTA1MDYyMzAwMjkxOFoXDTA2MDYyNDAwMjkxOFowRTELMAkGA1UE BhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdp ZGdpdHMgUHR5IEx0ZDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCvLeZfj1G0wuJb 8JTcVeZaZftncEmRluOnaME3ustd918lRMDYokZmtmDZN8SrP0yd1pfVqZ7NkmBA CbBX0k7pAgMBAAEwDQYJKoZIhvcNAQEEBQADQQDnV8QNFVVJx/+7IselU0wsepqM urivXZzuxOmTEmTVDzCJx1xhA8jd3vGAj7XDIYiPub1PV23eY5a2ARJuw5w9 -----END CERTIFICATE-----kamailio-4.0.4/obsolete/osp/osp_mod.c0000644000000000000000000002635712223032460016233 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "../../sr_module.h" #include "osp_mod.h" #include "orig_transaction.h" #include "term_transaction.h" #include "usage.h" #include "tm.h" #include "provider.h" MODULE_VERSION extern unsigned int _osp_sp_number; extern char* _osp_sp_uris[]; extern unsigned long _osp_sp_weights[]; extern char* _osp_device_ip; extern char* _osp_device_port; extern unsigned char* _osp_private_key; extern unsigned char* _osp_local_certificate; extern unsigned char* _osp_ca_certificate; extern int _osp_crypto_hw; extern int _osp_validate_callid; extern int _osp_token_format; extern int _osp_ssl_lifetime; extern int _osp_persistence; extern int _osp_retry_delay; extern int _osp_retry_limit; extern int _osp_timeout; extern int _osp_max_dests; extern int _osp_use_rpid; extern int _osp_redir_uri; extern char _osp_PRIVATE_KEY[]; extern char _osp_LOCAL_CERTIFICATE[]; extern char _osp_CA_CERTIFICATE[]; extern OSPTPROVHANDLE _osp_provider; int osp_index[OSP_DEF_SPS]; static int ospInitMod(void); static void ospDestMod(void); static int ospInitChild(int); static int ospVerifyParameters(void); static void ospDumpParameters(void); static cmd_export_t cmds[]={ {"checkospheader", ospCheckHeader, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"validateospheader", ospValidateHeader, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"requestosprouting", ospRequestRouting, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"checkosproute", ospCheckRoute, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"prepareospfirstroute", ospPrepareFirstRoute,0, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"prepareospnextroute", ospPrepareNextRoute, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"prepareallosproutes", ospPrepareAllRoutes, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"appendospheaders", ospAppendHeaders, 0, 0, BRANCH_ROUTE}, {"reportospusage", ospReportUsage, 0, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0} }; static param_export_t params[]={ {"sp1_uri", STR_PARAM, &_osp_sp_uris[0]}, {"sp2_uri", STR_PARAM, &_osp_sp_uris[1]}, {"sp3_uri", STR_PARAM, &_osp_sp_uris[2]}, {"sp4_uri", STR_PARAM, &_osp_sp_uris[3]}, {"sp5_uri", STR_PARAM, &_osp_sp_uris[4]}, {"sp6_uri", STR_PARAM, &_osp_sp_uris[5]}, {"sp7_uri", STR_PARAM, &_osp_sp_uris[6]}, {"sp8_uri", STR_PARAM, &_osp_sp_uris[7]}, {"sp9_uri", STR_PARAM, &_osp_sp_uris[8]}, {"sp10_uri", STR_PARAM, &_osp_sp_uris[9]}, {"sp11_uri", STR_PARAM, &_osp_sp_uris[10]}, {"sp12_uri", STR_PARAM, &_osp_sp_uris[11]}, {"sp13_uri", STR_PARAM, &_osp_sp_uris[12]}, {"sp14_uri", STR_PARAM, &_osp_sp_uris[13]}, {"sp15_uri", STR_PARAM, &_osp_sp_uris[14]}, {"sp16_uri", STR_PARAM, &_osp_sp_uris[15]}, {"sp1_weight", INT_PARAM, &(_osp_sp_weights[0])}, {"sp2_weight", INT_PARAM, &(_osp_sp_weights[1])}, {"sp3_weight", INT_PARAM, &(_osp_sp_weights[2])}, {"sp4_weight", INT_PARAM, &(_osp_sp_weights[3])}, {"sp5_weight", INT_PARAM, &(_osp_sp_weights[4])}, {"sp6_weight", INT_PARAM, &(_osp_sp_weights[5])}, {"sp7_weight", INT_PARAM, &(_osp_sp_weights[6])}, {"sp8_weight", INT_PARAM, &(_osp_sp_weights[7])}, {"sp9_weight", INT_PARAM, &(_osp_sp_weights[8])}, {"sp10_weight", INT_PARAM, &(_osp_sp_weights[9])}, {"sp11_weight", INT_PARAM, &(_osp_sp_weights[10])}, {"sp12_weight", INT_PARAM, &(_osp_sp_weights[11])}, {"sp13_weight", INT_PARAM, &(_osp_sp_weights[12])}, {"sp14_weight", INT_PARAM, &(_osp_sp_weights[13])}, {"sp15_weight", INT_PARAM, &(_osp_sp_weights[14])}, {"sp16_weight", INT_PARAM, &(_osp_sp_weights[15])}, {"device_ip", STR_PARAM, &_osp_device_ip}, {"device_port", STR_PARAM, &_osp_device_port}, {"private_key", STR_PARAM, &_osp_private_key}, {"local_certificate", STR_PARAM, &_osp_local_certificate}, {"ca_certificates", STR_PARAM, &_osp_ca_certificate}, {"enable_crypto_hardware_support", INT_PARAM, &_osp_crypto_hw}, {"validate_callid", INT_PARAM, &(_osp_validate_callid)}, {"token_format", INT_PARAM, &_osp_token_format}, {"ssl_lifetime", INT_PARAM, &_osp_ssl_lifetime}, {"persistence", INT_PARAM, &_osp_persistence}, {"retry_delay", INT_PARAM, &_osp_retry_delay}, {"retry_limit", INT_PARAM, &_osp_retry_limit}, {"timeout", INT_PARAM, &_osp_timeout}, {"max_destinations", INT_PARAM, &_osp_max_dests}, {"use_rpid_for_calling_number", INT_PARAM, &_osp_use_rpid}, {"redirection_uri_format", INT_PARAM, &_osp_redir_uri}, {0,0,0} }; struct module_exports exports = { "osp", cmds, 0, /* RPC methods */ params, ospInitMod, /* module initialization function */ 0, /* response function*/ ospDestMod, /* destroy function */ 0, /* oncancel function */ ospInitChild, /* per-child init function */ }; /* * Initialize OSP module * return 0 success, -1 failure */ static int ospInitMod(void) { LOG(L_DBG, "osp: ospInitMod\n"); if (ospVerifyParameters() != 0) { /* At least one parameter incorrect -> error */ return -1; } if (ospInitTm() < 0) { return -1; } /* everything is fine, initialization done */ return 0; } /* * Destrroy OSP module */ static void ospDestMod(void) { LOG(L_DBG, "osp: ospDestMod\n"); } /* * Initializeild process of OSP module * param rank * return 0 success, -1 failure */ static int ospInitChild( int rank) { int code = -1; LOG(L_DBG, "osp: ospInitChild\n"); code = ospSetupProvider(); LOG(L_DBG, "osp: provider '%i' (%d)\n", _osp_provider, code); return 0; } /* * Verify parameters for OSP module * return 0 success, -1 failure */ static int ospVerifyParameters(void) { int i; int result = 0; LOG(L_DBG, "osp: ospVerifyParamters\n"); /* Default location for the cert files is in the compile time variable CFG_DIR */ if (_osp_private_key == NULL) { sprintf(_osp_PRIVATE_KEY, "%spkey.pem", CFG_DIR); _osp_private_key = (unsigned char*)_osp_PRIVATE_KEY; } if (_osp_local_certificate == NULL) { sprintf(_osp_LOCAL_CERTIFICATE, "%slocalcert.pem", CFG_DIR); _osp_local_certificate = (unsigned char*)_osp_LOCAL_CERTIFICATE; } if (_osp_ca_certificate == NULL) { sprintf(_osp_CA_CERTIFICATE, "%scacert_0.pem", CFG_DIR); _osp_ca_certificate = (unsigned char*)_osp_CA_CERTIFICATE; } if (_osp_device_ip == NULL) { _osp_device_ip = ""; } if (_osp_device_port == NULL) { _osp_device_port = ""; } if (_osp_max_dests > OSP_DEF_DESTS || _osp_max_dests < 1) { _osp_max_dests = OSP_DEF_DESTS; LOG(L_WARN, "osp: WARN: max_destinations is out of range, reset to %d\n", OSP_DEF_DESTS); } if (_osp_token_format < 0 || _osp_token_format > 2) { _osp_token_format = OSP_DEF_TOKEN; LOG(L_WARN, "osp: WARN: token_format is out of range, reset to %d\n", OSP_DEF_TOKEN); } _osp_sp_number = 0; for (i = 0; i < OSP_DEF_SPS; i++) { if (_osp_sp_uris[i] != NULL) { if (_osp_sp_number != i) { _osp_sp_uris[_osp_sp_number] = _osp_sp_uris[i]; _osp_sp_weights[_osp_sp_number] = _osp_sp_weights[i]; _osp_sp_uris[i] = NULL; _osp_sp_weights[i] = OSP_DEF_WEIGHT; } osp_index[_osp_sp_number] = i + 1; _osp_sp_number++; } } if (_osp_sp_number == 0) { LOG(L_ERR, "osp: ERROR: at least one service point uri must be configured\n"); result = -1; } ospDumpParameters(); return result; } /* * Dump OSP module configuration */ static void ospDumpParameters(void) { int i; LOG(L_INFO, "osp: module configuration: "); LOG(L_INFO, " number of service points '%d'", _osp_sp_number); for (i = 0; i < _osp_sp_number; i++) { LOG(L_INFO, " sp%d_uri '%s' sp%d_weight '%ld' ", osp_index[i], _osp_sp_uris[i], osp_index[i], _osp_sp_weights[i]); } LOG(L_INFO, " device_ip '%s' device_port '%s' ", _osp_device_ip, _osp_device_port); LOG(L_INFO, " private_key '%s' ", _osp_private_key); LOG(L_INFO, " local_certificate '%s' ", _osp_local_certificate); LOG(L_INFO, " ca_certificates '%s' ", _osp_ca_certificate); LOG(L_INFO, " enable_crypto_hardware_support '%d' ", _osp_crypto_hw); LOG(L_INFO, " token_format '%d' ", _osp_token_format); LOG(L_INFO, " ssl_lifetime '%d' ", _osp_ssl_lifetime); LOG(L_INFO, " persistence '%d' ", _osp_persistence); LOG(L_INFO, " retry_delay '%d' ", _osp_retry_delay); LOG(L_INFO, " retry_limit '%d' ", _osp_retry_limit); LOG(L_INFO, " timeout '%d' ", _osp_timeout); LOG(L_INFO, " validate_call_id '%d' ", _osp_validate_callid); LOG(L_INFO, " use_rpid_for_calling_number '%d' ", _osp_use_rpid); LOG(L_INFO, " redirection_uri_format '%d' ", _osp_redir_uri); LOG(L_INFO, " max_destinations '%d'\n", _osp_max_dests); } kamailio-4.0.4/obsolete/osp/doc/0000755000000000000000000000000012223032460015157 5ustar rootrootkamailio-4.0.4/obsolete/osp/doc/osp_admin.xml0000644000000000000000000005136312223032460017662 0ustar rootroot User's Guide
Overview The OSP module enables SER to support secure, multi-lateral peering using the OSP standard defined by ETSI (TS 101 321 V4.1.1). This module will enable your SER to: Send a peering authorization request to a peering server. Validate a digitally signed peering authorization token received in a SIP INVITE message. Report usage information to a peering server.
Dependencies The OSP module depends on the following modules which must be loaded before the OSP module. sl -- stateless replier tm -- stateful processing rr -- Record-Route/Route operation textops -- text based operation avpops -- AVP operation OSP Toolkit -- The OSP Toolkit, available from http://sourceforge.net/projects/osp-toolkit, must be built before building SER with the OSP Module. For instructions on building SER with the OSP Toolkit, see http://www.transnexus.com/White%20Papers/Multi-Lateral_Peering_with_SER_V2.0.x.pdf
Exported Parameters
<varname>sp1_uri</varname>, <varname>sp2_uri</varname>, ..., <varname>sp16_uri</varname> These sp_uri (string) parameters define peering servers to be used for requesting peering authorization and routing information. At least one peering server must be configured. Others are required only if there are more than one peering servers. Each peering server address takes the form of a standard URL, and consists of up to four components: An optional indication of the protocol to be used for communicating with the peering server. Both HTTP and HTTP secured with SSL/TLS are supported and are indicated by "http://" and "https://" respectively. If the protocol is not explicitly indicated, the SER defaults to HTTP secured with SSL. The Internet domain name for the peering server. An IP address may also be used, provided it is enclosed in square brackets such as [172.16.1.1]. An optional TCP port number for communicating with the peering server. If the port number is omitted, the SER defaults to port 1080 (for HTTP) or port 1443 (for HTTP secured with SSL). The uniform resource identifier for requests to the peering server. This component is not optional and must be included. Setting the OSP servers modparam("osp","sp1_uri","http://osptestserver.transnexus.com:1080/osp") modparam("osp","sp2_uri","https://[1.2.3.4]:1443/osp")
<varname>device_ip</varname> The device_ip (string) is a recommended parameter that explicitly defines the IP address of SER in a peering request message (as SourceAlternate type=transport). The IP address must be in brackets as shown in the example below. Setting the device IP address modparam("osp","device_ip","[1.1.1.1]")
<varname>token_format</varname> When SER receives a SIP INVITE with a peering token, the OSP Module will validate the token to determine whether or not the call has been authorized by a peering server. Peering tokens may, or may not, be digitally signed. The token format (integer) parameter defines if SER will validate signed or unsigned tokens or both. The values for token format are defined below. The default value is 2. 0 - Validate only signed tokens. Calls with valid signed tokens are allowed. 1 - Validate only unsigned tokens. Calls with valid unsigned tokens are allowed. 2 - Validate both signed and unsigned tokens are allowed. Calls with valid tokens are allowed. Setting the token format modparam("osp","token_format",2)
<varname>private_key</varname>, <varname>local_certificate</varname>, <varname>ca_certificates</varname> These parameters identify files are used for validating peering authorization tokens and establishing a secure channel between SER and a peering server using SSL. The files are generated using the 'Enroll' utility from the OSP Toolkit. By default, the proxy will look for pkey.pem, localcert.pem, and cacart_0.pem in the default configuration directory. The default config directory is set at compile time using CFG_DIR and defaults to /usr/local/etc/ser/. The files may be copied to the expected file location or the parameters below may be changed. Set authorization files If the default CFG_DIR value was used at compile time, the files will be loaded from: modparam("osp","private_key","/usr/local/etc/ser/pkey.pem") modparam("osp","local_certificate","/usr/local/etc/ser/localcert.pem") modparam("osp","ca_certificates","/usr/local/etc/ser/cacert.pem")
<varname>sp1_weight</varname>, <varname>sp2_weight</varname>, ..., <varname>sp16_weight</varname> These sp_weight (integer) parameters are used for load balancing peering requests to peering servers. These parameters are most effective when configured as factors of 1000. For example, if sp1_uri should manage twice the traffic load of sp2_uri, then set sp1_weight to 2000 and sp2_weight to 1000. Shared load balancing between peering servers is recommended. However, peering servers can be configured as primary and backup by assigning a sp_weight of 0 to the primary server and a non-zero sp_weight to the back-up server. The default values for sp1_weight and sp2_weight are 1000. Setting the OSP server weights modparam("osp","sp1_weight",1000)
<varname>device_port</varname> The device_port (string) parameter is an optional field which includes the SIP port being used by SER in the peering request (as SourceAlternate type=network) to the peering server. If is not configured, this information is not included in the peering request. This field is useful if multiple SERs are running on the same Linux computer since it enables the peering server to administer different peering policies based on different SIP proxies. This parameter has not been implemented yet. Setting the device port modparam("osp","device_port","5060")
<varname>enable_crypto_hardware_support</varname> The enable_crypto_hardware_support (integer) parameter is used to set the cryptographic hardware acceleration engine in the openssl library. The default value is 0 (no crypto hardware is present). If crypto hardware is used, the value should be set to 1. Setting the hardware support modparam("osp","enable_crypto_hardware_support",0)
<varname>ssl_lifetime</varname> The ssl_lifetime (integer) parameter defines the lifetime, in seconds, of a single SSL session key. Once this time limit is exceeded, the OSP Module will negotiate a new session key. Communication exchanges in progress will not be interrupted when this time limit expires. This is an optional field with default value is 200 seconds. Setting the ssl lifetime modparam("osp","ssl_lifetime",200)
<varname>persistence</varname> The persistence (integer) parameter defines the time, in seconds, that an HTTP connection should be maintained after the completion of a communication exchange. The OSP Module will maintain the connection for this time period in anticipation of future communication exchanges to the same peering server. Setting the persistence modparam("osp","persistence",1000)
<varname>retry_delay</varname> The retry_delay (integer) parameter defines the time, in seconds, between retrying connection attempts to an OSP peering server. After exhausting all peering servers the OSP Module will delay for this amount of time before resuming connection attempts. This is an optional field with default value is 1 second. Setting the retry delay modparam("osp","retry_delay",1)
<varname>retry_limit</varname> The retry_limit (integer) parameter defines the maximum number of retries for connection attempts to a peering server. If no connection is established after this many retry attempts to all peering servers, the OSP Module will cease connection attempts and return appropriate error codes. This number does not count the initial connection attempt, so that a retry_limit of 1 will result in a total of two connection attempts to every peering server. The default value is 2. Setting the retry limit modparam("osp","retry_limit",2)
<varname>timeout</varname> The timeout (integer) parameter defines the maximum time in milliseconds, to wait for a response from a peering server. If no response is received within this time, the current connection is aborted and the OSP Module attempts to contact the next peering server. The default value is 10 seconds. Setting the timeout modparam("osp","timeout",10)
<varname>max_destinations</varname> The max_destinations (integer) parameter defines the maximum number of destinations that SER requests the peering server to return in a peering response. The default value is 5. Setting the number of destination modparam("osp","max_destinations",5)
<varname>validate_call_id</varname> The validate_call_id (integer) parameter instructs the OSP module to validate call id in the peering token. If this value is set to 1, the OSP Module validates that the call id in the SIP INVITE message matches the call id in the peering token. If they do not match the INVITE is rejected. If this value is set to 0, the OSP Module will not validate the call id in the peering token. The default value is 1. Instructing the module to validate call id modparam("osp","validate_call_id",1)
<varname>use_rpid_for_calling_number</varname> The use_rpid_for_calling_number(integer) parameter instructs the OSP module to use the calling number in the Remote-Party-ID of the SIP INVITE message. If this value is set to 1, the OSP Module uses the calling number in the Reomte-Party-ID header of the INVITE message when a Remote-Party-ID exists. If this value is set to 0, the OSP Module will use the calling number in the From header of the INVITE message. The default value is 1. Instructing the module to use calling number in Remote-Party-ID modparam("osp","use_rpid_calling_number",1)
Exported Functions
<function moreinfo="none">checkospheader()</function> This function checks for the existence of the OSP-Auth-Token header field. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. checkospheader usage ... if (checkospheader()) { log("OSP header field found.\n"); } else { log("no OSP header field present\n"); }; ...
<function moreinfo="none">validateospheader()</function> This function validates an OSP-Token specified in the OSP-Auth-Tokenheader field of the SIP message. If a peering token is present, it will be validated locally. If no OSP header is found or the header token is invalid or expired, -1 is returned; on successful validation 1 is returned. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. validateospheader usage ... if (validateospheader()) { log("valid OSP header found\n"); } else { log("OSP header not found, invalid or expired\n"); }; ...
<function moreinfo="none">requestosprouting()</function> This function launches a query to the peering server requesting the IP address of one or more destination peers serving the called party. If destination peers are available, the peering server will return the IP address and a peering authorization token for each destination peer. The OSP-Auth-Token Header field is inserted into the SIP message and the SIP uri is rewritten to the IP address of destination peer provided by the peering server. The address of the called party must be a valid E164 number, otherwise this function returns -1. If the transaction was accepted by the peering server, the uri is being rewritten and 1 returned, on errors (peering servers are not available, authentication failed or there is no route to destination or the route is blocked) -1 is returned. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. requestosprouting usage ... if (requestosprouting()) { log("successfully queried OSP server, now relaying call\n"); } else { log("Authorization request was rejected from OSP server\n"); }; ...
<function moreinfo="none">prepareospfirstroute()</function> This function tries to prepare the INVITE to be forwarded or redirected using the first destination in the list returned by the peering server. If the route could not be prepared, the function returns 'FALSE' back to the script, which can then decide how to handle the failure. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. prepareospfirstroute usage ... if (prepareospfirstroute ()) { log("successfully prepared the first route, now relaying call\n"); } else { log("could not prepare the route. The first destination was blocked\n"); }; ...
<function moreinfo="none">prepareosprnextoute()</function> Once the call could not be completed through the first destination, this function tries to prepare the INVITE message using the next destination in the list returned by the peering Server. If it succeeds in preparing the route, the message is either redirected or relayed on (using the t_relay call), or else the function returns 'FALSE' back to the script, which can then decide how to handle the failure. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. prepareospnextroute usage ... if (prepareospnextroute ()) { log("successfully prepared the next route, now relaying call\n"); } else { log("could not prepare the route. No next destination available\n"); }; ...
<function moreinfo="none">appendospheaders()</function> This function is used to append route specific OSP headers. Before calling appendospheaders, prepareospfirst/nextroute should be called. This function can be used from BRANCH_ROUTE. appendospheaders usage ... branch_route[1] { log("Prepare route specific OSP information\n"); appendospheaders(); } ...
<function moreinfo="none">prepareallosproute()</function> This function tries to prepare all the routes in the list returned by the peering server. The message is then either forked off or redirected to the destination. If unsuccessful in preparing the routes a SIP 500 is sent back and a trace message is logged. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. prepareallosproute usage ... if (prepareallosproute()) { log("Routes are prepared, now either forking or redirecting the call\n"); } else { log("Could not prepare the routes. No destination available\n"); }; ...
<function moreinfo="none">reportospusage()</function> This function should be called after receiving a BYE message. If the message contains an OSP cookie, the function will forward originating and/or terminating duration usage information to a peering server. The function returns TRUE if the BYE includes an OSP cookie. The actual usage message will be send on a different thread and will not delay BYE processing. The function should be called before relaying the message. This function can be used from REQUEST_ROUTE. reportospusage usage ... if (reportospusage ()) { log("OSP call duration usage will be reported\n"); } else { log("The BYE message does not include OSP information, it was not authorized by an OSP server\n"); }; ...
kamailio-4.0.4/obsolete/osp/doc/osp_devel.xml0000644000000000000000000000053412223032460017663 0ustar rootroot Developer's Guide The functions of the OSP modules are not used by other SER modules. kamailio-4.0.4/obsolete/osp/doc/osp.xml0000644000000000000000000000240412223032460016502 0ustar rootroot %docentities; ]> OSP Module for Secure, Multi-Lateral Peering &sername; Ulrich Abend &fhg;
ullstar@iptel.org
Di-Shi Sun TransNexus, Inc.
support@transnexus.com
2003 &fhg;
&admin; &devel; &faq;
kamailio-4.0.4/obsolete/osp/doc/osp_faq.xml0000644000000000000000000000445012223032460017334 0ustar rootroot Frequently Asked Questions What platforms does this module work on? The module has been implemented using Linux, the underlying toolkit and the module code itself should compile and work on Solaris, *BSD, and probably most other unix platforms with ssl and pthreads available, but the OSP Module has only been tested Linux. Where can I get more information on this module? Please see http://www.iptel.org/views/moduledocs/ or post a message on the SER mailing list. Where can I get more information on OSP? The OSP technical specification (ETSI TS 101 321) may be obtained from www.etsi.org. You can also post a message on the OSP Toolkit mailing list at https://lists.sourceforge.net/lists/listinfo/osp-toolkit-client. How do I obtain an OSP server for testing? OSP peering servers are available from the following sources: OpenOSP is an OSP server reference implementation created by Cisco Systems and available at http://www.vovida.org/applications/downloads/openosp/ RAMS is an open source, java based OSP server project available from http://sourceforge.net/projects/rams/ A free version of the commercial TransNexus OSP peering server is available at www.transnexus.com. How are the exported functions used by the OSP module? See sample-osp-ser.cfg in modules/osp/etc for examples kamailio-4.0.4/obsolete/osp/doc/Makefile0000644000000000000000000000012412223032460016614 0ustar rootrootdocs = osp.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/osp/term_transaction.h0000644000000000000000000000323212223032460020137 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _OSP_MOD_TERM_TRANSACTION_H_ #define _OSP_MOD_TERM_TRANSACTION_H_ #include "../../parser/msg_parser.h" int ospCheckHeader(struct sip_msg*, char*, char*); int ospValidateHeader(struct sip_msg*, char*, char*); #endif /* _OSP_MOD_TERM_TRANSACTION_H_ */ kamailio-4.0.4/obsolete/osp/README0000644000000000000000000005054212223032460015300 0ustar rootrootOSP Module for Secure, Multi-Lateral Peering Ulrich Abend FhG FOKUS Edited by Di-Shi Sun TransNexus, Inc. Copyright © 2003 FhG FOKUS __________________________________________________________________ Table of Contents 1. User's Guide 1. Overview 2. Dependencies 3. Exported Parameters 3.1. sp1_uri, sp2_uri, ..., sp16_uri 3.2. device_ip 3.3. token_format 3.4. private_key, local_certificate, ca_certificates 3.5. sp1_weight, sp2_weight, ..., sp16_weight 3.6. device_port 3.7. enable_crypto_hardware_support 3.8. ssl_lifetime 3.9. persistence 3.10. retry_delay 3.11. retry_limit 3.12. timeout 3.13. max_destinations 3.14. validate_call_id 3.15. use_rpid_for_calling_number 4. Exported Functions 4.1. checkospheader() 4.2. validateospheader() 4.3. requestosprouting() 4.4. prepareospfirstroute() 4.5. prepareosprnextoute() 4.6. appendospheaders() 4.7. prepareallosproute() 4.8. reportospusage() 2. Developer's Guide 3. Frequently Asked Questions List of Examples 1.1. Setting the OSP servers 1.2. Setting the device IP address 1.3. Setting the token format 1.4. Set authorization files 1.5. Setting the OSP server weights 1.6. Setting the device port 1.7. Setting the hardware support 1.8. Setting the ssl lifetime 1.9. Setting the persistence 1.10. Setting the retry delay 1.11. Setting the retry limit 1.12. Setting the timeout 1.13. Setting the number of destination 1.14. Instructing the module to validate call id 1.15. Instructing the module to use calling number in Remote-Party-ID 1.16. checkospheader usage 1.17. validateospheader usage 1.18. requestosprouting usage 1.19. prepareospfirstroute usage 1.20. prepareospnextroute usage 1.21. appendospheaders usage 1.22. prepareallosproute usage 1.23. reportospusage usage Chapter 1. User's Guide Table of Contents 1. Overview 2. Dependencies 3. Exported Parameters 3.1. sp1_uri, sp2_uri, ..., sp16_uri 3.2. device_ip 3.3. token_format 3.4. private_key, local_certificate, ca_certificates 3.5. sp1_weight, sp2_weight, ..., sp16_weight 3.6. device_port 3.7. enable_crypto_hardware_support 3.8. ssl_lifetime 3.9. persistence 3.10. retry_delay 3.11. retry_limit 3.12. timeout 3.13. max_destinations 3.14. validate_call_id 3.15. use_rpid_for_calling_number 4. Exported Functions 4.1. checkospheader() 4.2. validateospheader() 4.3. requestosprouting() 4.4. prepareospfirstroute() 4.5. prepareosprnextoute() 4.6. appendospheaders() 4.7. prepareallosproute() 4.8. reportospusage() 1. Overview The OSP module enables SER to support secure, multi-lateral peering using the OSP standard defined by ETSI (TS 101 321 V4.1.1). This module will enable your SER to: * Send a peering authorization request to a peering server. * Validate a digitally signed peering authorization token received in a SIP INVITE message. * Report usage information to a peering server. 2. Dependencies The OSP module depends on the following modules which must be loaded before the OSP module. * sl -- stateless replier * tm -- stateful processing * rr -- Record-Route/Route operation * textops -- text based operation * avpops -- AVP operation * OSP Toolkit -- The OSP Toolkit, available from http://sourceforge.net/projects/osp-toolkit, must be built before building SER with the OSP Module. For instructions on building SER with the OSP Toolkit, see http://www.transnexus.com/White%20Papers/Multi-Lateral_Peering_with _SER_V2.0.x.pdf 3. Exported Parameters 3.1. sp1_uri, sp2_uri, ..., sp16_uri 3.2. device_ip 3.3. token_format 3.4. private_key, local_certificate, ca_certificates 3.5. sp1_weight, sp2_weight, ..., sp16_weight 3.6. device_port 3.7. enable_crypto_hardware_support 3.8. ssl_lifetime 3.9. persistence 3.10. retry_delay 3.11. retry_limit 3.12. timeout 3.13. max_destinations 3.14. validate_call_id 3.15. use_rpid_for_calling_number 3.1. sp1_uri, sp2_uri, ..., sp16_uri These sp_uri (string) parameters define peering servers to be used for requesting peering authorization and routing information. At least one peering server must be configured. Others are required only if there are more than one peering servers. Each peering server address takes the form of a standard URL, and consists of up to four components: * An optional indication of the protocol to be used for communicating with the peering server. Both HTTP and HTTP secured with SSL/TLS are supported and are indicated by "http://" and "https://" respectively. If the protocol is not explicitly indicated, the SER defaults to HTTP secured with SSL. * The Internet domain name for the peering server. An IP address may also be used, provided it is enclosed in square brackets such as [172.16.1.1]. * An optional TCP port number for communicating with the peering server. If the port number is omitted, the SER defaults to port 1080 (for HTTP) or port 1443 (for HTTP secured with SSL). The uniform resource identifier for requests to the peering server. This component is not optional and must be included. Example 1.1. Setting the OSP servers modparam("osp","sp1_uri","http://osptestserver.transnexus.com:1080/osp") modparam("osp","sp2_uri","https://[1.2.3.4]:1443/osp") 3.2. device_ip The device_ip (string) is a recommended parameter that explicitly defines the IP address of SER in a peering request message (as SourceAlternate type=transport). The IP address must be in brackets as shown in the example below. Example 1.2. Setting the device IP address modparam("osp","device_ip","[1.1.1.1]") 3.3. token_format When SER receives a SIP INVITE with a peering token, the OSP Module will validate the token to determine whether or not the call has been authorized by a peering server. Peering tokens may, or may not, be digitally signed. The token format (integer) parameter defines if SER will validate signed or unsigned tokens or both. The values for token format are defined below. The default value is 2. 0 - Validate only signed tokens. Calls with valid signed tokens are allowed. 1 - Validate only unsigned tokens. Calls with valid unsigned tokens are allowed. 2 - Validate both signed and unsigned tokens are allowed. Calls with valid tokens are allowed. Example 1.3. Setting the token format modparam("osp","token_format",2) 3.4. private_key, local_certificate, ca_certificates These parameters identify files are used for validating peering authorization tokens and establishing a secure channel between SER and a peering server using SSL. The files are generated using the 'Enroll' utility from the OSP Toolkit. By default, the proxy will look for pkey.pem, localcert.pem, and cacart_0.pem in the default configuration directory. The default config directory is set at compile time using CFG_DIR and defaults to /usr/local/etc/ser/. The files may be copied to the expected file location or the parameters below may be changed. Example 1.4. Set authorization files If the default CFG_DIR value was used at compile time, the files will be loaded from: modparam("osp","private_key","/usr/local/etc/ser/pkey.pem") modparam("osp","local_certificate","/usr/local/etc/ser/localcert.pem") modparam("osp","ca_certificates","/usr/local/etc/ser/cacert.pem") 3.5. sp1_weight, sp2_weight, ..., sp16_weight These sp_weight (integer) parameters are used for load balancing peering requests to peering servers. These parameters are most effective when configured as factors of 1000. For example, if sp1_uri should manage twice the traffic load of sp2_uri, then set sp1_weight to 2000 and sp2_weight to 1000. Shared load balancing between peering servers is recommended. However, peering servers can be configured as primary and backup by assigning a sp_weight of 0 to the primary server and a non-zero sp_weight to the back-up server. The default values for sp1_weight and sp2_weight are 1000. Example 1.5. Setting the OSP server weights modparam("osp","sp1_weight",1000) 3.6. device_port The device_port (string) parameter is an optional field which includes the SIP port being used by SER in the peering request (as SourceAlternate type=network) to the peering server. If is not configured, this information is not included in the peering request. This field is useful if multiple SERs are running on the same Linux computer since it enables the peering server to administer different peering policies based on different SIP proxies. This parameter has not been implemented yet. Example 1.6. Setting the device port modparam("osp","device_port","5060") 3.7. enable_crypto_hardware_support The enable_crypto_hardware_support (integer) parameter is used to set the cryptographic hardware acceleration engine in the openssl library. The default value is 0 (no crypto hardware is present). If crypto hardware is used, the value should be set to 1. Example 1.7. Setting the hardware support modparam("osp","enable_crypto_hardware_support",0) 3.8. ssl_lifetime The ssl_lifetime (integer) parameter defines the lifetime, in seconds, of a single SSL session key. Once this time limit is exceeded, the OSP Module will negotiate a new session key. Communication exchanges in progress will not be interrupted when this time limit expires. This is an optional field with default value is 200 seconds. Example 1.8. Setting the ssl lifetime modparam("osp","ssl_lifetime",200) 3.9. persistence The persistence (integer) parameter defines the time, in seconds, that an HTTP connection should be maintained after the completion of a communication exchange. The OSP Module will maintain the connection for this time period in anticipation of future communication exchanges to the same peering server. Example 1.9. Setting the persistence modparam("osp","persistence",1000) 3.10. retry_delay The retry_delay (integer) parameter defines the time, in seconds, between retrying connection attempts to an OSP peering server. After exhausting all peering servers the OSP Module will delay for this amount of time before resuming connection attempts. This is an optional field with default value is 1 second. Example 1.10. Setting the retry delay modparam("osp","retry_delay",1) 3.11. retry_limit The retry_limit (integer) parameter defines the maximum number of retries for connection attempts to a peering server. If no connection is established after this many retry attempts to all peering servers, the OSP Module will cease connection attempts and return appropriate error codes. This number does not count the initial connection attempt, so that a retry_limit of 1 will result in a total of two connection attempts to every peering server. The default value is 2. Example 1.11. Setting the retry limit modparam("osp","retry_limit",2) 3.12. timeout The timeout (integer) parameter defines the maximum time in milliseconds, to wait for a response from a peering server. If no response is received within this time, the current connection is aborted and the OSP Module attempts to contact the next peering server. The default value is 10 seconds. Example 1.12. Setting the timeout modparam("osp","timeout",10) 3.13. max_destinations The max_destinations (integer) parameter defines the maximum number of destinations that SER requests the peering server to return in a peering response. The default value is 5. Example 1.13. Setting the number of destination modparam("osp","max_destinations",5) 3.14. validate_call_id The validate_call_id (integer) parameter instructs the OSP module to validate call id in the peering token. If this value is set to 1, the OSP Module validates that the call id in the SIP INVITE message matches the call id in the peering token. If they do not match the INVITE is rejected. If this value is set to 0, the OSP Module will not validate the call id in the peering token. The default value is 1. Example 1.14. Instructing the module to validate call id modparam("osp","validate_call_id",1) 3.15. use_rpid_for_calling_number The use_rpid_for_calling_number(integer) parameter instructs the OSP module to use the calling number in the Remote-Party-ID of the SIP INVITE message. If this value is set to 1, the OSP Module uses the calling number in the Reomte-Party-ID header of the INVITE message when a Remote-Party-ID exists. If this value is set to 0, the OSP Module will use the calling number in the From header of the INVITE message. The default value is 1. Example 1.15. Instructing the module to use calling number in Remote-Party-ID modparam("osp","use_rpid_calling_number",1) 4. Exported Functions 4.1. checkospheader() 4.2. validateospheader() 4.3. requestosprouting() 4.4. prepareospfirstroute() 4.5. prepareosprnextoute() 4.6. appendospheaders() 4.7. prepareallosproute() 4.8. reportospusage() 4.1. checkospheader() This function checks for the existence of the OSP-Auth-Token header field. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.16. checkospheader usage ... if (checkospheader()) { log("OSP header field found.\n"); } else { log("no OSP header field present\n"); }; ... 4.2. validateospheader() This function validates an OSP-Token specified in the OSP-Auth-Tokenheader field of the SIP message. If a peering token is present, it will be validated locally. If no OSP header is found or the header token is invalid or expired, -1 is returned; on successful validation 1 is returned. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.17. validateospheader usage ... if (validateospheader()) { log("valid OSP header found\n"); } else { log("OSP header not found, invalid or expired\n"); }; ... 4.3. requestosprouting() This function launches a query to the peering server requesting the IP address of one or more destination peers serving the called party. If destination peers are available, the peering server will return the IP address and a peering authorization token for each destination peer. The OSP-Auth-Token Header field is inserted into the SIP message and the SIP uri is rewritten to the IP address of destination peer provided by the peering server. The address of the called party must be a valid E164 number, otherwise this function returns -1. If the transaction was accepted by the peering server, the uri is being rewritten and 1 returned, on errors (peering servers are not available, authentication failed or there is no route to destination or the route is blocked) -1 is returned. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.18. requestosprouting usage ... if (requestosprouting()) { log("successfully queried OSP server, now relaying call\n"); } else { log("Authorization request was rejected from OSP server\n"); }; ... 4.4. prepareospfirstroute() This function tries to prepare the INVITE to be forwarded or redirected using the first destination in the list returned by the peering server. If the route could not be prepared, the function returns 'FALSE' back to the script, which can then decide how to handle the failure. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.19. prepareospfirstroute usage ... if (prepareospfirstroute ()) { log("successfully prepared the first route, now relaying call\n"); } else { log("could not prepare the route. The first destination was blocked\n"); }; ... 4.5. prepareosprnextoute() Once the call could not be completed through the first destination, this function tries to prepare the INVITE message using the next destination in the list returned by the peering Server. If it succeeds in preparing the route, the message is either redirected or relayed on (using the t_relay call), or else the function returns 'FALSE' back to the script, which can then decide how to handle the failure. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.20. prepareospnextroute usage ... if (prepareospnextroute ()) { log("successfully prepared the next route, now relaying call\n"); } else { log("could not prepare the route. No next destination available\n"); }; ... 4.6. appendospheaders() This function is used to append route specific OSP headers. Before calling appendospheaders, prepareospfirst/nextroute should be called. This function can be used from BRANCH_ROUTE. Example 1.21. appendospheaders usage ... branch_route[1] { log("Prepare route specific OSP information\n"); appendospheaders(); } ... 4.7. prepareallosproute() This function tries to prepare all the routes in the list returned by the peering server. The message is then either forked off or redirected to the destination. If unsuccessful in preparing the routes a SIP 500 is sent back and a trace message is logged. This function can be used from REQUEST_ROUTE and FAILURE_ROUTE. Example 1.22. prepareallosproute usage ... if (prepareallosproute()) { log("Routes are prepared, now either forking or redirecting the call\n"); } else { log("Could not prepare the routes. No destination available\n"); }; ... 4.8. reportospusage() This function should be called after receiving a BYE message. If the message contains an OSP cookie, the function will forward originating and/or terminating duration usage information to a peering server. The function returns TRUE if the BYE includes an OSP cookie. The actual usage message will be send on a different thread and will not delay BYE processing. The function should be called before relaying the message. This function can be used from REQUEST_ROUTE. Example 1.23. reportospusage usage ... if (reportospusage ()) { log("OSP call duration usage will be reported\n"); } else { log("The BYE message does not include OSP information, it was not authorized b y an OSP server\n"); }; ... Chapter 2. Developer's Guide The functions of the OSP modules are not used by other SER modules. Chapter 3. Frequently Asked Questions 3.1. What platforms does this module work on? 3.2. Where can I get more information on this module? 3.3. Where can I get more information on OSP? 3.4. How do I obtain an OSP server for testing? 3.5. How are the exported functions used by the OSP module? 3.1. What platforms does this module work on? The module has been implemented using Linux, the underlying toolkit and the module code itself should compile and work on Solaris, *BSD, and probably most other unix platforms with ssl and pthreads available, but the OSP Module has only been tested Linux. 3.2. Where can I get more information on this module? Please see http://www.iptel.org/views/moduledocs/ or post a message on the SER mailing list. 3.3. Where can I get more information on OSP? The OSP technical specification (ETSI TS 101 321) may be obtained from www.etsi.org. You can also post a message on the OSP Toolkit mailing list at https://lists.sourceforge.net/lists/listinfo/osp-toolkit-client. 3.4. How do I obtain an OSP server for testing? OSP peering servers are available from the following sources: * OpenOSP is an OSP server reference implementation created by Cisco Systems and available at http://www.vovida.org/applications/downloads/openosp/ * RAMS is an open source, java based OSP server project available from http://sourceforge.net/projects/rams/ * A free version of the commercial TransNexus OSP peering server is available at www.transnexus.com. 3.5. How are the exported functions used by the OSP module? See sample-osp-ser.cfg in modules/osp/etc for examples kamailio-4.0.4/obsolete/osp/osp_mod.h0000644000000000000000000000477412223032460016237 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _OSP_MOD_H_ #define _OSP_MOD_H_ #define MODULE_RETURNCODE_TRUE 1 #define MODULE_RETURNCODE_STOPROUTE 0 #define MODULE_RETURNCODE_FALSE -1 #define OSP_DEF_SPS 16 #define OSP_DEF_WEIGHT 1000 #define OSP_DEF_HW 0 #define OSP_DEF_CALLID 1 /* Validate call ids, set to 0 to disable */ #define OSP_DEF_TOKEN 2 #define OSP_DEF_SSLLIFE 300 #define OSP_DEF_PERSISTENCE (60 * 1000) #define OSP_DEF_DELAY 0 #define OSP_DEF_RETRY 2 #define OSP_DEF_TIMEOUT (60 * 1000) #define OSP_DEF_DESTS 5 #define OSP_DEF_USERPID 1 #define OSP_DEF_REDIRURI 0 /* 0 for "xxxxxxxxxx@xxx.xxx.xxx.xxx", 1 for "" format */ #define OSP_KEYBUF_SIZE 256 #define OSP_STRBUF_SIZE 256 #define OSP_E164BUF_SIZE 1024 #define OSP_TOKENBUF_SIZE 2048 #define OSP_HEADERBUF_SIZE 3072 #endif /* _OSP_MOD_H_ */ kamailio-4.0.4/obsolete/osp/term_transaction.c0000644000000000000000000001530012223032460020131 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "osp_mod.h" #include "term_transaction.h" #include "sipheader.h" #include "destination.h" #include "osptoolkit.h" #include "usage.h" extern char* _osp_device_ip; extern int _osp_token_format; extern int _osp_validate_callid; extern OSPTPROVHANDLE _osp_provider; /* * Get OSP token * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospCheckHeader( struct sip_msg* msg, char* ignore1, char* ignore2) { unsigned char buffer[OSP_TOKENBUF_SIZE]; unsigned int buffersize = sizeof(buffer); LOG(L_DBG, "osp: ospCheckHeader\n"); if (ospGetOspHeader(msg, buffer, &buffersize) != 0) { return MODULE_RETURNCODE_FALSE; } else { return MODULE_RETURNCODE_TRUE; } } /* * Validate OSP token * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospValidateHeader ( struct sip_msg* msg, char* ignore1, char* ignore2) { int errorcode; OSPTTRANHANDLE transaction = -1; unsigned int authorized = 0; unsigned int timelimit = 0; void* detaillog = NULL; unsigned int logsize = 0; unsigned char* callidval = (unsigned char*)""; OSPTCALLID* callid = NULL; unsigned callidsize = 0; unsigned char token[OSP_TOKENBUF_SIZE]; unsigned int tokensize = sizeof(token); osp_dest dest; int result = MODULE_RETURNCODE_FALSE; LOG(L_DBG, "osp: ospValidateHeader\n"); ospInitDestination(&dest); if ((errorcode = OSPPTransactionNew(_osp_provider, &transaction) != OSPC_ERR_NO_ERROR)) { LOG(L_ERR, "osp: ERROR: failed to create a new OSP transaction handle (%d)\n", errorcode); } else if ((ospGetRpidUserpart(msg, dest.calling, sizeof(dest.calling)) != 0) && (ospGetFromUserpart(msg, dest.calling, sizeof(dest.calling)) != 0)) { LOG(L_ERR, "osp: ERROR: failed to extract calling number\n"); } else if ((ospGetUriUserpart(msg, dest.called, sizeof(dest.called)) != 0) && (ospGetToUserpart(msg, dest.called, sizeof(dest.called)) != 0)) { LOG(L_ERR, "osp: ERROR: failed to extract called number\n"); } else if (ospGetCallId(msg, &callid) != 0) { LOG(L_ERR, "osp: ERROR: failed to extract call id\n"); } else if (ospGetSourceAddress(msg, dest.source, sizeof(dest.source)) != 0) { LOG(L_ERR, "osp: ERROR: failed to extract source address\n"); } else if (ospGetOspHeader(msg, token, &tokensize) != 0) { LOG(L_ERR, "osp: ERROR: failed to extract OSP authorization token\n"); } else { LOG(L_INFO, "osp: validate token for: " "transaction_handle '%i' " "e164_source '%s' " "e164_dest '%s' " "validate_call_id '%s' " "call_id '%.*s'\n", transaction, dest.calling, dest.called, _osp_validate_callid == 0 ? "No" : "Yes", callid->ospmCallIdLen, callid->ospmCallIdVal); if (_osp_validate_callid != 0) { callidsize = callid->ospmCallIdLen; callidval = callid->ospmCallIdVal; } errorcode = OSPPTransactionValidateAuthorisation( transaction, "", "", "", "", dest.calling, OSPC_E164, dest.called, OSPC_E164, callidsize, callidval, tokensize, token, &authorized, &timelimit, &logsize, detaillog, _osp_token_format); if (callid->ospmCallIdLen > sizeof(dest.callid) - 1) { dest.callidsize = sizeof(dest.callid) - 1; } else { dest.callidsize = callid->ospmCallIdLen; } memcpy(dest.callid, callid->ospmCallIdVal, dest.callidsize); dest.callid[dest.callidsize] = 0; dest.transid = ospGetTransactionId(transaction); dest.type = OSPC_DESTINATION; dest.authtime = time(NULL); strncpy(dest.host, _osp_device_ip, sizeof(dest.host) - 1); ospSaveTermDestination(&dest); if ((errorcode == OSPC_ERR_NO_ERROR) && (authorized == 1)) { LOG(L_DBG, "osp: call is authorized for %d seconds, call_id '%.*s' transaction_id '%llu'", timelimit, dest.callidsize, dest.callid, dest.transid); ospRecordTermTransaction(msg, dest.transid, dest.source, dest.calling, dest.called, dest.authtime); result = MODULE_RETURNCODE_TRUE; } else { LOG(L_ERR, "osp: ERROR: token is invalid (%i)\n", errorcode); /* * Update terminating status code to 401 and report terminating setup usage. * We may need to make 401 configurable, just in case a user decides to reply with * a different code. Other options - trigger call setup usage reporting from the cpl * (after replying with an error code), or maybe use a different tm callback. */ ospRecordEvent(0, 401); } } if (transaction != -1) { OSPPTransactionDelete(transaction); } if (callid != NULL) { OSPPCallIdDelete(&callid); } return result; } kamailio-4.0.4/obsolete/osp/tm.h0000644000000000000000000000325012223032460015203 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _OSP_MOD_TM_H_ #define _OSP_MOD_TM_H_ /* * Register for tm events and use them to record and report information * about the call set up transaction - return codes, call start, alert and * connect times, etc. */ int ospInitTm(void); #endif /* _OSP_MOD_TM_H_ */ kamailio-4.0.4/obsolete/osp/sipheader.h0000644000000000000000000000515412223032460016534 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _OSP_MOD_SIPHEADER_H_ #define _OSP_MOD_SIPHEADER_H_ #include #include "../../parser/msg_parser.h" #define OSP_TOKEN_HEADER "P-OSP-Auth-Token: " #define OSP_HEADER_SIZE strlen(OSP_TOKEN_HEADER) void ospCopyStrToBuffer(str* source, char* buffer, int buffersize); int ospGetFromUserpart(struct sip_msg* msg, char* fromuser, int buffersize); int ospGetRpidUserpart(struct sip_msg* msg, char* fromuser, int buffersize); int ospGetToUserpart(struct sip_msg* msg, char* touser, int buffersize); int ospGetUriUserpart(struct sip_msg* msg, char* touser, int buffersize); int ospAddOspHeader(struct sip_msg* msg, unsigned char* token, unsigned int tokensize); int ospGetOspHeader(struct sip_msg* msg, unsigned char* token, unsigned int* tokensize); int ospGetSourceAddress(struct sip_msg* msg, char* sourceaddress, int buffersize); int ospGetCallId(struct sip_msg* msg, OSPTCALLID** callid); int ospGetRouteParameters(struct sip_msg* msg, char* routeparams, int buffersize); int ospRebuildDestionationUri(str* newuri, char* called, char* dest, char* port, int format); void ospGetNextHop(struct sip_msg* msg, char* nexthop, int buffersize); int ospGetDirection(struct sip_msg* msg); #endif /* _OSP_MOD_SIPHEADER_H_ */ kamailio-4.0.4/obsolete/osp/orig_transaction.h0000644000000000000000000000356712223032460020143 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _OSP_MOD_ORIG_TRANSACTION_H_ #define _OSP_MOD_ORIG_TRANSACTION_H_ #include "../../parser/msg_parser.h" int ospRequestRouting(struct sip_msg*, char*, char*); int ospCheckRoute(struct sip_msg*, char*, char*); int ospPrepareFirstRoute(struct sip_msg*, char*, char*); int ospPrepareNextRoute(struct sip_msg*, char*, char*); int ospPrepareAllRoutes(struct sip_msg*, char*, char*); int ospAppendHeaders(struct sip_msg*, char*, char*); #endif /* _OSP_MOD_ORIG_TRANSACTION_H_ */ kamailio-4.0.4/obsolete/osp/osptoolkit.c0000644000000000000000000001421712223032460016772 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "../../dprint.h" #include "osptoolkit.h" static OSPTTHREADRETURN ospReportUsageWork(void* usagearg); typedef struct _osp_usage { OSPTTRANHANDLE ospvTransaction; /* Transaction handle */ unsigned ospvReleaseCode; /* Release code */ unsigned ospvDuration; /* Length of call */ time_t ospvStartTime; /* Call start time */ time_t ospvEndTime; /* Call end time */ time_t ospvAlertTime; /* Call alert time */ time_t ospvConnectTime; /* Call connect time */ unsigned ospvIsPDDInfoPresent; /* Is PDD Info present */ unsigned ospvPostDialDelay; /* Post Dial Delay */ unsigned ospvReleaseSource; /* EP that released the call */ } osp_usage; /* * Get OSP transaction ID from transaction handle * param transaction OSP transaction headle * return OSP transaction ID */ unsigned long long ospGetTransactionId( OSPTTRANHANDLE transaction) { OSPTTRANS* context = NULL; unsigned long long id = 0; int errorcode = OSPC_ERR_NO_ERROR; LOG(L_DBG, "osp: ospGetTransactionId\n"); context = OSPPTransactionGetContext(transaction, &errorcode); if (errorcode == OSPC_ERR_NO_ERROR) { id = (unsigned long long)context->TransactionID; } else { LOG(L_ERR, "osp: ERROR: failed to extract transaction_id from transaction handle %d (%d)\n", transaction, errorcode); } return id; } /* * Create a thread to report OSP usage * param ospvTransaction OSP transaction handle * param ospvReleaseCode Call release reason * param ospvDurating Call duration * param ospvStartTime Call start time * param ospvEndTime Call end time * param ospvAlertTime Call alert time * param ospvConnectTime Call connected time * param ospvIsPDDInfoPresent If post dial delay information avaliable * param ospvPostDialDelay Post dial delay information * param ospvReleaseSource Which side release the call */ void ospReportUsageWrapper( OSPTTRANHANDLE ospvTransaction, unsigned ospvReleaseCode, unsigned ospvDuration, time_t ospvStartTime, time_t ospvEndTime, time_t ospvAlertTime, time_t ospvConnectTime, unsigned ospvIsPDDInfoPresent, unsigned ospvPostDialDelay, unsigned ospvReleaseSource) { osp_usage* usage; OSPTTHREADID threadid; OSPTTHRATTR threadattr; int errorcode; LOG(L_DBG, "osp: ospReportUsageWrapper\n"); LOG(L_DBG, "osp: schedule usage report for '%llu'\n", ospGetTransactionId(ospvTransaction)); usage = (osp_usage*)malloc(sizeof(osp_usage)); usage->ospvTransaction = ospvTransaction; usage->ospvReleaseCode = ospvReleaseCode; usage->ospvDuration = ospvDuration; usage->ospvStartTime = ospvStartTime; usage->ospvEndTime = ospvEndTime; usage->ospvAlertTime = ospvAlertTime; usage->ospvConnectTime = ospvConnectTime; usage->ospvIsPDDInfoPresent = ospvIsPDDInfoPresent; usage->ospvPostDialDelay = ospvPostDialDelay; usage->ospvReleaseSource = ospvReleaseSource; OSPM_THRATTR_INIT(threadattr, errorcode); OSPM_SETDETACHED_STATE(threadattr, errorcode); OSPM_CREATE_THREAD(threadid, &threadattr, ospReportUsageWork, usage, errorcode); OSPM_THRATTR_DESTROY(threadattr); } /* * Report OSP usage thread function * param usagearg OSP usage information * return */ static OSPTTHREADRETURN ospReportUsageWork( void* usagearg) { int i; const int MAX_RETRIES = 5; osp_usage* usage; int errorcode; LOG(L_DBG, "osp: ospReportUsageWork\n"); usage = (osp_usage*)usagearg; OSPPTransactionRecordFailure( usage->ospvTransaction, (enum OSPEFAILREASON)usage->ospvReleaseCode); for (i = 1; i <= MAX_RETRIES; i++) { errorcode = OSPPTransactionReportUsage( usage->ospvTransaction, usage->ospvDuration, usage->ospvStartTime, usage->ospvEndTime, usage->ospvAlertTime, usage->ospvConnectTime, usage->ospvIsPDDInfoPresent, usage->ospvPostDialDelay, usage->ospvReleaseSource, (unsigned char*)"", 0, 0, 0, 0, NULL, NULL); if (errorcode == OSPC_ERR_NO_ERROR) { LOG(L_DBG, "osp: reporte usage for '%llu'\n", ospGetTransactionId(usage->ospvTransaction)); break; } else { LOG(L_ERR, "osp: ERROR: failed to report usage for '%llu' (%d) attempt '%d' of '%d'\n", ospGetTransactionId(usage->ospvTransaction), errorcode, i, MAX_RETRIES); } } OSPPTransactionDelete(usage->ospvTransaction); free(usage); OSPTTHREADRETURN_NULL(); } kamailio-4.0.4/obsolete/osp/destination.c0000644000000000000000000003146012223032460017103 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "../../str.h" #include "../../dprint.h" #include "../../usr_avp.h" #include "destination.h" #include "usage.h" /* Name of AVP of OSP destination list */ const str OSP_ORIGDEST_NAME = {"_osp_orig_dests_", 16}; const str OSP_TERMDEST_NAME = {"_osp_term_dests_", 16}; static int ospSaveDestination(osp_dest* dest, const str* name); static void ospRecordCode(int code, osp_dest* dest); static int ospIsToReportUsage(int code); /* * Initialize destination structure * param dest Destination data structure * return initialized destination sturcture */ osp_dest* ospInitDestination( osp_dest* dest) { LOG(L_DBG, "osp: ospInitDestion\n"); memset(dest, 0, sizeof(osp_dest)); dest->callidsize = sizeof(dest->callid); dest->tokensize = sizeof(dest->token); LOG(L_DBG, "osp: callidsize '%d' tokensize '%d'\n", dest->callidsize, dest->tokensize); return dest; } /* * Save destination as an AVP * name - OSP_ORIGDEST_NAME / OSP_TERMDEST_NAME * value - osp_dest wrapped in a string * param dest Destination structure * param name Name of AVP * return 0 success, -1 failure */ static int ospSaveDestination( osp_dest* dest, const str* name) { str wrapper; int result = -1; LOG(L_DBG, "osp: ospSaveDestination\n"); wrapper.s = (char*)dest; wrapper.len = sizeof(osp_dest); /* * add_avp will make a private copy of both the name and value in shared memory * which will be released by TM at the end of the transaction */ if (add_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)*name, (int_str)wrapper) == 0) { result = 0; LOG(L_DBG, "osp: destination saved\n"); } else { LOG(L_ERR, "osp: ERROR: failed to save destination\n"); } return result; } /* * Save originate destination * param dest Originate destination structure * return 0 success, -1 failure */ int ospSaveOrigDestination( osp_dest* dest) { LOG(L_DBG, "osp: ospSaveOrigDestination\n"); return ospSaveDestination(dest, &OSP_ORIGDEST_NAME); } /* * Save terminate destination * param dest Terminate destination structure * return 0 success, -1 failure */ int ospSaveTermDestination( osp_dest* dest) { LOG(L_DBG, "osp: ospSaveTermDestination\n"); return ospSaveDestination(dest, &OSP_TERMDEST_NAME); } /* * Check if there is an unused and supported originate destination from an AVP * name - OSP_ORIGDEST_NAME * value - osp_dest wrapped in a string * search unused (used==0) & supported (support==1) * return 0 success, -1 failure */ int ospCheckOrigDestination(void) { struct usr_avp* destavp = NULL; int_str destval; struct search_state state; osp_dest* dest = NULL; int result = -1; LOG(L_DBG, "osp: ospCheckOrigDestination\n"); for (destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_ORIGDEST_NAME, &destval, &state); destavp != NULL; destavp = search_next_avp(&state, &destval)) { /* OSP destintaion is wrapped in a string */ dest = (osp_dest*)destval.s.s; if (dest->used == 0) { if (dest->supported == 1) { LOG(L_DBG, "osp: orig dest exist\n"); result = 0; break; } else { LOG(L_DBG, "osp: destination does not been supported\n"); } } else { LOG(L_DBG, "osp: destination has already been used\n"); } } if (result == -1) { LOG(L_DBG, "osp: there is not unused destination\n"); } return result; } /* * Retrieved an unused and supported originate destination from an AVP * name - OSP_ORIGDEST_NAME * value - osp_dest wrapped in a string * There can be 0, 1 or more originate destinations. * Find the 1st unused destination (used==0) & supported (support==1), * return it, and mark it as used (used==1). * return NULL on failure */ osp_dest* ospGetNextOrigDestination(void) { struct usr_avp* destavp = NULL; int_str destval; struct search_state state; osp_dest* dest = NULL; osp_dest* result = NULL; LOG(L_DBG, "osp: ospGetNextOrigDestination\n"); for (destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_ORIGDEST_NAME, &destval, &state); destavp != NULL; destavp = search_next_avp(&state, &destval)) { /* OSP destintaion is wrapped in a string */ dest = (osp_dest*)destval.s.s; if (dest->used == 0) { if (dest->supported == 1) { LOG(L_DBG, "osp: orig dest found\n"); dest->used = 1; result = dest; break; } else { /* Make it looks like used */ dest->used = 1; /* 111 means wrong protocol */ dest->lastcode = 111; LOG(L_DBG, "osp: destination does not been supported\n"); } } else { LOG(L_DBG, "osp: destination has already been used\n"); } } if (result == NULL) { LOG(L_DBG, "osp: there is not unused destination\n"); } return result; } /* * Retrieved the last used originate destination from an AVP * name - OSP_ORIGDEST_NAME * value - osp_dest wrapped in a string * There can be 0, 1 or more destinations. * Find the last used destination (used==1) & supported (support==1), * and return it. * In normal condition, this one is the current destination. But it may * be wrong for loop condition. * return NULL on failure */ osp_dest* ospGetLastOrigDestination(void) { struct usr_avp* destavp = NULL; int_str destval; struct search_state state; osp_dest* dest = NULL; osp_dest* lastdest = NULL; LOG(L_DBG, "osp: ospGetLastOrigDesintaion\n"); for (destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_ORIGDEST_NAME, &destval, &state); destavp != NULL; destavp = search_next_avp(&state, &destval)) { /* OSP destination is wrapped in a string */ dest = (osp_dest*)destval.s.s; if (dest->used == 1) { if (dest->supported == 1) { lastdest = dest; LOG(L_DBG, "osp: curent destination '%s'\n", lastdest->host); } } else { break; } } return lastdest; } /* * Retrieved the terminate destination from an AVP * name - OSP_TERMDEST_NAME * value - osp_dest wrapped in a string * There can be 0 or 1 term destinations. Find and return it. * return NULL on failure (no terminate destination) */ osp_dest* ospGetTermDestination(void) { struct usr_avp* destavp = NULL; int_str destval; osp_dest* dest = NULL; LOG(L_DBG, "osp: ospGetTermDestination\n"); destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_TERMDEST_NAME, &destval, NULL); if (destavp) { /* OSP destination is wrapped in a string */ dest = (osp_dest*)destval.s.s; LOG(L_DBG, "osp: term dest found\n"); } return dest; } /* * Record destination status * param code Destination status * param dest Destination */ static void ospRecordCode( int code, osp_dest* dest) { LOG(L_DBG, "osp: ospRecordCode\n"); LOG(L_DBG, "osp: code '%d'\n", code); dest->lastcode = code; switch (code) { case 100: if (!dest->time100) { dest->time100 = time(NULL); } else { LOG(L_DBG, "osp: 100 already recorded\n"); } break; case 180: case 181: case 182: case 183: if (!dest->time180) { dest->time180 = time(NULL); } else { LOG(L_DBG, "osp: 180, 181, 182 or 183 allready recorded\n"); } break; case 200: case 202: if (!dest->time200) { dest->time200 = time(NULL); } else { LOG(L_DBG, "osp: 200 or 202 allready recorded\n"); } break; default: LOG(L_DBG, "osp: will not record time for '%d'\n", code); } } /* * Check destination status for reporting usage * param code Destination status * return 1 should report, 0 should not report */ static int ospIsToReportUsage( int code) { int istime = 0; LOG(L_DBG, "osp: ospIsToReportUsage\n"); LOG(L_DBG, "osp: code '%d'\n", code); if (code >= 200) { istime = 1; } return istime; } /* * Report call setup usage for both client and server side * param clientcode Client status * param servercode Server status */ void ospRecordEvent( int clientcode, int servercode) { osp_dest* dest; LOG(L_DBG, "osp: ospRecordEvent\n"); LOG(L_DBG, "osp: client status '%d'\n", clientcode); if ((clientcode != 0) && (dest = ospGetLastOrigDestination())) { ospRecordCode(clientcode, dest); if (ospIsToReportUsage(servercode) == 1) { ospReportOrigSetupUsage(); } } LOG(L_DBG, "osp: server status '%d'\n", servercode); if ((servercode != 0) && (dest = ospGetTermDestination())) { ospRecordCode(servercode, dest); if (ospIsToReportUsage(servercode) == 1) { ospReportTermSetupUsage(); } } } /* * Dump destination information * param dest Destination */ void ospDumpDestination( osp_dest* dest) { LOG(L_DBG, "osp: dest->host..........'%s'\n", dest->host); LOG(L_DBG, "osp: dest->used..........'%d'\n", dest->used); LOG(L_DBG, "osp: dest->lastcode......'%d'\n", dest->lastcode); LOG(L_DBG, "osp: dest->time100.......'%d'\n", (unsigned int)dest->time100); LOG(L_DBG, "osp: dest->time180.......'%d'\n", (unsigned int)dest->time180); LOG(L_DBG, "osp: dest->time200.......'%d'\n", (unsigned int)dest->time200); } /* * Dump all destination information */ void ospDumpAllDestination(void) { struct usr_avp* destavp = NULL; int_str destval; struct search_state state; osp_dest* dest = NULL; int count = 0; LOG(L_DBG, "osp: ospDumpAllDestination\n"); for (destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_ORIGDEST_NAME, &destval, &state); destavp != NULL; destavp = search_next_avp(&state, &destval)) { /* OSP destination is wrapped in a string */ dest = (osp_dest*)destval.s.s; LOG(L_DBG, "osp: ....originate '%d'....\n", count++); ospDumpDestination(dest); } if (count == 0) { LOG(L_DBG, "osp: there is not originate destination AVP\n"); } destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_TERMDEST_NAME, &destval, NULL); if (destavp) { /* OSP destination is wrapped in a string */ dest = (osp_dest*)destval.s.s; LOG(L_DBG, "osp: ....terminate....\n"); ospDumpDestination(dest); } else { LOG(L_DBG, "osp: there is not terminate destination AVP\n"); } } /* * Convert address to "[x.x.x.x]" or "host.domain" format * param src Source address * param dst Destination address * param buffersize Size of dst buffer */ void ospConvertAddress( char* src, char* dst, int buffersize) { struct in_addr inp; LOG(L_DBG, "osp: ospConvertAddress\n"); if (inet_aton(src, &inp) != 0) { snprintf(dst, buffersize, "[%s]", src); } else { snprintf(dst, buffersize, "%s", src); } } kamailio-4.0.4/obsolete/osp/sipheader.c0000644000000000000000000004760412223032460016535 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "../../trim.h" #include "../../forward.h" #include "../../parser/parse_from.h" #include "../../parser/parse_rpid.h" #include "../../parser/parse_rr.h" #include "../../parser/parse_uri.h" #include "../../data_lump.h" #include "../../mem/mem.h" #include "osp_mod.h" #include "sipheader.h" extern int _osp_use_rpid; static void ospSkipPlus(char* e164); static int ospAppendHeader(struct sip_msg* msg, str* header); /* * Copy str to buffer and check overflow * param source Str * param buffer Buffer * param buffersize Size of buffer */ void ospCopyStrToBuffer( str* source, char* buffer, int buffersize) { int copybytes; LOG(L_DBG, "osp: ospCopyStrToBuffer\n"); if (source->len > buffersize - 1) { LOG(L_ERR, "osp: ERROR: buffer for copying '%.*s' is too small, will copy the first '%d' bytes\n", source->len, source->s, buffersize); copybytes = buffersize - 1; } else { copybytes = source->len; } strncpy(buffer, source->s, copybytes); buffer[copybytes] = '\0'; } /* * Remove '+' in E164 string * param e164 E164 string */ static void ospSkipPlus( char* e164) { LOG(L_DBG, "osp: ospSkipPlus\n"); if (*e164 == '+') { strncpy(e164, e164 + 1, strlen(e164) - 1); e164[strlen(e164) - 1] = '\0'; } } /* * Get calling number from From header * param msg SIP message * param fromuser User part of From header * param buffersize Size of fromuser buffer * return 0 success, -1 failure */ int ospGetFromUserpart( struct sip_msg* msg, char* fromuser, int buffersize) { struct to_body* from; struct sip_uri uri; int result = -1; LOG(L_DBG, "osp: ospGetFromUserpart\n"); fromuser[0] = '\0'; if (msg->from != NULL) { if (parse_from_header(msg) == 0) { from = get_from(msg); if (parse_uri(from->uri.s, from->uri.len, &uri) == 0) { ospCopyStrToBuffer(&uri.user, fromuser, buffersize); ospSkipPlus(fromuser); result = 0; } else { LOG(L_ERR, "osp: ERROR: failed to parse From uri\n"); } } else { LOG(L_ERR, "osp: ERROR: failed to parse From header\n"); } } else { LOG(L_ERR, "osp: ERROR: failed to find From header\n"); } return result; } /* * Get calling number from Remote-Party-ID header * param msg SIP message * param rpiduser User part of Remote-Party-ID header * param buffersize Size of fromuser buffer * return 0 success, -1 failure */ int ospGetRpidUserpart( struct sip_msg* msg, char* rpiduser, int buffersize) { struct to_body* rpid; struct sip_uri uri; int result = -1; LOG(L_DBG, "osp: ospGetRpidUserpart\n"); rpiduser[0] = '\0'; if (_osp_use_rpid != 0) { if (msg->rpid != NULL) { if (parse_rpid_header(msg) == 0) { rpid = get_rpid(msg); if (parse_uri(rpid->uri.s, rpid->uri.len, &uri) == 0) { ospCopyStrToBuffer(&uri.user, rpiduser, buffersize); ospSkipPlus(rpiduser); result = 0; } else { LOG(L_ERR, "osp: ERROR: failed to parse RPID uri\n"); } } else { LOG(L_ERR, "osp: ERROR: failed to parse RPID header\n"); } } else { LOG(L_DBG, "osp: without RPID header\n"); } } else { LOG(L_DBG, "osp: do not use RPID header\n"); } return result; } /* * Get called number from To header * param msg SIP message * param touser User part of To header * param buffersize Size of touser buffer * return 0 success, -1 failure */ int ospGetToUserpart( struct sip_msg* msg, char* touser, int buffersize) { struct to_body* to; struct sip_uri uri; int result = -1; LOG(L_DBG, "osp: ospGetToUserpart\n"); touser[0] = '\0'; if (msg->to != NULL) { if (parse_headers(msg, HDR_TO_F, 0) == 0) { to = get_to(msg); if (parse_uri(to->uri.s, to->uri.len, &uri) == 0) { ospCopyStrToBuffer(&uri.user, touser, buffersize); ospSkipPlus(touser); result = 0; } else { LOG(L_ERR, "osp: ERROR: failed to parse To uri\n"); } } else { LOG(L_ERR, "osp: ERROR: failed to parse To header\n"); } } else { LOG(L_ERR, "osp: ERROR: failed to find To header\n"); } return result; } /* * Get called number from Request-Line header * param msg SIP message * param touser User part of To header * param buffersize Size of touser buffer * return 0 success, -1 failure */ int ospGetUriUserpart( struct sip_msg* msg, char* uriuser, int buffersize) { int result = -1; LOG(L_DBG, "osp: ospGetUriUserpart\n"); uriuser[0] = '\0'; if (parse_sip_msg_uri(msg) >= 0) { ospCopyStrToBuffer(&msg->parsed_uri.user, uriuser, buffersize); ospSkipPlus(uriuser); result = 0; } else { LOG(L_ERR, "osp: ERROR: failed to parse Request-Line URI\n"); } return result; } /* * Append header to SIP message * param msg SIP message * param header Header to be appended * return 0 success, -1 failure */ static int ospAppendHeader( struct sip_msg* msg, str* header) { char* s; struct lump* anchor; LOG(L_DBG, "osp: ospAppendHeader\n"); if((msg == 0) || (header == 0) || (header->s == 0) || (header->len <= 0)) { LOG(L_ERR, "osp: ERROR: bad parameters for appending header\n"); return -1; } if (parse_headers(msg, HDR_EOH_F, 0) == -1) { LOG(L_ERR, "osp: ERROR: failed to parse message\n"); return -1; } anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0); if (anchor == 0) { LOG(L_ERR, "osp: ERROR: failed to get anchor\n"); return -1; } s = (char*)pkg_malloc(header->len); if (s == 0) { LOG(L_ERR, "osp: ERROR: no memory\n"); return -1; } memcpy(s, header->s, header->len); if (insert_new_lump_before(anchor, s, header->len, 0) == 0) { LOG(L_ERR, "osp: ERROR: failed to insert lump\n"); pkg_free(s); return -1; } return 0; } /* * Add OSP token header to SIP message * param msg SIP message * param token OSP authorization token * param tokensize Size of OSP authorization token * return 0 success, -1 failure */ int ospAddOspHeader( struct sip_msg* msg, unsigned char* token, unsigned int tokensize) { str headerval; char buffer[OSP_HEADERBUF_SIZE]; unsigned char encodedtoken[OSP_TOKENBUF_SIZE]; unsigned int encodedtokensize = sizeof(encodedtoken); int result = -1; LOG(L_DBG, "osp: ospAddOspHeader\n"); if (tokensize == 0) { LOG(L_DBG, "osp: destination is not OSP device\n"); result = 0; } else { if (OSPPBase64Encode(token, tokensize, encodedtoken, &encodedtokensize) == 0) { snprintf(buffer, sizeof(buffer), "%s%.*s\r\n", OSP_TOKEN_HEADER, encodedtokensize, encodedtoken); headerval.s = buffer; headerval.len = strlen(buffer); LOG(L_DBG, "osp: setting osp token header field '%s'\n", buffer); if (ospAppendHeader(msg, &headerval) == 0) { result = 0; } else { LOG(L_ERR, "osp: ERROR: failed to append osp header\n"); } } else { LOG(L_ERR, "osp: ERROR: failed to base64 encode token\n"); } } return result; } /* * Get OSP token from SIP message * param msg SIP message * param token OSP authorization token * param tokensize Size of OSP authorization token * return 0 success, -1 failure */ int ospGetOspHeader( struct sip_msg* msg, unsigned char* token, unsigned int* tokensize) { struct hdr_field* hf; int errorcode; int result = -1; LOG(L_DBG, "osp: ospGetOspHeader\n"); if (parse_headers(msg, HDR_EOH_F, 0) == -1) { LOG(L_ERR, "osp: ERROR: failed to parse headers\n"); return result; } for (hf = msg->headers; hf; hf = hf->next) { if ((hf->type == HDR_OTHER_T) && (hf->name.len == OSP_HEADER_SIZE - 2)) { // possible hit if (strncasecmp(hf->name.s, OSP_TOKEN_HEADER, OSP_HEADER_SIZE) == 0) { if ((errorcode = OSPPBase64Decode(hf->body.s, hf->body.len, token, tokensize)) == 0) { result = 0; } else { LOG(L_ERR, "osp: ERROR: failed to base64 decode token (%d)\n", errorcode); LOG(L_ERR, "osp: ERROR: header '%.*s' length %d\n", hf->body.len, hf->body.s, hf->body.len); } break; } } } return result; } /* * Get first VIA header and use the IP or host name * param msg SIP message * param sourceaddress Source address * param buffersize Size of sourceaddress * return 0 success, -1 failure */ int ospGetSourceAddress( struct sip_msg* msg, char* sourceaddress, int buffersize) { struct via_body* via; int result = -1; LOG(L_DBG, "osp: ospGetSourceAddress\n"); if (msg->h_via1 || (parse_headers(msg, HDR_VIA_F, 0) == 0 && msg->h_via1)) { via = msg->h_via1->parsed; ospCopyStrToBuffer(&via->host, sourceaddress, buffersize); LOG(L_DBG, "osp: source address '%s'\n", sourceaddress); result = 0; } return result; } /* * Get Call-ID header from SIP message * param msg SIP message * param callid Call ID * return 0 success, -1 failure */ int ospGetCallId( struct sip_msg* msg, OSPTCALLID** callid) { struct hdr_field* hf; int result = -1; LOG(L_DBG, "osp: ospGetCallId\n"); if (!msg->callid && parse_headers(msg, HDR_CALLID_F, 0) == -1) { LOG(L_ERR, "osp: failed to parse Call-ID\n"); return result; } hf = (struct hdr_field*)msg->callid; if (hf != NULL) { *callid = OSPPCallIdNew(hf->body.len, (unsigned char*)hf->body.s); if (*callid) { result = 0; } else { LOG(L_ERR, "osp: ERROR: failed to allocate OSPCALLID object for '%.*s'\n", hf->body.len, hf->body.s); } } else { LOG(L_ERR, "osp: ERROR: failed to find Call-ID header\n"); } return result; } /* * Get route parameters from the 1st Route or Request-Line * param msg SIP message * param routeparameters Route parameters * param buffersize Size of routeparameters * return 0 success, -1 failure */ int ospGetRouteParameters( struct sip_msg* msg, char* routeparameters, int buffersize) { struct hdr_field* hf; rr_t* rt; struct sip_uri uri; int result = -1; LOG(L_DBG, "osp: ospGetRouterParameters\n"); LOG(L_DBG, "osp: parsed uri host '%.*s' port '%d' vars '%.*s'\n", msg->parsed_uri.host.len, msg->parsed_uri.host.s, msg->parsed_uri.port_no, msg->parsed_uri.params.len, msg->parsed_uri.params.s); if (!(hf = msg->route)) { LOG(L_DBG, "osp: there is no Route headers\n"); } else if (!(rt = (rr_t*)hf->parsed)) { LOG(L_ERR, "osp: ERROR: route headers are not parsed\n"); } else if (parse_uri(rt->nameaddr.uri.s, rt->nameaddr.uri.len, &uri) != 0) { LOG(L_ERR, "osp: ERROR: failed to parse the Route uri '%.*s'\n", rt->nameaddr.uri.len, rt->nameaddr.uri.s); } else if (check_self(&uri.host, uri.port_no ? uri.port_no : SIP_PORT, PROTO_NONE) != 1) { LOG(L_DBG, "osp: the Route uri is NOT mine\n"); LOG(L_DBG, "osp: host '%.*s' port '%d'\n", uri.host.len, uri.host.s, uri.port_no); LOG(L_DBG, "osp: params '%.*s'\n", uri.params.len, uri.params.s); } else { LOG(L_DBG, "osp: the Route uri IS mine - '%.*s'\n", uri.params.len, uri.params.s); LOG(L_DBG, "osp: host '%.*s' port '%d'\n", uri.host.len, uri.host.s, uri.port_no); ospCopyStrToBuffer(&uri.params, routeparameters, buffersize); result = 0; } if ((result == -1) && (msg->parsed_uri.params.len > 0)) { LOG(L_DBG, "osp: using route parameters from Request-Line uri\n"); ospCopyStrToBuffer(&msg->parsed_uri.params, routeparameters, buffersize); routeparameters[msg->parsed_uri.params.len] = '\0'; result = 0; } return result; } /* * Rebuild URI using called number, destination IP, and port * param newuri URI to be built * param called Called number * param dest Destination IP * param port Destination port * param format URI format * return 0 success, -1 failure */ int ospRebuildDestionationUri( str* newuri, char* called, char* dest, char* port, int format) { static const str TRANS = {";transport=tcp", 14}; char* buffer; int calledsize; int destsize; int portsize; LOG(L_DBG, "osp: ospRebuildDestinationUri\n"); calledsize = strlen(called); destsize = strlen(dest); portsize = strlen(port); LOG(L_DBG, "osp: '%s'(%i) '%s'(%i) '%s'(%i) '%d'\n", called, calledsize, dest, destsize, port, portsize, format); /* "sip:" + called + "@" + dest + : + port + " SIP/2.0" for URI format 0 */ /* " + " SIP/2.0" for URI format 1 */ newuri->s = (char*)pkg_malloc(1 + 4 + calledsize + 1 + destsize + 1 + portsize + 1 + 1 + 16 + TRANS.len); if (newuri == NULL) { LOG(L_ERR, "osp: ERROR: no memory\n"); return -1; } buffer = newuri->s; if (format == 1) { *buffer++ = '<'; } *buffer++ = 's'; *buffer++ = 'i'; *buffer++ = 'p'; *buffer++ = ':'; memcpy(buffer, called, calledsize); buffer += calledsize; *buffer++ = '@'; if (*dest == '[') { /* leave out annoying [] */ memcpy(buffer, dest + 1, destsize - 2); buffer += destsize - 2; } else { memcpy(buffer, dest, destsize); buffer += destsize; } if (portsize > 0) { *buffer++ = ':'; memcpy(buffer, port, portsize); buffer += portsize; } if (format == 1) { *buffer++ = '>'; } /* *buffer++ = ' '; *buffer++ = 'S'; *buffer++ = 'I'; *buffer++ = 'P'; *buffer++ = '/'; *buffer++ = '2'; *buffer++ = '.'; *buffer++ = '0'; memcpy(buffer, TRANS.s, TRANS.len); buffer += TRANS.len; *buffer = '\0'; */ newuri->len = buffer - newuri->s; LOG(L_DBG, "osp: new uri '%.*s'\n", newuri->len, newuri->s); return 0; } /* * Get next hop using the first Route not generated by this proxy or URI from the Request-Line * param msg SIP message * param nexthop Next hop IP * param buffersize Size of nexthop */ void ospGetNextHop( struct sip_msg* msg, char* nexthop, int buffersize) { struct hdr_field* hf; struct sip_uri uri; rr_t* rt; int found = 0; LOG(L_DBG, "osp: ospGetNextHop\n"); for (hf = msg->headers; hf; hf = hf->next) { if (hf->type == HDR_ROUTE_T) { for (rt = (rr_t*)hf->parsed; rt; rt = rt->next) { if (parse_uri(rt->nameaddr.uri.s, rt->nameaddr.uri.len, &uri) == 0) { LOG(L_DBG, "osp: host '%.*s' port '%d'\n", uri.host.len, uri.host.s, uri.port_no); if (check_self(&uri.host, uri.port_no ? uri.port_no : SIP_PORT, PROTO_NONE) != 1) { LOG(L_DBG, "osp: it is NOT me, FOUND!\n"); ospCopyStrToBuffer(&uri.host, nexthop, buffersize); found = 1; break; } else { LOG(L_DBG, "osp: it IS me, keep looking\n"); } } else { LOG(L_ERR, "osp: ERROR: failed to parsed route uri '%.*s'\n", rt->nameaddr.uri.len, rt->nameaddr.uri.s); } } if (found == 1) { break; } } } if (!found) { LOG(L_DBG, "osp: using the Request-Line instead host '%.*s' port '%d'\n", msg->parsed_uri.host.len, msg->parsed_uri.host.s, msg->parsed_uri.port_no); ospCopyStrToBuffer(&msg->parsed_uri.host, nexthop, buffersize); found = 1; } } /* * Get direction using the first Route not generated by this proxy or URI from the Request-Line * SER does not have is_direction as Kamailio. We have to write it. * The problem is we cannot check append_fromtag option before running. So, ftag may not exist. * param msg SIP message * return 0 originating, 1 terminating, -1 failed */ int ospGetDirection(struct sip_msg* msg) { static const str FTAG = {"ftag", 4}; char parameters[OSP_HEADERBUF_SIZE]; char* tmp; char* token; str ftag; struct to_body* from; int result = -1; LOG(L_DBG, "osp: ospGetDirection\n"); if (ospGetRouteParameters(msg, parameters, sizeof(parameters)) == 0) { for (token = strtok_r(parameters, ";", &tmp); token; token = strtok_r(NULL, ";", &tmp)) { ftag.s = token; ftag.len = strlen(token); /* Remove leading white space char */ trim_leading(&ftag); if (strncmp(ftag.s, FTAG.s, FTAG.len) == 0) { /* Remove "ftag" string */ ftag.s += FTAG.len; ftag.len -= FTAG.len; /* Remove leading white space char */ trim_leading(&ftag); /* Remove '=' char */ ftag.s++; ftag.len--; /* Remove leading and tailing white space char */ trim(&ftag); LOG(L_DBG, "osp: ftag '%s'\n", ftag.s); if ((parse_from_header(msg) == 0) && ((from = get_from(msg)) != 0)) { if ((ftag.len == from->tag_value.len) && !strncmp(ftag.s, from->tag_value.s, ftag.len)) { LOG(L_DBG, "osp: originating\n"); result = 0; } else { LOG(L_DBG, "osp: terminating\n"); result = 1; } } break; } else { LOG(L_DBG, "osp: ignoring parameter '%s'\n", token); } } } return result; } kamailio-4.0.4/obsolete/osp/provider.h0000644000000000000000000000305412223032460016417 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _OSP_MOD_PROVIDER_H_ #define _OSP_MOD_PROVIDER_H_ int ospSetupProvider(void); int ospDeleteProvider(void); #endif /* _OSP_MOD_PROVIDER_H_ */ kamailio-4.0.4/obsolete/osp/tm.c0000644000000000000000000001136412223032460015203 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../../modules/tm/tm_load.h" #include "tm.h" #include "destination.h" struct tm_binds osp_tmb; static void ospOnReq(struct cell* t, int type, struct tmcb_params* ps); static void ospTmcbFunc(struct cell* t, int type, struct tmcb_params* ps); /* * Load TM API * return 0 success, -1 failure */ int ospInitTm(void) { load_tm_f load_tm; LOG(L_DBG, "osp: ospInitTm\n"); if ((load_tm = (load_tm_f)find_export("load_tm", NO_SCRIPT, 0)) == 0) { LOG(L_ERR, "osp: ERROR: failed to import load_tm\n"); return -1; } if (load_tm(&osp_tmb) == -1) { LOG(L_ERR, "osp: ERROR: failed to load TM API\n"); LOG(L_ERR, "osp: ERROR: TM is required for reporting call setup usage\n"); return -1; } /* Register callbacks, listen for all incoming requests */ if (osp_tmb.register_tmcb(0, 0, TMCB_REQUEST_IN, ospOnReq, 0, 0) <= 0) { LOG(L_ERR, "osp: ERROR: failed to register TMCB_REQUEST_IN callback\n"); LOG(L_ERR, "osp: ERROR: TM callbacks are required for reporting call set up usage\n"); return -1; } return 0; } /* * Register OSP callback function * param t * param type * param ps */ static void ospOnReq( struct cell* t, int type, struct tmcb_params* ps) { int tmcb_types; LOG(L_DBG, "osp: ospOnReq\n"); /* install addaitional handlers */ tmcb_types = // TMCB_REQUEST_FWDED | // TMCB_RESPONSE_FWDED | TMCB_ON_FAILURE | // TMCB_LOCAL_COMPLETED | /* report on completed transactions */ TMCB_RESPONSE_OUT | /* account e2e acks if configured to do so */ TMCB_E2EACK_IN | /* report on missed calls */ TMCB_ON_FAILURE_RO | /* get incoming replies ready for processing */ // TMCB_RESPONSE_IN | 0; if (osp_tmb.register_tmcb(0, t, tmcb_types, ospTmcbFunc, 0, 0) <= 0) { LOG(L_ERR, "osp: ERROR: failed to register TM callbacks\n"); LOG(L_ERR, "osp: ERROR: TM callbacks are required for reporting call setup usage\n"); return; } /* Also, if that is INVITE, disallow silent t-drop */ if (ps->req->REQ_METHOD == METHOD_INVITE) { LOG(L_DBG, "osp: noisy_timer set for accounting\n"); t->flags |= T_NOISY_CTIMER_FLAG; } } /* * OSP callback function * param t * param type * param ps */ static void ospTmcbFunc( struct cell* t, int type, struct tmcb_params* ps) { LOG(L_DBG, "osp: ospTmcbFunc\n"); if (type & TMCB_RESPONSE_OUT) { LOG(L_DBG, "osp: RESPONSE_OUT\n"); } else if (type & TMCB_E2EACK_IN) { LOG(L_DBG, "osp: E2EACK_IN\n"); } else if (type & TMCB_ON_FAILURE_RO) { LOG(L_DBG, "osp: FAILURE_RO\n"); } else if (type & TMCB_RESPONSE_IN) { LOG(L_DBG, "osp: RESPONSE_IN\n"); } else if (type & TMCB_REQUEST_FWDED) { LOG(L_DBG, "osp: REQUEST_FWDED\n"); } else if (type & TMCB_RESPONSE_FWDED) { LOG(L_DBG, "osp: RESPONSE_FWDED\n"); } else if (type & TMCB_ON_FAILURE) { LOG(L_DBG, "osp: FAILURE\n"); } else if (type & TMCB_LOCAL_COMPLETED) { LOG(L_DBG, "osp: COMPLETED\n"); } else { LOG(L_DBG, "osp: something else '%d'\n", type); } if (t) { ospRecordEvent(t->uac[t->nr_of_outgoings - 1].last_received, t->uas.status); } else { LOG(L_DBG, "osp: cell is empty\n"); } } kamailio-4.0.4/obsolete/osp/orig_transaction.c0000644000000000000000000004166712223032460020141 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "../../dset.h" #include "../../usr_avp.h" #include "../../mem/mem.h" #include "orig_transaction.h" #include "destination.h" #include "osptoolkit.h" #include "sipheader.h" #include "usage.h" extern char* _osp_device_ip; extern char* _osp_device_port; extern int _osp_max_dests; extern OSPTPROVHANDLE _osp_provider; extern int _osp_redir_uri; const int OSP_FIRST_ROUTE = 1; const int OSP_NEXT_ROUTE = 0; const int OSP_MAIN_ROUTE = 1; const int OSP_BRANCH_ROUTE = 0; const str OSP_CALLING_NAME = {"_osp_calling_translated_", 24}; static int ospLoadRoutes(OSPTTRANHANDLE transaction, int destcount, char* source, char* sourcedev, char* origcalled, time_t authtime); static int ospPrepareDestination(struct sip_msg* msg, int isfirst, int type, int format); /* * Get routes from AuthRsp * param transaction Transaction handle * param destcount Expected destination count * param source Source IP * param sourcedev Source device IP * param origcalled Original called number * param authtime Request authorization time * return 0 success, -1 failure */ static int ospLoadRoutes( OSPTTRANHANDLE transaction, int destcount, char* source, char* sourcedev, char* origcalled, time_t authtime) { int count; int errorcode; osp_dest* dest; osp_dest dests[OSP_DEF_DESTS]; OSPE_DEST_PROT protocol; OSPE_DEST_OSP_ENABLED enabled; int result = 0; LOG(L_DBG, "osp: ospLoadRoutes\n"); for (count = 0; count < destcount; count++) { /* This is necessary becuase we will save destinations in reverse order */ dest = ospInitDestination(&dests[count]); if (dest == NULL) { result = -1; break; } dest->destinationCount = count + 1; strncpy(dest->origcalled, origcalled, sizeof(dest->origcalled) - 1); if (count == 0) { errorcode = OSPPTransactionGetFirstDestination( transaction, sizeof(dest->validafter), dest->validafter, dest->validuntil, &dest->timelimit, &dest->callidsize, (void*)dest->callid, sizeof(dest->called), dest->called, sizeof(dest->calling), dest->calling, sizeof(dest->host), dest->host, sizeof(dest->destdev), dest->destdev, &dest->tokensize, dest->token); } else { errorcode = OSPPTransactionGetNextDestination( transaction, 0, sizeof(dest->validafter), dest->validafter, dest->validuntil, &dest->timelimit, &dest->callidsize, (void*)dest->callid, sizeof(dest->called), dest->called, sizeof(dest->calling), dest->calling, sizeof(dest->host), dest->host, sizeof(dest->destdev), dest->destdev, &dest->tokensize, dest->token); } if (errorcode != OSPC_ERR_NO_ERROR) { LOG(L_ERR, "osp: ERROR: failed to load routes (%d) expected '%d' current '%d'\n", errorcode, destcount, count); result = -1; break; } errorcode = OSPPTransactionGetDestProtocol(transaction, &protocol); if (errorcode != OSPC_ERR_NO_ERROR) { /* This does not mean an ERROR. The OSP server may not support OSP 2.1.1 */ LOG(L_DBG, "osp: cannot get dest protocol (%d)\n", errorcode); protocol = OSPE_DEST_PROT_SIP; } switch (protocol) { case OSPE_DEST_PROT_H323_LRQ: case OSPE_DEST_PROT_H323_SETUP: case OSPE_DEST_PROT_IAX: dest->supported = 0; break; case OSPE_DEST_PROT_SIP: case OSPE_DEST_PROT_UNDEFINED: case OSPE_DEST_PROT_UNKNOWN: default: dest->supported = 1; break; } errorcode = OSPPTransactionIsDestOSPEnabled(transaction, &enabled); if (errorcode != OSPC_ERR_NO_ERROR) { /* This does not mean an ERROR. The OSP server may not support OSP 2.1.1 */ LOG(L_DBG, "osp: cannot get dest OSP version (%d)\n", errorcode); } else if (enabled == OSPE_OSP_FALSE) { /* Destination device does not support OSP. Do not send token to it */ dest->token[0] = '\0'; dest->tokensize = 0; } errorcode = OSPPTransactionGetDestNetworkId(transaction, dest->networkid); if (errorcode != OSPC_ERR_NO_ERROR) { /* This does not mean an ERROR. The OSP server may not support OSP 2.1.1 */ LOG(L_DBG, "osp: cannot get dest network ID (%d)\n", errorcode); dest->networkid[0] = '\0'; } strncpy(dest->source, source, sizeof(dest->source) - 1); strncpy(dest->srcdev, sourcedev, sizeof(dest->srcdev) - 1); dest->type = OSPC_SOURCE; dest->transid = ospGetTransactionId(transaction); dest->authtime = authtime; LOG(L_INFO, "osp: get destination '%d': " "valid after '%s' " "valid until '%s' " "time limit '%i' seconds " "call id '%.*s' " "calling number '%s' " "called number '%s' " "host '%s' " "supported '%d' " "network id '%s' " "token size '%i'\n", count, dest->validafter, dest->validuntil, dest->timelimit, dest->callidsize, dest->callid, dest->calling, dest->called, dest->host, dest->supported, dest->networkid, dest->tokensize); } /* * Save destination in reverse order, * when we start searching avps the destinations * will be in order */ if (result == 0) { for(count = destcount -1; count >= 0; count--) { ospSaveOrigDestination(&dests[count]); } } return result; } /* * Request OSP authorization and routeing * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospRequestRouting( struct sip_msg* msg, char* ignore1, char* ignore2) { int errorcode; time_t authtime; char source[OSP_E164BUF_SIZE]; char sourcedev[OSP_STRBUF_SIZE]; char src[OSP_STRBUF_SIZE]; char destination[OSP_E164BUF_SIZE]; unsigned int callidnumber = 1; OSPTCALLID* callids[callidnumber]; unsigned int logsize = 0; char* detaillog = NULL; const char** preferred = NULL; unsigned int destcount; OSPTTRANHANDLE transaction = -1; int result = MODULE_RETURNCODE_FALSE; LOG(L_DBG, "osp: ospRequestRouting\n"); authtime = time(NULL); destcount = _osp_max_dests; if ((errorcode = OSPPTransactionNew(_osp_provider, &transaction)) != OSPC_ERR_NO_ERROR) { LOG(L_ERR, "osp: ERROR: failed to create new OSP transaction (%d)\n", errorcode); } else if ((ospGetRpidUserpart(msg, source, sizeof(source)) != 0) && (ospGetFromUserpart(msg, source, sizeof(source)) != 0)) { LOG(L_ERR, "osp: ERROR: failed to extract calling number\n"); } else if ((ospGetUriUserpart(msg, destination, sizeof(destination)) != 0) && (ospGetToUserpart(msg, destination, sizeof(destination)) != 0)) { LOG(L_ERR, "osp: ERROR: failed to extract called number\n"); } else if (ospGetCallId(msg, &(callids[0])) != 0) { LOG(L_ERR, "osp: ERROR: failed to extract call id\n"); } else if (ospGetSourceAddress(msg, sourcedev, sizeof(sourcedev)) != 0) { LOG(L_ERR, "osp: ERROR: failed to extract source address\n"); } else { ospConvertAddress(sourcedev, src, sizeof(src)); LOG(L_INFO, "osp: request auth and routing for: " "source '%s' " "source_port '%s' " "source_dev '%s' " "e164_source '%s' " "e164_dest '%s' " "call_id '%.*s' " "dest_count '%i'\n", _osp_device_ip, _osp_device_port, src, /* sourcedev in "[x.x.x.x]" or host.domain format */ source, destination, callids[0]->ospmCallIdLen, callids[0]->ospmCallIdVal, destcount ); /* try to request authorization */ errorcode = OSPPTransactionRequestAuthorisation( transaction, /* transaction handle */ _osp_device_ip, /* from the configuration file */ src, /* source of call, protocol specific, in OSP format */ source, /* calling number in nodotted e164 notation */ OSPC_E164, /* calling number format */ destination, /* called number */ OSPC_E164, /* called number format */ "", /* optional username string, used if no number */ callidnumber, /* number of call ids, here always 1 */ callids, /* sized-1 array of call ids */ preferred, /* preferred destinations, here always NULL */ &destcount, /* max destinations, after call dest_count */ &logsize, /* size allocated for detaillog (next param) 0=no log */ detaillog); /* memory location for detaillog to be stored */ if ((errorcode == OSPC_ERR_NO_ERROR) && (ospLoadRoutes(transaction, destcount, _osp_device_ip, sourcedev, destination, authtime) == 0)) { LOG(L_INFO, "osp: there are '%d' OSP routes, call_id '%.*s'\n", destcount, callids[0]->ospmCallIdLen, callids[0]->ospmCallIdVal); result = MODULE_RETURNCODE_TRUE; } else { LOG(L_ERR, "osp: ERROR: failed to request auth and routing (%i), call_id '%.*s\n", errorcode, callids[0]->ospmCallIdLen, callids[0]->ospmCallIdVal); switch (errorcode) { case OSPC_ERR_TRAN_ROUTE_BLOCKED: result = -403; break; case OSPC_ERR_TRAN_ROUTE_NOT_FOUND: result = -404; break; case OSPC_ERR_NO_ERROR: /* AuthRsp ok but ospLoadRoutes fails */ result = -500; break; default: result = MODULE_RETURNCODE_FALSE; break; } } } if (callids[0] != NULL) { OSPPCallIdDelete(&(callids[0])); } if (transaction != -1) { OSPPTransactionDelete(transaction); } return result; } /* * Check if there is a route * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospCheckRoute( struct sip_msg* msg, char* ignore1, char* ignore2) { LOG(L_DBG, "osp: ospCheckRoute\n"); if (ospCheckOrigDestination() == 0) { return MODULE_RETURNCODE_TRUE; } else { return MODULE_RETURNCODE_FALSE; } } /* * Append route specific OSP headers * This function only works in branch route block. * This function is only for SER. SER does not support rewrite_uri in BRANCH_ROUTE. * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success MODULE_RETURNCODE_FALSE failed */ int ospAppendHeaders( struct sip_msg* msg, char* ignore1, char* ignore2) { osp_dest* dest; int result = MODULE_RETURNCODE_FALSE; LOG(L_DBG, "osp: ospAppendHeaders\n"); dest = ospGetLastOrigDestination(); if (dest != NULL) { ospAddOspHeader(msg, dest->token, dest->tokensize); result = MODULE_RETURNCODE_TRUE; } else { LOG(L_ERR, "osp: ERROR: failed to get last used destination\n"); } return result; } /* * Build SIP message for destination * param msg SIP message * param isfirst Is first destination * param type Main or branch route block * param format URI format * return MODULE_RETURNCODE_TRUE success MODULE_RETURNCODE_FALSE failure */ static int ospPrepareDestination( struct sip_msg* msg, int isfirst, int type, int format) { str newuri = {NULL, 0}; int result = MODULE_RETURNCODE_FALSE; LOG(L_DBG, "osp: ospPrepareDestination\n"); osp_dest *dest = ospGetNextOrigDestination(); if (dest != NULL) { ospRebuildDestionationUri(&newuri, dest->called, dest->host, "", format); LOG(L_INFO, "osp: prepare route to URI '%.*s' for call_id '%.*s' transaction_id '%llu'\n", newuri.len, newuri.s, dest->callidsize, dest->callid, dest->transid); if (type == OSP_MAIN_ROUTE) { if (isfirst == OSP_FIRST_ROUTE) { rewrite_uri(msg, &newuri); } else { append_branch(msg, &newuri, NULL, NULL, Q_UNSPECIFIED, 0, NULL); } result = MODULE_RETURNCODE_TRUE; } else { LOG(L_ERR, "osp: ERROR: unsupported route block type\n"); } } else { LOG(L_DBG, "osp: there is no more routes\n"); ospReportOrigSetupUsage(); } if (newuri.len > 0) { pkg_free(newuri.s); } return result; } /* * Prepare OSP first route * This function prepare the first OSP route * This function is only for SER. SER does not support rewrite_uri in BRANCH_ROUTE. * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospPrepareFirstRoute( struct sip_msg* msg, char* ignore1, char* ignore2) { int result = MODULE_RETURNCODE_TRUE; LOG(L_DBG, "osp: ospPrepareFirstRoute\n"); result = ospPrepareDestination(msg, OSP_FIRST_ROUTE, OSP_MAIN_ROUTE, 0); return result; } /* * Prepare OSP next route * This function prepare the next OSP route * This function is only for SER. SER does not support rewrite_uri in BRANCH_ROUTE. * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospPrepareNextRoute( struct sip_msg* msg, char* ignore1, char* ignore2) { int result = MODULE_RETURNCODE_TRUE; LOG(L_DBG, "osp: ospPrepareNextRoute\n"); result = ospPrepareDestination(msg, OSP_NEXT_ROUTE, OSP_MAIN_ROUTE, 0); return result; } /* * Prepare all OSP routes * This function does not work in branch route block. * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospPrepareAllRoutes( struct sip_msg* msg, char* ignore1, char* ignore2) { int result = MODULE_RETURNCODE_TRUE; LOG(L_DBG, "osp: ospPrepareAllRoutes\n"); for(result = ospPrepareDestination(msg, OSP_FIRST_ROUTE, OSP_MAIN_ROUTE, _osp_redir_uri); result == MODULE_RETURNCODE_TRUE; result = ospPrepareDestination(msg, OSP_NEXT_ROUTE, OSP_MAIN_ROUTE, _osp_redir_uri)) { } return MODULE_RETURNCODE_TRUE; } kamailio-4.0.4/obsolete/osp/destination.h0000644000000000000000000000543512223032460017113 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _OSP_MOD_DESTINATION_H_ #define _OSP_MOD_DESTINATION_H_ #include #include "osp_mod.h" typedef struct _osp_dest { char validafter[OSP_STRBUF_SIZE]; char validuntil[OSP_STRBUF_SIZE]; char callid[OSP_STRBUF_SIZE]; char called[OSP_STRBUF_SIZE]; char calling[OSP_STRBUF_SIZE]; char source[OSP_STRBUF_SIZE]; char srcdev[OSP_STRBUF_SIZE]; char host[OSP_STRBUF_SIZE]; char destdev[OSP_STRBUF_SIZE]; char networkid[OSP_STRBUF_SIZE]; unsigned char token[OSP_TOKENBUF_SIZE]; unsigned int callidsize; unsigned int tokensize; unsigned int timelimit; int lastcode; time_t authtime; time_t time100; time_t time180; time_t time200; int type; unsigned long long transid; int supported; int used; int reported; unsigned int destinationCount; char origcalled[OSP_STRBUF_SIZE]; } osp_dest; osp_dest* ospInitDestination(osp_dest* dest); int ospSaveOrigDestination(osp_dest* dest); int ospSaveTermDestination(osp_dest* dest); int ospCheckOrigDestination(void); osp_dest* ospGetNextOrigDestination(void); osp_dest* ospGetLastOrigDestination(void); osp_dest* ospGetTermDestination(void); void ospRecordEvent(int clientcode, int servercode); void ospDumpDestination(osp_dest* dest); void ospDumpAllDestination(void); void ospConvertAddress(char* src, char* dst, int buffersize); #endif /* _OSP_MOD_DESTINATION_H_ */ kamailio-4.0.4/obsolete/osp/osptoolkit.h0000644000000000000000000000510412223032460016772 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _OSP_MOD_OSPTOOLKIT_H_ #define _OSP_MOD_OSPTOOLKIT_H_ #include /* * This module implements help functions for the OSP toolkit. * Some of the functions maybe impemented by the toolkit in the future. */ /* * Returns the OSP transaction id generated by the server. The id is * returned in the Authorization Response and also packaged in the token. */ unsigned long long ospGetTransactionId(OSPTTRANHANDLE transaction); void ospReportUsageWrapper( OSPTTRANHANDLE ospvTransaction, /* In - Transaction handle */ unsigned ospvReleaseCode, /* In - Release code */ unsigned ospvDuration, /* In - Length of call */ time_t ospvStartTime, /* In - Call start time */ time_t ospvEndTime, /* In - Call end time */ time_t ospvAlertTime, /* In - Call alert time */ time_t ospvConnectTime, /* In - Call connect time */ unsigned ospvIsPDDInfoPresent, /* In - Is PDD Info present */ unsigned ospvPostDialDelay, /* In - Post Dial Delay */ unsigned ospvReleaseSource /* In - EP that released the call */ ); #endif /* _OSP_MOD_OSPTOOLKIT_H_ */ kamailio-4.0.4/obsolete/osp/provider.c0000644000000000000000000001065212223032460016414 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "../../dprint.h" #include "provider.h" extern unsigned int _osp_sp_number; extern char* _osp_sp_uris[]; extern unsigned long _osp_sp_weights[]; extern unsigned char* _osp_private_key; extern unsigned char* _osp_local_certificate; extern unsigned char* _osp_ca_certificate; extern int _osp_ssl_lifetime; extern int _osp_persistence; extern int _osp_retry_delay; extern int _osp_retry_limit; extern int _osp_timeout; extern int _osp_crypto_hw; extern OSPTPROVHANDLE _osp_provider; /* * Create a new OSP provider object per process * return 0 success, others failure */ int ospSetupProvider(void) { OSPTPRIVATEKEY privatekey; OSPTCERT localcert; OSPTCERT cacert; OSPTCERT* cacerts[1]; int result; LOG(L_DBG, "osp: ospSetupProvider\n"); cacerts[0] = &cacert; if ((result = OSPPInit(_osp_crypto_hw)) != 0) { LOG(L_ERR, "osp: ERROR: failed to initalize OSP (%i)\n", result); } else if (OSPPUtilLoadPEMPrivateKey(_osp_private_key, &privatekey) != 0) { LOG(L_ERR, "osp: ERROR: failed to load private key from '%s'\n", _osp_private_key); } else if (OSPPUtilLoadPEMCert(_osp_local_certificate, &localcert) != 0) { LOG(L_ERR, "osp: ERROR: failed to load local certificate from '%s'\n",_osp_local_certificate); } else if (OSPPUtilLoadPEMCert(_osp_ca_certificate, &cacert) != 0) { LOG(L_ERR, "osp: ERROR: failed to load CA certificate from '%s'\n", _osp_ca_certificate); } else { result = OSPPProviderNew( _osp_sp_number, (const char**)_osp_sp_uris, _osp_sp_weights, "http://localhost:1234", &privatekey, &localcert, 1, (const OSPTCERT**)cacerts, 1, _osp_ssl_lifetime, _osp_sp_number, _osp_persistence, _osp_retry_delay, _osp_retry_limit, _osp_timeout, "", "", &_osp_provider); if (result != 0) { LOG(L_ERR, "osp: ERROR: failed to create provider (%i)\n", result); } else { LOG(L_DBG, "osp: created new (per process) provider '%d'\n", _osp_provider); result = 0; } } /* * Free space allocated while loading crypto information from PEM-encoded files. * There are some problems to free the memory, do not free them */ if (privatekey.PrivateKeyData != NULL) { //free(privatekey.PrivateKeyData); } if (localcert.CertData != NULL) { //free(localcert.CertData); } if (cacert.CertData != NULL) { //free(localcert.CertData); } return result; } /* * Erase OSP provider object * return 0 success, others failure */ int ospDeleteProvider(void) { int result; LOG(L_DBG, "osp: ospDeleteProvider\n"); if ((result = OSPPProviderDelete(_osp_provider, 0)) != 0) { LOG(L_ERR, "osp: ERROR: failed to erase provider '%d' (%d)\n", _osp_provider, result); } return result; } kamailio-4.0.4/obsolete/osp/usage.c0000644000000000000000000004522712223032460015674 0ustar rootroot/* * ser osp module. * * This module enables ser to communicate with an Open Settlement * Protocol (OSP) server. The Open Settlement Protocol is an ETSI * defined standard for Inter-Domain VoIP pricing, authorization * and usage exchange. The technical specifications for OSP * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. * * Uli Abend was the original contributor to this module. * * Copyright (C) 2001-2005 Fhg Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "../../sr_module.h" #include "../../usr_avp.h" #include "usage.h" #include "destination.h" #include "osptoolkit.h" #include "sipheader.h" #define OSP_ORIG_COOKIE "osp-o" #define OSP_TERM_COOKIE "osp-t" #define OSP_RELEASE_ORIG 0 #define OSP_RELEASE_TERM 1 /* The up case tags for the destinations may corrupt OSP cookies */ #define OSP_COOKIE_TRANSID 't' #define OSP_COOKIE_TRANSIDUP 'T' #define OSP_COOKIE_SRCIP 's' #define OSP_COOKIE_SRCIPUP 'S' #define OSP_COOKIE_AUTHTIME 'a' #define OSP_COOKIE_AUTHTIMEUP 'A' #define OSP_COOKIE_DSTCOUNT 'c' #define OSP_COOKIE_DSTCOUNTUP 'C' /* SER uses AVP to add RR pararmters */ const str OSP_ORIGCOOKIE_NAME = {"_osp_orig_cookie_", 17}; const str OSP_TERMCOOKIE_NAME = {"_osp_term_cookie_", 17}; extern char* _osp_device_ip; extern OSPTPROVHANDLE _osp_provider; extern str OSP_ORIGDEST_NAME; static void ospRecordTransaction(struct sip_msg* msg, unsigned long long transid, char* uac, char* from, char* to, time_t authtime, int isorig, unsigned destinationCount); static int ospBuildUsageFromDestination(OSPTTRANHANDLE transaction, osp_dest* dest, int lastcode); static int ospReportUsageFromDestination(OSPTTRANHANDLE transaction, osp_dest* dest); /* SER checks ftag by itself, without release parameter */ static int ospReportUsageFromCookie(struct sip_msg* msg, char* cooky, OSPTCALLID* callid, OSPE_MSG_ROLETYPES type); /* * Create OSP cookie and insert it into Record-Route header * param msg SIP message * param tansid Transaction ID * param uac Source IP * param from * param to * param authtime Request authorization time * param isorig Originate / Terminate * param destinationCount Destination count */ static void ospRecordTransaction( struct sip_msg* msg, unsigned long long transid, char* uac, char* from, char* to, time_t authtime, int isorig, unsigned destinationCount) { const str* name; str cookie; char buffer[OSP_STRBUF_SIZE]; LOG(L_DBG, "osp: ospRecordTransaction\n"); cookie.s = buffer; if (isorig == 1) { cookie.len = snprintf( buffer, sizeof(buffer), "%c%llu_%c%s_%c%d_%c%d", OSP_COOKIE_TRANSID, transid, OSP_COOKIE_SRCIP, uac, OSP_COOKIE_AUTHTIME, (unsigned int)authtime, OSP_COOKIE_DSTCOUNT, destinationCount); } else { cookie.len = snprintf( buffer, sizeof(buffer), "%c%llu_%c%s_%c%d", OSP_COOKIE_TRANSID, transid, OSP_COOKIE_SRCIP, uac, OSP_COOKIE_AUTHTIME, (unsigned int)authtime); } if (cookie.len < 0) { LOG(L_ERR, "osp: ERROR: failed to create OSP cookie\n"); return; } /* SER uses AVP to add RR parameters */ LOG(L_DBG, "osp: adding RR parameter '%s' for '%s'\n", buffer, (isorig == 1) ? "orig" : "term"); name = (isorig == 1) ? &OSP_ORIGCOOKIE_NAME : &OSP_TERMCOOKIE_NAME; add_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)*name, (int_str)cookie); } /* * Create OSP originate cookie and insert it into Record-Route header * param msg SIP message * param tansid Transaction ID * param uac Source IP * param from * param to * param authtime Request authorization time * param destinationCount Destination count */ void ospRecordOrigTransaction( struct sip_msg* msg, unsigned long long transid, char* uac, char* from, char* to, time_t authtime, unsigned destinationCount) { int isorig = 1; LOG(L_DBG, "osp: ospRecordOrigTransaction\n"); ospRecordTransaction(msg, transid, uac, from, to, authtime, isorig, destinationCount); } /* * Create OSP terminate cookie and insert it into Record-Route header * param msg SIP message * param tansid Transaction ID * param uac Source IP * param from * param to * param authtime Request authorization time */ void ospRecordTermTransaction( struct sip_msg* msg, unsigned long long transid, char* uac, char* from, char* to, time_t authtime) { int isorig = 0; unsigned destinationCount = 0; /* N/A */ LOG(L_DBG, "osp: ospRecordTermTransaction\n"); ospRecordTransaction(msg, transid, uac, from, to, authtime, isorig, destinationCount); } /* * Report OSP usage from OSP cookie * SER checks ftag by itself, without release parameter * param msg SIP message * param cookie OSP cookie * param callid Call ID * param type Usage type * return */ static int ospReportUsageFromCookie( struct sip_msg* msg, char* cookie, OSPTCALLID* callid, OSPE_MSG_ROLETYPES type) { int release; char* tmp; char* token; char tag; char* value; unsigned long long transid = 0; time_t authtime = 0; unsigned destinationCount = 0; time_t endtime = time(NULL); char firstvia[OSP_STRBUF_SIZE]; char from[OSP_STRBUF_SIZE]; char to[OSP_STRBUF_SIZE]; char nexthop[OSP_STRBUF_SIZE]; char* calling; char* called; char* originator = NULL; char* terminator; char* source; char srcbuf[OSP_STRBUF_SIZE]; char* destination; char dstbuf[OSP_STRBUF_SIZE]; char* srcdev; char devbuf[OSP_STRBUF_SIZE]; OSPTTRANHANDLE transaction = -1; int errorcode; LOG(L_DBG, "osp: ospReportUsageFromCookie\n"); LOG(L_DBG, "osp: '%s' type '%d'\n", cookie, type); if (cookie != NULL) { for (token = strtok_r(cookie, "_", &tmp); token; token = strtok_r(NULL, "_", &tmp)) { tag = *token; value= token + 1; switch (tag) { case OSP_COOKIE_TRANSID: case OSP_COOKIE_TRANSIDUP: transid = atoll(value); break; case OSP_COOKIE_AUTHTIME: case OSP_COOKIE_AUTHTIMEUP: authtime = atoi(value); break; case OSP_COOKIE_SRCIP: case OSP_COOKIE_SRCIPUP: originator = value; break; case OSP_COOKIE_DSTCOUNT: case OSP_COOKIE_DSTCOUNTUP: destinationCount = (unsigned)atoi(value); break; default: LOG(L_ERR, "osp: ERROR: unexpected tag '%c' / value '%s'\n", tag, value); break; } } } ospGetSourceAddress(msg, firstvia, sizeof(firstvia)); ospGetFromUserpart(msg, from, sizeof(from)); ospGetToUserpart(msg, to, sizeof(to)); ospGetNextHop(msg, nexthop, sizeof(nexthop)); LOG(L_DBG, "osp: first via '%s' from '%s' to '%s' next hop '%s'\n", firstvia, from, to, nexthop); /* SER checks ftag by itself */ errorcode = ospGetDirection(msg); switch (errorcode) { case 0: release = OSP_RELEASE_ORIG; break; case 1: release = OSP_RELEASE_TERM; break; default: /* This approach has a problem of flipping called/calling number */ if (strcmp(firstvia, originator) == 0) { release = OSP_RELEASE_ORIG; } else { release = OSP_RELEASE_TERM; } } if (release == OSP_RELEASE_ORIG) { LOG(L_DBG, "osp: orig '%s' released the call, call_id '%.*s' transaction_id '%llu'\n", firstvia, callid->ospmCallIdLen, callid->ospmCallIdVal, transid); if (originator == NULL) { originator = firstvia; } calling = from; called = to; terminator = nexthop; } else { release = OSP_RELEASE_TERM; LOG(L_DBG, "osp: term '%s' released the call, call_id '%.*s' transaction_id '%llu'\n", firstvia, callid->ospmCallIdLen, callid->ospmCallIdVal, transid); if (originator == NULL) { originator = nexthop; } calling = to; called = from; terminator = firstvia; } errorcode = OSPPTransactionNew(_osp_provider, &transaction); LOG(L_DBG, "osp: created transaction handle '%d' (%d)\n", transaction, errorcode); switch (type) { case OSPC_DESTINATION: ospConvertAddress(originator, srcbuf, sizeof(srcbuf)); source = srcbuf; destination = _osp_device_ip; srcdev = ""; break; case OSPC_SOURCE: case OSPC_OTHER: case OSPC_UNDEFINED_ROLE: default: source = _osp_device_ip; ospConvertAddress(terminator, dstbuf, sizeof(dstbuf)); destination = dstbuf; ospConvertAddress(originator, devbuf, sizeof(devbuf)); srcdev = devbuf; break; } errorcode = OSPPTransactionBuildUsageFromScratch( transaction, transid, type, source, destination, srcdev, "", calling, OSPC_E164, called, OSPC_E164, callid->ospmCallIdLen, callid->ospmCallIdVal, (enum OSPEFAILREASON)0, NULL, NULL); LOG(L_DBG, "osp: built usage handle '%d' (%d)\n", transaction, errorcode); if ((errorcode == OSPC_ERR_NO_ERROR) && (destinationCount > 0)) { errorcode = OSPPTransactionSetDestinationCount( transaction, destinationCount); } ospReportUsageWrapper( transaction, cookie == NULL ? 9016 : 10016, cookie == NULL ? 0 : endtime - authtime, authtime, endtime, 0,0, 0,0, release); return errorcode; } /* * Report OSP usage * SER uses AVP to add RR pararmters * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospReportUsage( struct sip_msg* msg, char* ignore1, char* ignore2) { int_str cookieval; struct usr_avp* cookieavp = NULL; static const int FROMFLAGS = AVP_TRACK_FROM | AVP_CLASS_URI | AVP_NAME_STR | AVP_VAL_STR; static const int TOFLAGS = AVP_TRACK_TO | AVP_CLASS_URI | AVP_NAME_STR | AVP_VAL_STR; char buffer[OSP_HEADERBUF_SIZE]; OSPTCALLID* callid = NULL; int result = MODULE_RETURNCODE_FALSE; LOG(L_DBG, "osp: ospReportUsage\n"); ospGetCallId(msg, &callid); if (callid != NULL) { if ((cookieavp = search_first_avp(FROMFLAGS, (int_str)OSP_ORIGCOOKIE_NAME, &cookieval, NULL)) != 0 || (cookieavp = search_first_avp(TOFLAGS, (int_str)OSP_ORIGCOOKIE_NAME, &cookieval, NULL)) != 0) { ospCopyStrToBuffer(&cookieval.s, buffer, sizeof(buffer)); LOG(L_DBG, "orig cookie '%s'\n", buffer); LOG(L_INFO, "osp: report orig duration for call_id '%.*s'\n", callid->ospmCallIdLen, callid->ospmCallIdVal); ospReportUsageFromCookie(msg, buffer, callid, OSPC_SOURCE); result = MODULE_RETURNCODE_TRUE; } if ((cookieavp = search_first_avp(FROMFLAGS, (int_str)OSP_TERMCOOKIE_NAME, &cookieval, NULL)) != 0 || (cookieavp = search_first_avp(TOFLAGS, (int_str)OSP_TERMCOOKIE_NAME, &cookieval, NULL)) != 0) { ospCopyStrToBuffer(&cookieval.s, buffer, sizeof(buffer)); LOG(L_DBG, "term cookie '%s'\n", buffer); LOG(L_INFO, "osp: report term duration for call_id '%.*s'\n", callid->ospmCallIdLen, callid->ospmCallIdVal); ospReportUsageFromCookie(msg, buffer, callid, OSPC_DESTINATION); result = MODULE_RETURNCODE_TRUE; } if (result == MODULE_RETURNCODE_FALSE) { LOG(L_DBG, "without orig or term OSP information\n"); LOG(L_INFO, "report other duration for call_id '%.*s'\n", callid->ospmCallIdLen, callid->ospmCallIdVal); ospReportUsageFromCookie(msg, NULL, callid, OSPC_SOURCE); result = MODULE_RETURNCODE_TRUE; } OSPPCallIdDelete(&callid); } if (result == MODULE_RETURNCODE_FALSE) { LOG(L_DBG, "osp: failed to report usage\n"); } return result; } /* * Build OSP usage from destination * param transaction OSP transaction handle * param dest Destination * param lastcode Destination status * return 0 success, others failure */ static int ospBuildUsageFromDestination( OSPTTRANHANDLE transaction, osp_dest* dest, int lastcode) { int errorcode; char addr[OSP_STRBUF_SIZE]; char* source; char* srcdev; LOG(L_DBG, "osp: ospBuildUsageFromDestination\n"); if (dest->type == OSPC_SOURCE) { ospConvertAddress(dest->srcdev, addr, sizeof(addr)); source = dest->source; srcdev = addr; } else { ospConvertAddress(dest->source, addr, sizeof(addr)); source = addr; srcdev = dest->srcdev; } errorcode = OSPPTransactionBuildUsageFromScratch( transaction, dest->transid, dest->type, source, dest->host, srcdev, dest->destdev, dest->calling, OSPC_E164, dest->origcalled, /* Report original called number */ OSPC_E164, dest->callidsize, dest->callid, (enum OSPEFAILREASON)lastcode, NULL, NULL); return errorcode; } /* * Report OSP usage from destination * param transaction OSP transaction handle * param dest Destination * return 0 success */ static int ospReportUsageFromDestination( OSPTTRANHANDLE transaction, osp_dest* dest) { ospReportUsageWrapper( transaction, /* In - Transaction handle */ dest->lastcode, /* In - Release Code */ 0, /* In - Length of call */ dest->authtime, /* In - Call start time */ 0, /* In - Call end time */ dest->time180, /* In - Call alert time */ dest->time200, /* In - Call connect time */ dest->time180 ? 1 : 0, /* In - Is PDD Info present */ dest->time180 ? dest->time180 - dest->authtime : 0, /* In - Post Dial Delay */ 0); return 0; } /* * Report originate call setup usage */ void ospReportOrigSetupUsage(void) { osp_dest* dest = NULL; osp_dest* lastused = NULL; struct usr_avp* destavp = NULL; int_str destval; struct search_state state; OSPTTRANHANDLE transaction = -1; int lastcode = 0; int errorcode; LOG(L_DBG, "osp: ospReportOrigSetupUsage\n"); errorcode = OSPPTransactionNew(_osp_provider, &transaction); for (destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_ORIGDEST_NAME, &destval, &state); destavp != NULL; destavp = search_next_avp(&state, &destval)) { /* OSP destination is wrapped in a string */ dest = (osp_dest*)destval.s.s; if (dest->used == 1) { if (dest->reported == 1) { LOG(L_DBG, "osp: orig setup already reported\n"); break; } else { dest->reported = 1; } LOG(L_DBG, "osp: iterating through used destination\n"); ospDumpDestination(dest); lastused = dest; errorcode = ospBuildUsageFromDestination(transaction, dest, lastcode); lastcode = dest->lastcode; } else { LOG(L_DBG, "osp: destination has not been used, breaking out\n"); break; } } if (lastused) { LOG(L_INFO, "osp: report orig setup for call_id '%.*s' transaction_id '%llu'\n", lastused->callidsize, lastused->callid, lastused->transid); errorcode = ospReportUsageFromDestination(transaction, lastused); } else { /* If a Toolkit transaction handle was created, but we did not find * any destinations to report, we need to release the handle. Otherwise, * the ospReportUsageFromDestination will release it. */ OSPPTransactionDelete(transaction); } } /* * Report terminate call setup usage */ void ospReportTermSetupUsage(void) { osp_dest* dest = NULL; OSPTTRANHANDLE transaction = -1; int errorcode; LOG(L_DBG, "osp: ospReportTermSetupUsage\n"); if ((dest = ospGetTermDestination())) { if (dest->reported == 0) { dest->reported = 1; LOG(L_INFO, "osp: report term setup for call_id '%.*s' transaction_id '%llu'\n", dest->callidsize, dest->callid, dest->transid); errorcode = OSPPTransactionNew(_osp_provider, &transaction); errorcode = ospBuildUsageFromDestination(transaction, dest, 0); errorcode = ospReportUsageFromDestination(transaction, dest); } else { LOG(L_DBG, "osp: term setup already reported\n"); } } else { LOG(L_ERR, "osp: ERROR: without term setup to report\n"); } } kamailio-4.0.4/obsolete/osp/Makefile0000644000000000000000000000327312223032460016057 0ustar rootroot# # ser osp module. # # This module enables ser to communicate with an Open Settlement # Protocol (OSP) server. The Open Settlement Protocol is an ETSI # defined standard for Inter-Domain VoIP pricing, authorization # and usage exchange. The technical specifications for OSP # (ETSI TS 101 321 V4.1.1) are available at www.etsi.org. # # Uli Abend was the original contributor to this module. # # Copyright (C) 2001-2005 Fhg Fokus # # This file is part of ser, a free SIP server. # # ser is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # For a license to use the ser software under conditions # other than those described here, or to purchase support for this # software, please contact iptel.org by e-mail at the following addresses: # info@iptel.org # # ser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=osp.so DEFS+=-D_POSIX_THREADS -I$(LOCALBASE)/include LIBS=-L$(LOCALBASE)/lib -losptk -lssl -lcrypto -lpthread -lm DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/osp/RELEASE-NOTES.txt0000644000000000000000000000576412223032460017135 0ustar rootroot June 23, 2005 Implemented routing and authorization validation. The module can: o Request authorization and routing information from an OSP server o Use provided routes to: o Redirect the originating UI to all routes OR o Try all routes simultaneously using parallel forking OR o Try routes in the order they were received using sequential forking o Add and retrieve P-OSP-Auth-Token header o Perform local validation of the P-OSP-Auth-Token September 12, 2005 o Bug 4863 - the module will remove OSP Auth token from the incoming Invite after validating the token and before forwarding Invite to the next hop. o Bug 4891 - added a new flag 'validate_call_id' for disabling call id validation in OSP tokens. The value is optional, call id validation can be turned off by setting the value to 0 (No). It may be used for interoperability issues with some OSP clients sending invalid call ids in Authorization Requests. o Bug 4986 - changed return value for 'prepareallosproutes' from 0 - stop routing logic to 1 - true o Bug 5039 - changed return value for 'preparenextosproute' when there is no more routes from 0 - stop routing logic to -1 - false o Bug 5039 - changed return value for 'preparenextosproute' when there is o Bug 4893 - sequential routing (in failure_route[1]) now checks for code 487 (Canceled) before trying the next OSP route. Note - this check does not work when UAC cancels the call while SER is trying to connect to an unresponsive UAS, code 408 (time out) masks code 487. See new function 't_was_cancelled' in the 'tm' module. o Bug 4892 - removed trailing binary chars from some log messages. o Bug 4987 - fixed a compile time error on Solaris o Bug 4946 - added README file September 26, 2005 o Bug 5094 - don't route the call if OSP token validation fails. o Bug 5109 - send "100 Trying" message before requesting OSP routes. o Bug 5111 - fixed typos in error messages. o Bug 5153 - removed trailing binary chars from P-OSP-Auth-Token header value. October 3, 2005 o Report OSP usage indications after the call set-up and tear down transactions complete. October 17, 2005 o Report call set-up usage indication after receiving either 200 or 202. o Report termination cause code for duration usage indications as 10,016 instead of 1,016. o Improved error checking and logging. o Bug 5366 - removed a memory leak in usage reporting logic. October 31, 2005 o Copied files from cvs.berlios.de:/cvsroot/osp-module to cvs.berlios.de:/cvsroot/ser 2006 September 5 o Using BRAND_ROUTE approach to add route specific header to avoid sending an OSP token to a non-OSP destination. o Using called number in Request-Line for token validation and auth request. o Sending DeviceInfo in "[x.x.x.x]" format in OSP AuthReq messages. o Using fromtag in RR to report who releases the call first. o Using AVP to add RR paramters for inserting OSP cookies. kamailio-4.0.4/obsolete/avp_radius/0000755000000000000000000000000012223032460015746 5ustar rootrootkamailio-4.0.4/obsolete/avp_radius/doc/0000755000000000000000000000000012223032460016513 5ustar rootrootkamailio-4.0.4/obsolete/avp_radius/doc/avp_radius.xml0000644000000000000000000000263112223032460021374 0ustar rootroot
Juha Heinanen jh@tutpro.com 2004 Juha Heinanen Avp_radius Module
Overview avp_radius module allows loading of user's attributes into AVPs from Radius. User's name and domain can be based on From URI, Request URI, or authenticated credentials. The module assumes that Radius returns the AVPs as values of reply attribute SIP-AVP. Its value must be a string of form "name:value" or of form "name#value". In the first case, value is interpreted as a string and in the second case as an int (second case has not been implemented yet). The module prefixes each attribute name as returned from Radius by string "caller_" or "callee_" depending if caller's or callee's attributes are loaded.
kamailio-4.0.4/obsolete/avp_radius/doc/functions.xml0000644000000000000000000000332112223032460021244 0ustar rootroot
Functions
<function>avp_load_radius(user)</function> The functions loads user's attributes from radius and stores them into AVPs. Parameter "user" is used to indicate, whose attributes are loaded. Possible values are "caller", "caller_from_ruri", "callee", and "digest". In "caller" case, attributes belong to the user of the From URI, in "callee" case, to the user of the Request URI, and, in "digest" case, to the authenticated user. The "caller_from_uri" case loads attribute value pairs defined for caller (default SER-Caller-AVPs), but uses the user in the Request URI. This is useful for the case where a call has been forwarded by callee (Request URI) and you need to look up whether callee is allowed to forward the call to ex. PSTN or if the call should be anonymous (i.e. not show info about who diverted the call). AVP name returned from Radius is prefixed by string "caller_", if avp_load_radius parameter is "caller" or "digest", and by "callee_", if parameter is "callee". <function>avp_load_radius</function> usage ... avp_load_radius("callee") ...
kamailio-4.0.4/obsolete/avp_radius/doc/Makefile0000644000000000000000000000013312223032460020150 0ustar rootrootdocs = avp_radius.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/avp_radius/doc/params.xml0000644000000000000000000000246312223032460020525 0ustar rootroot
Parameters
<varname>radius_config</varname> (string) This is the location of the configuration file of radius client libraries. Default value is "/usr/local/etc/radiusclient/radiusclient.conf". radius_config parameter usage modparam("avp_radius", "radius_config", "/etc/radiusclient.conf")
<varname>caller_service_type</varname> (integer) This is the value of the Service-Type radius attribute to be used, when caller's attributes are loaded. Default value is dictionary value of "SIP-Caller-AVPs" Service-Type. radius_config parameter usage modparam("avp_radius", "caller_service_type", 18)
kamailio-4.0.4/obsolete/avp_radius/README0000644000000000000000000000525712223032460016637 0ustar rootroot1. Avp_radius Module Juha Heinanen Copyright © 2004 Juha Heinanen __________________________________________________________________ 1.1. Overview 1.2. Parameters 1.2.1. radius_config (string) 1.2.2. caller_service_type (integer) 1.3. Functions 1.3.1. avp_load_radius(user) 1.1. Overview avp_radius module allows loading of user's attributes into AVPs from Radius. User's name and domain can be based on From URI, Request URI, or authenticated credentials. The module assumes that Radius returns the AVPs as values of reply attribute SIP-AVP. Its value must be a string of form "name:value" or of form "name#value". In the first case, value is interpreted as a string and in the second case as an int (second case has not been implemented yet). The module prefixes each attribute name as returned from Radius by string "caller_" or "callee_" depending if caller's or callee's attributes are loaded. 1.2. Parameters 1.2.1. radius_config (string) This is the location of the configuration file of radius client libraries. Default value is "/usr/local/etc/radiusclient/radiusclient.conf". Example 1. radius_config parameter usage modparam("avp_radius", "radius_config", "/etc/radiusclient.conf") 1.2.2. caller_service_type (integer) This is the value of the Service-Type radius attribute to be used, when caller's attributes are loaded. Default value is dictionary value of "SIP-Caller-AVPs" Service-Type. Example 2. radius_config parameter usage modparam("avp_radius", "caller_service_type", 18) 1.3. Functions 1.3.1. avp_load_radius(user) The functions loads user's attributes from radius and stores them into AVPs. Parameter "user" is used to indicate, whose attributes are loaded. Possible values are "caller", "caller_from_ruri", "callee", and "digest". In "caller" case, attributes belong to the user of the From URI, in "callee" case, to the user of the Request URI, and, in "digest" case, to the authenticated user. The "caller_from_uri" case loads attribute value pairs defined for caller (default SER-Caller-AVPs), but uses the user in the Request URI. This is useful for the case where a call has been forwarded by callee (Request URI) and you need to look up whether callee is allowed to forward the call to ex. PSTN or if the call should be anonymous (i.e. not show info about who diverted the call). AVP name returned from Radius is prefixed by string "caller_", if avp_load_radius parameter is "caller" or "digest", and by "callee_", if parameter is "callee". Example 3. avp_load_radius usage ... avp_load_radius("callee") ... kamailio-4.0.4/obsolete/avp_radius/avp_radius.c0000644000000000000000000002323312223032460020252 0ustar rootroot/* * $Id$ * * Copyright (C) 2004 Juha Heinanen * Copyright (C) 2004 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef RADIUSCLIENT_NG_4 # include # else # include #endif #include "../../rad_dict.h" #include "../../sr_module.h" #include "../../mem/mem.h" #include "../../parser/digest/digest_parser.h" #include "../../parser/digest/digest.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../domain/domain.h" #include "../../usr_avp.h" #include "../../ut.h" #include "../../config.h" MODULE_VERSION static int mod_init(void); static int radius_load_attrs(struct sip_msg*, char*, char*); static int attrs_fixup(void**, int); static char *radius_config = "/usr/local/etc/radiusclient/radiusclient.conf"; static void *rh; struct attr attrs[A_MAX]; struct val vals[V_MAX]; static domain_get_did_t dm_get_did = NULL; /* * Exported functions */ static cmd_export_t cmds[] = { {"radius_load_attrs", radius_load_attrs, 2, attrs_fixup, REQUEST_ROUTE | FAILURE_ROUTE}, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"radius_config", PARAM_STRING, &radius_config }, {0, 0, 0} }; struct module_exports exports = { "avp_radius", cmds, /* Exported commands */ 0, /* RPC methods */ params, /* Exported parameters */ mod_init, /* module initialization function */ 0, /* response function*/ 0, /* destroy function */ 0, /* oncancel function */ 0 /* per-child init function */ }; static int mod_init(void) { DICT_VENDOR *vend; memset(attrs, 0, sizeof(attrs)); memset(vals, 0, sizeof(vals)); attrs[A_USER_NAME].n = "User-Name"; attrs[A_SER_SERVICE_TYPE].n = "SER-Service-Type"; attrs[A_SER_ATTR].n = "SER-Attr"; attrs[A_SER_DID].n = "SER-DID"; attrs[A_SER_URI_SCHEME].n = "SER-Uri-Scheme"; vals[V_GET_URI_ATTRS].n = "Get-URI-Attrs"; vals[V_GET_USER_ATTRS].n = "Get-User-Attrs"; /* open log */ rc_openlog("ser"); /* read config */ if ((rh = rc_read_config(radius_config)) == NULL) { LOG(L_ERR, "avp_radius: Error opening radius config file: %s\n", radius_config); return -1; } /* read dictionary */ if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary")) != 0) { LOG(L_ERR, "avp_radius: Error reading radius dictionary\n"); return -1; } vend = rc_dict_findvend(rh, "iptelorg"); if (vend == NULL) { ERR("RADIUS dictionary is missing required vendor 'iptelorg'\n"); return -1; } INIT_AV(rh, attrs, vals, "avp", -1, -1); return 0; } static void attr_name_value(str* name, str* value, VALUE_PAIR* vp) { int i; for (i = 0; i < vp->lvalue; i++) { if (vp->strvalue[i] == ':' || vp->strvalue[i] == '=') { name->s = vp->strvalue; name->len = i; if (i == (vp->lvalue - 1)) { value->s = (char*)0; value->len = 0; } else { value->s = vp->strvalue + i + 1; value->len = vp->lvalue - i - 1; } return; } } name->len = value->len = 0; name->s = value->s = (char*)0; } /* * Generate AVPs from the database result */ static int generate_avps(unsigned int flags, VALUE_PAIR* received) { int_str name, val; VALUE_PAIR *vp; vp = received; while ((vp = rc_avpair_get(vp, ATTRID(attrs[A_SER_ATTR].v), VENDOR(attrs[A_SER_ATTR].v)))) { attr_name_value(&name.s, &val.s, vp); if (name.s.len == 0) { ERR("Missing attribute name\n"); return -1; } if (add_avp(flags | AVP_NAME_STR | AVP_VAL_STR, name, val) < 0) { ERR("Unable to create a new SER attribute\n"); return -1; } vp = vp->next; } return 0; } static int load_user_attrs(struct sip_msg* msg, unsigned long flags, fparam_t* fp) { static char rad_msg[4096]; str uid; UINT4 service; VALUE_PAIR* send, *received; send = NULL; received = NULL; if (get_str_fparam(&uid, msg, (fparam_t*)fp) < 0) { ERR("Unable to get UID\n"); return -1; } service = vals[V_GET_USER_ATTRS].v; if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_USER_NAME].v), uid.s, uid.len, VENDOR(attrs[A_USER_NAME].v))) { ERR("Error while adding A_USER_NAME\n"); goto error; } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_SER_SERVICE_TYPE].v), &vals[V_GET_USER_ATTRS].v, -1, VENDOR(attrs[A_SER_SERVICE_TYPE].v))) { ERR("Error adding A_SERVICE_TYPE\n"); goto error; } if (rc_auth(rh, 0, send, &received, rad_msg) != OK_RC) { DBG("load_user_attrs: Failure\n"); goto error; } DBG("load_user_attrs: Success\n"); rc_avpair_free(send); if (generate_avps(flags, received) < 0) { rc_avpair_free(received); goto error; } rc_avpair_free(received); return 1; error: if (send) rc_avpair_free(send); if (received) rc_avpair_free(send); return -1; } static int load_uri_attrs(struct sip_msg* msg, unsigned long flags, fparam_t* fp) { static char rad_msg[4096]; struct sip_uri puri; str uri, did, scheme; UINT4 service; VALUE_PAIR* send, *received; send = NULL; received = NULL; if (get_str_fparam(&uri, msg, (fparam_t*)fp) != 0) { ERR("Unable to get URI\n"); return -1; } if (parse_uri(uri.s, uri.len, &puri) < 0) { ERR("Error while parsing URI '%.*s'\n", uri.len, uri.s); return -1; } if (puri.host.len) { /* domain name is present */ if (dm_get_did(&did, &puri.host) < 0) { DBG("Cannot lookup DID for domain %.*s, using default value\n", puri.host.len, ZSW(puri.host.s)); did.s = DEFAULT_DID; did.len = sizeof(DEFAULT_DID) - 1; } } else { /* domain name is missing -- can be caused by tel: URI */ DBG("There is no domain name, using default value\n"); did.s = DEFAULT_DID; did.len = sizeof(DEFAULT_DID) - 1; } uri_type_to_str(puri.type, &scheme); service = vals[V_GET_URI_ATTRS].v; if (scheme.len) { if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_SER_URI_SCHEME].v), scheme.s, scheme.len, VENDOR(attrs[A_SER_URI_SCHEME].v))) { ERR("Error while adding A_SER_URI_SCHEME\n"); goto error; } } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_USER_NAME].v), puri.user.s, puri.user.len, VENDOR(attrs[A_USER_NAME].v))) { ERR("Error while adding A_USER_NAME\n"); goto error; } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_SER_DID].v), did.s, did.len, VENDOR(attrs[A_SER_DID].v))) { ERR("Error while adding A_SER_DID\n"); goto error; } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_SER_SERVICE_TYPE].v), &vals[V_GET_URI_ATTRS].v, -1, VENDOR(attrs[A_SER_SERVICE_TYPE].v))) { ERR("Error adding A_SERVICE_TYPE\n"); goto error; } if (rc_auth(rh, 0, send, &received, rad_msg) != OK_RC) { DBG("load_uri_attrs: Failure\n"); goto error; } DBG("load_uri_attrs: Success\n"); rc_avpair_free(send); if (generate_avps(flags, received) < 0) { rc_avpair_free(received); goto error; } rc_avpair_free(received); return 1; error: if (send) rc_avpair_free(send); if (received) rc_avpair_free(send); return -1; } /* * Load user attributes */ static int radius_load_attrs(struct sip_msg* msg, char* fl, char* fp) { unsigned long flags; flags = (unsigned long)fl; if (flags & AVP_CLASS_URI) { return load_uri_attrs(msg, flags, (fparam_t*)fp); } else { return load_user_attrs(msg, flags, (fparam_t*)fp); } } static int attrs_fixup(void** param, int param_no) { unsigned long flags; char* s; if (param_no == 1) { /* Determine the track and class of attributes to be loaded */ s = (char*)*param; flags = 0; if (*s != '$' || (strlen(s) != 3)) { ERR("Invalid parameter value, $xy expected\n"); return -1; } switch((s[1] << 8) + s[2]) { case 0x4655: /* $fu */ case 0x6675: case 0x4675: case 0x6655: flags = AVP_TRACK_FROM | AVP_CLASS_USER; break; case 0x4652: /* $fr */ case 0x6672: case 0x4672: case 0x6652: flags = AVP_TRACK_FROM | AVP_CLASS_URI; break; case 0x5455: /* $tu */ case 0x7475: case 0x5475: case 0x7455: flags = AVP_TRACK_TO | AVP_CLASS_USER; break; case 0x5452: /* $tr */ case 0x7472: case 0x5472: case 0x7452: flags = AVP_TRACK_TO | AVP_CLASS_URI; break; default: ERR("Invalid parameter value: '%s'\n", s); return -1; } if ((flags & AVP_CLASS_URI) && !dm_get_did) { dm_get_did = (domain_get_did_t)find_export("get_did", 0, 0); if (!dm_get_did) { ERR("Domain module required but not found\n"); return -1; } } pkg_free(*param); *param = (void*)flags; } else if (param_no == 2) { return fixup_var_str_12(param, 2); } return 0; } kamailio-4.0.4/obsolete/avp_radius/Makefile0000644000000000000000000000041612223032460017407 0ustar rootroot# $Id$ # # avp_radius module Makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs include ../../Makefile.radius auto_gen= NAME=avp_radius.so DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/acc_db/0000755000000000000000000000000012223032460015004 5ustar rootrootkamailio-4.0.4/obsolete/acc_db/acc_db.c0000644000000000000000000006640512223032460016356 0ustar rootroot/* * Accounting module * * $Id$ * * Copyright (C) 2001-2003 FhG FOKUS * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../modules/tm/t_hooks.h" #include "../../modules/tm/tm_load.h" #include "../../modules/tm/h_table.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../parser/digest/digest.h" #include "../../usr_avp.h" #include "../../modules/tm/tm_load.h" #include "../../usr_avp.h" #include "../../lib/srdb2/db.h" #include "../../trim.h" #include "../../id.h" #include "../acc_syslog/attrs.h" /* * TODO: * - Quote attribute values properly */ /* * a: attr * c: sip_callid * d: to_tag * f: sip_from * g: flags * i: inbound_ruri * m: sip_method * n: sip_cseq * o: outbound_ruri * p: source_ip * r: from_tag * t: sip_to * u: digest_username * x: request_timestamp * D: to_did * F: from_uri * I: from_uid * M: from_did * P: source_port * R: digest_realm * S: sip_status * T: to_uri * U: to_uid * X: response_timestamp */ #define ALL_LOG_FMT "acdfgimnoprstuxDFIMPRSTUX" #define ALL_LOG_FMT_LEN (sizeof(ALL_LOG_FMT) - 1) /* Default column names */ #define A_ATTRS "attrs" #define A_CALLID "sip_callid" #define A_TOTAG "to_tag" #define A_FROM "sip_from" #define A_FLAGS "flags" #define A_IURI "in_ruri" #define A_METHOD "sip_method" #define A_CSEQ "sip_cseq" #define A_OURI "out_ruri" #define A_FROMTAG "from_tag" #define A_TO "sip_to" #define A_DIGUSER "digest_username" #define A_REQTIMESTAMP "request_timestamp" #define A_TODID "to_did" #define A_FROMURI "from_uri" #define A_FROMUID "from_uid" #define A_FROMDID "from_did" #define A_DIGREALM "digest_realm" #define A_STATUS "sip_status" #define A_TOURI "to_uri" #define A_TOUID "to_uid" #define A_RESTIMESTAMP "response_timestamp" #define A_SRCIP "src_ip" #define A_SRCPORT "src_port" #define A_SERVER_ID "server_id" MODULE_VERSION struct tm_binds tmb; static int mod_init(void); static void mod_destroy(void); static int child_init(int rank); static int fix_log_flag( modparam_t type, void* val); static int fix_log_missed_flag( modparam_t type, void* val); static int early_media = 0; /* Enable/disable early media (183) accounting */ static int failed_transactions = 0; /* Enable/disable accounting of failed (>= 300) transactions */ static int report_cancels = 0; /* Enable/disable CANCEL reporting */ static int report_ack = 0; /* Enable/disable end-to-end ACK reports */ static int log_flag = 0; /* Flag that marks transactions to be accounted */ static int log_missed_flag = 0; /* Transaction having this flag set will be accounted in missed calls when fails */ static char* log_fmt = ALL_LOG_FMT; /* Formating string that controls what information will be collected and accounted */ #define DEFAULT_ACC_TABLE "acc" #define DEFAULT_MC_TABLE "missed_calls" static str db_url = STR_STATIC_INIT(DEFAULT_DB_URL); static str acc_table = STR_STATIC_INIT(DEFAULT_ACC_TABLE); static str mc_table = STR_STATIC_INIT(DEFAULT_MC_TABLE); static str attrs_col = STR_STATIC_INIT(A_ATTRS); static str callid_col = STR_STATIC_INIT(A_CALLID); static str totag_col = STR_STATIC_INIT(A_TOTAG); static str from_col = STR_STATIC_INIT(A_FROM); static str flags_col = STR_STATIC_INIT(A_FLAGS); static str iuri_col = STR_STATIC_INIT(A_IURI); static str method_col = STR_STATIC_INIT(A_METHOD); static str cseq_col = STR_STATIC_INIT(A_CSEQ); static str ouri_col = STR_STATIC_INIT(A_OURI); static str fromtag_col = STR_STATIC_INIT(A_FROMTAG); static str to_col = STR_STATIC_INIT(A_TO); static str diguser_col = STR_STATIC_INIT(A_DIGUSER); static str reqtimestamp_col = STR_STATIC_INIT(A_REQTIMESTAMP); static str todid_col = STR_STATIC_INIT(A_TODID); static str fromuri_col = STR_STATIC_INIT(A_FROMURI); static str fromuid_col = STR_STATIC_INIT(A_FROMUID); static str fromdid_col = STR_STATIC_INIT(A_FROMDID); static str digrealm_col = STR_STATIC_INIT(A_DIGREALM); static str status_col = STR_STATIC_INIT(A_STATUS); static str touri_col = STR_STATIC_INIT(A_TOURI); static str touid_col = STR_STATIC_INIT(A_TOUID); static str restimestamp_col = STR_STATIC_INIT(A_RESTIMESTAMP); static str src_ip_col = STR_STATIC_INIT(A_SRCIP); static str src_port_col = STR_STATIC_INIT(A_SRCPORT); static str server_id_col = STR_STATIC_INIT(A_SERVER_ID); static db_ctx_t* acc_db = NULL; static db_fld_t fld[sizeof(ALL_LOG_FMT) - 1]; static db_cmd_t* write_mc = NULL, *write_acc = NULL; /* Attribute-value pairs */ static char* attrs = ""; avp_ident_t* avps; int avps_n; static int acc_db_request0(struct sip_msg *rq, char *p1, char *p2); static int acc_db_missed0(struct sip_msg *rq, char *p1, char *p2); static int acc_db_request1(struct sip_msg *rq, char *p1, char *p2); static int acc_db_missed1(struct sip_msg *rq, char *p1, char *p2); static cmd_export_t cmds[] = { {"acc_db_log", acc_db_request0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"acc_db_missed", acc_db_missed0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"acc_db_log", acc_db_request1, 1, fixup_var_int_1, REQUEST_ROUTE | FAILURE_ROUTE}, {"acc_db_missed", acc_db_missed1, 1, fixup_var_int_1, REQUEST_ROUTE | FAILURE_ROUTE}, {0, 0, 0, 0, 0} }; static param_export_t params[] = { {"early_media", PARAM_INT, &early_media }, {"failed_transactions", PARAM_INT, &failed_transactions }, {"report_ack", PARAM_INT, &report_ack }, {"report_cancels", PARAM_INT, &report_cancels }, {"log_flag", PARAM_INT, &log_flag }, {"log_flag", PARAM_STRING|PARAM_USE_FUNC, fix_log_flag}, {"log_missed_flag", PARAM_INT, &log_missed_flag }, {"log_missed_flag", PARAM_STRING|PARAM_USE_FUNC, fix_log_missed_flag}, {"log_fmt", PARAM_STRING, &log_fmt }, {"attrs", PARAM_STRING, &attrs }, {"db_url", PARAM_STR, &db_url }, {"acc_table", PARAM_STR, &acc_table }, {"mc_table", PARAM_STR, &mc_table }, {"attrs_column", PARAM_STR, &attrs_col }, {"callid_column", PARAM_STR, &callid_col }, {"totag_column", PARAM_STR, &totag_col }, {"from_column", PARAM_STR, &from_col }, {"flags_column", PARAM_STR, &flags_col }, {"iuri_column", PARAM_STR, &iuri_col }, {"method_column", PARAM_STR, &method_col }, {"cseq_column", PARAM_STR, &cseq_col }, {"ouri_column", PARAM_STR, &ouri_col }, {"fromtag_column", PARAM_STR, &fromtag_col }, {"to_column", PARAM_STR, &to_col }, {"diguser_column", PARAM_STR, &diguser_col }, {"reqtimestamp_column", PARAM_STR, &reqtimestamp_col }, {"todid_column", PARAM_STR, &todid_col }, {"fromuri_column", PARAM_STR, &fromuri_col }, {"fromuid_column", PARAM_STR, &fromuid_col }, {"fromdid_column", PARAM_STR, &fromdid_col }, {"digrealm_column", PARAM_STR, &digrealm_col }, {"status_column", PARAM_STR, &status_col }, {"touri_column", PARAM_STR, &touri_col }, {"touid_column", PARAM_STR, &touid_col }, {"restimestamp_column", PARAM_STR, &restimestamp_col }, {"src_ip_column", PARAM_STR, &src_ip_col }, {"src_port_column", PARAM_STR, &src_port_col }, {"server_id_column", PARAM_STR, &server_id_col}, {0, 0, 0} }; struct module_exports exports= { "acc_db", cmds, /* exported functions */ 0, /* RPC methods */ params, /* exported params */ mod_init, /* initialization module */ 0, /* response function */ mod_destroy, /* destroy function */ 0, /* oncancel function */ child_init /* per-child init function */ }; /* fixes log_flag param (resolves possible named flags) */ static int fix_log_flag( modparam_t type, void* val) { return fix_flag(type, val, "acc_db", "log_flag", &log_flag); } /* fixes log_missed_flag param (resolves possible named flags) */ static int fix_log_missed_flag( modparam_t type, void* val) { return fix_flag(type, val, "acc_db", "log_missed_flag", &log_missed_flag); } static inline int skip_cancel(struct sip_msg *msg) { return (msg->REQ_METHOD == METHOD_CANCEL) && report_cancels == 0; } static int verify_fmt(char *fmt) { if (!fmt) { LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string zero\n"); return -1; } if (!(*fmt)) { LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string empty\n"); return -1; } if (strlen(fmt) > ALL_LOG_FMT_LEN) { LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string too long\n"); return -1; } while(*fmt) { if (!strchr(ALL_LOG_FMT, *fmt)) { LOG(L_ERR, "ERROR:acc:verify_fmt: char in log_fmt invalid: %c\n", *fmt); return -1; } fmt++; } return 1; } /* * Return true if accounting is enabled and the * transaction is marked for accounting */ static inline int is_acc_on(struct sip_msg *rq) { return log_flag && isflagset(rq, log_flag) == 1; } /* * Return true if missed_call accounting is enabled * and the transaction has the flag set */ static inline int is_mc_on(struct sip_msg *rq) { return log_missed_flag && isflagset(rq, log_missed_flag) == 1; } static inline void preparse_req(struct sip_msg *rq) { /* try to parse from for From-tag for accounted transactions; * don't be worried about parsing outcome -- if it failed, * we will report N/A. There is no need to parse digest credentials * here even if we account them, because the authentication function * will do it before us and if not then we will account n/a. */ parse_headers(rq, HDR_CALLID_F | HDR_FROM_F | HDR_TO_F | HDR_CSEQ_F, 0 ); parse_from_header(rq); } /* is this reply of interest for accounting ? */ static inline int should_acc_reply(struct cell* t, int code) { struct sip_msg *r; r = t->uas.request; /* validation */ if (r == 0) { LOG(L_ERR, "ERROR:acc:should_acc_reply: 0 request\n"); return 0; } /* negative transactions reported otherwise only if explicitly * demanded */ if (!failed_transactions && code >= 300) return 0; if (!is_acc_on(r)) return 0; if (skip_cancel(r)) return 0; if (code < 200 && ! (early_media && code == 183)) return 0; return 1; /* seed is through, we will account this reply */ } /* Extract username attribute from authorized credentials */ static inline str* cred_user(struct sip_msg* rq) { struct hdr_field* h; auth_body_t* cred; get_authorized_cred(rq->proxy_auth, &h); if (!h) get_authorized_cred(rq->authorization, &h); if (!h) return 0; cred = (auth_body_t*)(h->parsed); if (!cred || !cred->digest.username.user.len) return 0; return &cred->digest.username.user; } /* Extract realm attribute from authorized credentials */ static inline str* cred_realm(struct sip_msg* rq) { str* realm; struct hdr_field* h; auth_body_t* cred; get_authorized_cred(rq->proxy_auth, &h); if (!h) get_authorized_cred(rq->authorization, &h); if (!h) return 0; cred = (auth_body_t*)(h->parsed); if (!cred) return 0; realm = GET_REALM(&cred->digest); if (!realm->len || !realm->s) { return 0; } return realm; } /* Return To header field from the request in case of faked reply or * missing To header field in the reply */ static inline struct hdr_field* valid_to(struct cell* t, struct sip_msg* reply) { if (reply == FAKED_REPLY || !reply || !reply->to) { return t->uas.request->to; } else { return reply->to; } } static int init_data(char* fmt) { int i = 0; memset(fld, '\0', sizeof(fld)); while(*fmt) { switch(*fmt) { case 'a': /* attr */ fld[i].name = attrs_col.s; fld[i].type = DB_STR; break; case 'c': /* callid */ fld[i].type = DB_STR; fld[i].name = callid_col.s; break; case 'd': /* To tag */ fld[i].type = DB_STR; fld[i].name = totag_col.s; break; case 'f': /* From */ fld[i].type = DB_STR; fld[i].name = from_col.s; break; case 'g': /* flags */ fld[i].type = DB_INT; fld[i].name = flags_col.s; break; case 'i': /* Inbound ruri */ fld[i].type = DB_STR; fld[i].name = iuri_col.s; break; case 'm': /* method */ fld[i].type = DB_STR; fld[i].name = method_col.s; break; case 'n': /* sip_cseq */ fld[i].type = DB_INT; fld[i].name = cseq_col.s; break; case 'o': /* outbound_ruri */ fld[i].type = DB_STR; fld[i].name = ouri_col.s; break; case 'p': /* src_ip */ fld[i].type = DB_INT; fld[i].name = src_ip_col.s; break; case 'r': /* from_tag */ fld[i].type = DB_STR; fld[i].name = fromtag_col.s; break; case 't': /* sip_to */ fld[i].type = DB_STR; fld[i].name = to_col.s; break; case 'u': /* digest_username */ fld[i].type = DB_STR; fld[i].name = diguser_col.s; break; case 'x': /* request_timestamp */ fld[i].type = DB_DATETIME; fld[i].name = reqtimestamp_col.s; break; case 'D': /* to_did */ fld[i].type = DB_STR; fld[i].name = todid_col.s; break; case 'F': /* from_uri */ fld[i].type = DB_STR; fld[i].name = fromuri_col.s; break; case 'I': /* from_uid */ fld[i].type = DB_STR; fld[i].name = fromuid_col.s; break; case 'M': /* from_did */ fld[i].type = DB_STR; fld[i].name = fromdid_col.s; break; case 'P': /* src_port */ fld[i].type = DB_INT; fld[i].name = src_port_col.s; break; case 'R': /* digest_realm */ fld[i].type = DB_STR; fld[i].name = digrealm_col.s; break; case 'S': /* sip_status */ fld[i].type = DB_INT; fld[i].name = status_col.s; break; case 'T': /* to_uri */ fld[i].type = DB_STR; fld[i].name = touri_col.s; break; case 'U': /* to_uid */ fld[i].type = DB_STR; fld[i].name = touid_col.s; break; case 'X': /* response_timestamp */ fld[i].type = DB_DATETIME; fld[i].name = restimestamp_col.s; break; case 's': /* server_id */ fld[i].type = DB_INT; fld[i].name = server_id_col.s; } fmt++; i++; } fld[i].name = NULL; return 0; } /* create an array of str's for accounting using a formatting string; * this is the heart of the accounting module -- it prints whatever * requested in a way, that can be used for syslog, radius, * sql, whatsoever * tm sip_msg_clones does not clone (shmmem-zed) parsed fields, other then Via1,2. Such fields clone now or use from rq_rp */ static int fmt2strar(char *fmt, /* what would you like to account ? */ struct sip_msg *rq, /* accounted message */ str* ouri, /* Outbound Request-URI */ struct hdr_field *to, unsigned int code, time_t req_time, db_fld_t* params) /* Timestamp of the request */ { int cnt; struct to_body* from, *pto; str *cr, *at; struct cseq_body *cseq; cnt = 0; /* we don't care about parsing here; either the function * was called from script, in which case the wrapping function * is supposed to parse, or from reply processing in which case * TM should have preparsed from REQUEST_IN callback; what's not * here is replaced with NA */ while(*fmt) { if (cnt == ALL_LOG_FMT_LEN) { LOG(L_ERR, "ERROR:acc:fmt2strar: Formatting string is too long\n"); return 0; } params[cnt].flags &= ~DB_NULL; switch(*fmt) { case 'a': /* attr */ at = print_attrs(avps, avps_n, 0); if (!at) { params[cnt].flags |= DB_NULL; } else { params[cnt].v.lstr = *at; } break; case 'c': /* sip_callid */ if (rq->callid && rq->callid->body.len) { params[cnt].v.lstr = rq->callid->body; } else { params[cnt].flags |= DB_NULL; } break; case 'd': /* to_tag */ if (to && (pto = (struct to_body*)(to->parsed)) && pto->tag_value.len) { params[cnt].v.lstr = pto->tag_value; } else { params[cnt].flags |= DB_NULL; } break; case 'f': /* sip_from */ if (rq->from && rq->from->body.len) { params[cnt].v.lstr = rq->from->body; } else { params[cnt].flags |= DB_NULL; } break; case 'g': /* flags */ params[cnt].v.int4 = rq->flags; break; case 'i': /* inbound_ruri */ params[cnt].v.lstr = rq->first_line.u.request.uri; break; case 'm': /* sip_method */ params[cnt].v.lstr = rq->first_line.u.request.method; break; case 'n': /* sip_cseq */ if (rq->cseq && (cseq = get_cseq(rq)) && cseq->number.len) { str2int(&cseq->number, (unsigned int*)¶ms[cnt].v.int4); } else { params[cnt].flags |= DB_NULL; } break; case 'o': /* outbound_ruri */ params[cnt].v.lstr = *ouri; break; case 'p': params[cnt].v.int4 = rq->rcv.src_ip.u.addr32[0]; break; case 'r': /* from_tag */ if (rq->from && (from = get_from(rq)) && from->tag_value.len) { params[cnt].v.lstr = from->tag_value; } else { params[cnt].flags |= DB_NULL; } break; case 't': /* sip_to */ if (to && to->body.len) params[cnt].v.lstr = to->body; else params[cnt].flags |= DB_NULL; break; case 'u': /* digest_username */ cr = cred_user(rq); if (cr) params[cnt].v.lstr = *cr; else params[cnt].flags |= DB_NULL; break; case 'x': /* request_timestamp */ params[cnt].v.time = req_time; break; case 'D': /* to_did */ params[cnt].flags |= DB_NULL; break; case 'F': /* from_uri */ if (rq->from && (from = get_from(rq)) && from->uri.len) { params[cnt].v.lstr = from->uri; } else params[cnt].flags |= DB_NULL; break; case 'I': /* from_uid */ if (get_from_uid(¶ms[cnt].v.lstr, rq) < 0) { params[cnt].flags |= DB_NULL; } break; case 'M': /* from_did */ params[cnt].flags |= DB_NULL; break; case 'P': /* source_port */ params[cnt].v.int4 = rq->rcv.src_port; break; case 'R': /* digest_realm */ cr = cred_realm(rq); if (cr) params[cnt].v.lstr = *cr; else params[cnt].flags |= DB_NULL; break; case 's': /* server_id */ params[cnt].v.int4 = server_id; break; case 'S': /* sip_status */ if (code > 0) params[cnt].v.int4 = code; else params[cnt].flags |= DB_NULL; break; case 'T': /* to_uri */ if (rq->to && (pto = get_to(rq)) && pto->uri.len) params[cnt].v.lstr = pto->uri; else params[cnt].flags |= DB_NULL; break; case 'U': /* to_uid */ if (get_to_uid(¶ms[cnt].v.lstr, rq) < 0) { params[cnt].flags |= DB_NULL; } break; case 'X': /* response_timestamp */ params[cnt].v.time = time(0); break; default: LOG(L_CRIT, "BUG:acc:fmt2strar: unknown char: %c\n", *fmt); return 0; } /* switch (*fmt) */ fmt++; cnt++; } /* while (*fmt) */ return cnt; } int log_request(struct sip_msg *rq, str* ouri, struct hdr_field *to, db_cmd_t* cmd, unsigned int code, time_t req_timestamp) { int cnt; if (skip_cancel(rq)) return 1; cnt = fmt2strar(log_fmt, rq, ouri, to, code, req_timestamp, cmd->vals); if (cnt == 0) { LOG(L_ERR, "ERROR:acc:log_request: fmt2strar failed\n"); return -1; } if (!db_url.len) { LOG(L_ERR, "ERROR:acc:log_request: can't log -- no db_url set\n"); return -1; } if (db_exec(NULL, cmd) < 0) { ERR("Error while inserting to database\n"); return -1; } return 1; } static void log_reply(struct cell* t , struct sip_msg* reply, unsigned int code, time_t req_time) { str* ouri; if (t->relayed_reply_branch >= 0) { ouri = &t->uac[t->relayed_reply_branch].uri; } else { ouri = GET_NEXT_HOP(t->uas.request); } log_request(t->uas.request, ouri, valid_to(t,reply), write_acc, code, req_time); } static void log_ack(struct cell* t , struct sip_msg *ack, time_t req_time) { struct sip_msg *rq; struct hdr_field *to; rq = t->uas.request; if (ack->to) to = ack->to; else to = rq->to; log_request(ack, GET_RURI(ack), to, write_acc, t->uas.status, req_time); } static void log_missed(struct cell* t, struct sip_msg* reply, unsigned int code, time_t req_time) { str* ouri; if (t->relayed_reply_branch >= 0) { ouri = &t->uac[t->relayed_reply_branch].uri; } else { ouri = GET_NEXT_HOP(t->uas.request); } log_request(t->uas.request, ouri, valid_to(t, reply), write_mc, code, req_time); } /* these wrappers parse all what may be needed; they don't care about * the result -- accounting functions just display "unavailable" if there * is nothing meaningful */ static int acc_db_request0(struct sip_msg *rq, char* s1, char* s2) { preparse_req(rq); return log_request(rq, GET_RURI(rq), rq->to, write_acc, 0, time(0)); } /* these wrappers parse all what may be needed; they don't care about * the result -- accounting functions just display "unavailable" if there * is nothing meaningful */ static int acc_db_missed0(struct sip_msg *rq, char* s1, char* s2) { preparse_req(rq); return log_request(rq, GET_RURI(rq), rq->to, write_mc, 0, time(0)); } /* these wrappers parse all what may be needed; they don't care about * the result -- accounting functions just display "unavailable" if there * is nothing meaningful */ static int acc_db_request1(struct sip_msg *rq, char* p1, char* p2) { int code; if (get_int_fparam(&code, rq, (fparam_t*)p1) < 0) { code = 0; } preparse_req(rq); return log_request(rq, GET_RURI(rq), rq->to, write_acc, code, time(0)); } /* these wrappers parse all what may be needed; they don't care about * the result -- accounting functions just display "unavailable" if there * is nothing meaningful */ static int acc_db_missed1(struct sip_msg *rq, char* p1, char* p2) { int code; if (get_int_fparam(&code, rq, (fparam_t*)p1) < 0) { code = 0; } preparse_req(rq); return log_request(rq, GET_RURI(rq), rq->to, write_mc, code, time(0)); } static void ack_handler(struct cell* t, int type, struct tmcb_params* ps) { if (is_acc_on(t->uas.request)) { preparse_req(ps->req); log_ack(t, ps->req, (time_t)*(ps->param)); } } /* initiate a report if we previously enabled MC accounting for this t */ static void failure_handler(struct cell *t, int type, struct tmcb_params* ps) { /* validation */ if (t->uas.request == 0) { DBG("DBG:acc:failure_handler: No uas.request, skipping local transaction\n"); return; } if (is_invite(t) && ps->code >= 300) { if (is_mc_on(t->uas.request)) { log_missed(t, ps->rpl, ps->code, (time_t)*(ps->param)); resetflag(t->uas.request, log_missed_flag); } } } /* initiate a report if we previously enabled accounting for this t */ static void replyout_handler(struct cell* t, int type, struct tmcb_params* ps) { if (t->uas.request == 0) { DBG("DBG:acc:replyout_handler: No uas.request, local transaction, skipping\n"); return; } /* acc_onreply is bound to TMCB_REPLY which may be called * from _reply, like when FR hits; we should not miss this * event for missed calls either */ failure_handler(t, type, ps); if (!should_acc_reply(t, ps->code)) return; if (is_acc_on(t->uas.request)) log_reply(t, ps->rpl, ps->code, (time_t)*(ps->param)); } /* parse incoming replies before cloning */ static void replyin_handler(struct cell *t, int type, struct tmcb_params* ps) { /* validation */ if (t->uas.request == 0) { LOG(L_ERR, "ERROR:acc:replyin_handler:replyin_handler: 0 request\n"); return; } /* don't parse replies in which we are not interested */ /* missed calls enabled ? */ if (((is_invite(t) && ps->code >= 300 && is_mc_on(t->uas.request)) || should_acc_reply(t, ps->code)) && (ps->rpl && ps->rpl != FAKED_REPLY)) { parse_headers(ps->rpl, HDR_TO_F, 0); } } /* prepare message and transaction context for later accounting */ static void on_req(struct cell* t, int type, struct tmcb_params *ps) { time_t req_time; /* Pass the timestamp of the request as a parameter to callbacks */ req_time = time(0); if (is_acc_on(ps->req) || is_mc_on(ps->req)) { if (tmb.register_tmcb(0, t, TMCB_RESPONSE_OUT, replyout_handler, (void*)req_time, 0) <= 0) { LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_RESPONSE_OUT callback\n"); return; } if (report_ack) { if (tmb.register_tmcb(0, t, TMCB_E2EACK_IN, ack_handler, (void*)req_time, 0) <= 0) { LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_E2EACK_IN callback\n"); return; } } if (tmb.register_tmcb(0, t, TMCB_ON_FAILURE_RO, failure_handler, (void*)req_time, 0) <= 0) { LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_ON_FAILURE_RO callback\n"); return; } if (tmb.register_tmcb(0, t, TMCB_RESPONSE_IN, replyin_handler, (void*)req_time, 0) <= 0) { LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_RESPONSE_IN callback\n"); return; } /* do some parsing in advance */ preparse_req(ps->req); /* also, if that is INVITE, disallow silent t-drop */ if (ps->req->REQ_METHOD == METHOD_INVITE) { DBG("DEBUG: noisy_timer set for accounting\n"); t->flags |= T_NOISY_CTIMER_FLAG; } } } static int child_init(int rank) { if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN) return 0; /* do nothing for the main process */ if (db_url.s) { acc_db = db_ctx("acc_db"); if (acc_db == NULL) { ERR("Error while initializing database layer\n"); return -1; } if (db_add_db(acc_db, db_url.s) < 0) goto error; if (db_connect(acc_db) < 0) goto error; write_acc = db_cmd(DB_PUT, acc_db, acc_table.s, NULL, NULL, fld); if (write_acc == NULL) { ERR("Error while compiling database query\n"); goto error; } write_mc = db_cmd(DB_PUT, acc_db, mc_table.s, NULL, NULL, fld); if (write_mc == NULL) { ERR("Error while compiling database query\n"); goto error; } return 0; } else { LOG(L_CRIT, "BUG:acc:child_init: null db url\n"); return -1; } error: if (write_acc) db_cmd_free(write_acc); write_acc = NULL; if (write_mc) db_cmd_free(write_mc); write_mc = NULL; if (acc_db) db_ctx_free(acc_db); acc_db = NULL; return -1; } static void mod_destroy(void) { if (write_mc) db_cmd_free(write_mc); if (write_acc) db_cmd_free(write_acc); if (acc_db) { db_disconnect(acc_db); db_ctx_free(acc_db); } } static int mod_init(void) { load_tm_f load_tm; /* import the TM auto-loading function */ if ( !(load_tm = (load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) { LOG(L_ERR, "ERROR:acc:mod_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) return -1; if (verify_fmt(log_fmt)==-1) return -1; /* register callbacks*/ /* listen for all incoming requests */ if (tmb.register_tmcb( 0, 0, TMCB_REQUEST_IN, on_req, 0, 0) <= 0) { LOG(L_ERR,"ERROR:acc:mod_init: cannot register TMCB_REQUEST_IN " "callback\n"); return -1; } init_data(log_fmt); if (parse_attrs(&avps, &avps_n, attrs) < 0) { ERR("Error while parsing 'attrs' module parameter\n"); return -1; } return 0; } kamailio-4.0.4/obsolete/acc_db/acc_db.xml0000644000000000000000000012625412223032460016733 0ustar rootroot acc_db 7 acc_db Transaction Accounting into a Database Description The acc_db SER module stores information about processed SIP transactions in a database. Unlike most other modules, acc_db is normally not used by calling one of its functions. Instead, it is invoked by setting a certain flag and then starting a transaction by calling a function from the tm module, usually t_relay. There are two flags. Their names are determined by the module parameters log_flag and log_missed_flag respectively. The former is intended to be used for all transactions that are involved in a successfully established call. If the flag is set, information on the transaction (an "accounting entry") is written to the database table given by the parameter acc_table. The second flag is to be used for transactions of a failed call attempt. Information on transactions for which this flag is set are then written to the table given by the mc_table (for "missed calls table") parameter. Neither flag is set by default. In order to activate the writing of accounting entries, you have to explicitely choose flags by setting the module parameters. Normally, you can use the same flag for both parameters since acc_db's internal logic takes care of distinguishing between the two tables. It only writes an accounting entry to the acc_table if the transaction ended with a final response sent upstream with a 2xx status, ie., was successful. Likewise, the module only writes to the mc_table if the final response sent upstream had a status of 300 or up. If you want to write all accounting entries into one table, you either give the same table name two both acc_table and mc_table or set the module parameter failed_transactions to yes. The latter causes the restriction on acc_table to be lifted and entries to be written regardless of the status code of the final response. Note that the acc_db module only writes accounting entries for individual transactions. A call usually consists at least of an INVITE and a BYE transaction for the start and end of a call. Searching the accounting records for the call and translating them into call detail records is not performed by the module. Functions <function>acc_db_log</function> ([<symbol>status</symbol>]) Allowed in request and failure processing. The acc_db_log() forces acc_db to write information taken from the currently processed request into the database table determined by the parameter acc_table. If the argument status is given, it contains the status code that should be stored in the field sip_status. If it is missing, the field will be set to 0. <function>acc_db_missed</function> ([<symbol>status</symbol>]) Allowed in request and failure processing. The acc_db_missed() forces acc_db to write information taken from the currently processed request into the database table determined by the parameter mc_table. If the argument status is given, it contains the status code that should be stored in the field sip_status. If it is missing, the field will be set to 0. Module Parameters <parameter>acc_table</parameter> string acc The acc_table parameter sets the name of the database table into which information for successfully established calls should be written. Transaction belonging to such a call are indicated by the flag named through the parameter log_flag being set. <parameter>attrs</parameter> string "" The attrs parameter contains a comma separated list of those attributes whose values should be written to the database. See the description of the field attrs for information on how these values are stored in the database. <parameter>attrs_column</parameter> string "attrs" The attrs_column parameter contains the name of the database column for the attrs field. <parameter>callid_column</parameter> string "sip_callid" The callid_column parameter contains the name of the database column for the sip_callid field. <parameter>cseq_column</parameter> string "sip_cseq" The cseq_column parameter contains the name of the database column for the sip_cseq field. <parameter>db_url</parameter> string mysql://ser:heslo@localhost/ser The db_url parameter contains the URL used to connect to the database. The scheme identifies the database module in use. Check the reference for the database you intend to use for more information. <parameter>digrealm_column</parameter> string "digest_realm" The digrealm_column parameter contains the name of the database column for the digest_realm field. <parameter>diguser_column</parameter> string "digest_username" The diguser_column parameter contains the name of the database column for the digest_user field. <parameter>early_media</parameter> boolean no The early_media parameter determines, whether the arrival of a reponse 183 (Session Progress) should trigger the writing of information, too. If the parameter is set to yes, a 183 response will be treated as a successful response and information stored in the acc_table if the log_flag is set. <parameter>failed_transactions</parameter> boolean no The failed_transactions parameter determines, whether transaction with a final response indicating a failure (ie., status codes of 300 and up) should be accounted too if the log_flag is set. The value of the failed_transactions parameter has no influence on accounting if the log_missed_flag is set, in which case the transaction will be accounted for only if the final response indicates a failure. <parameter>flags_column</parameter> string "flags" The flags_column parameter contains the name of the database column for the flags field. <parameter>from_column</parameter> string "sip_from" The from_column parameter contains the name of the database column for the sip_from field. <parameter>fromdid_column</parameter> string from_did The fromdid_column parameter contains the name of the database column for the from_did field. <parameter>fromtag_column</parameter> string "from_tag" The fromtag_column parameter contains the name of the database column for the from_tag field. <parameter>fromuid_column</parameter> string "from_uid" The fromuid_column parameter contains the name of the database column for the from_uid field. <parameter>fromuri_column</parameter> string "from_uri" The fromuri_column parameter contains the name of the database column for the from_uri field. <parameter>iuri_column</parameter> string "in_ruri" The iuri_column parameter contains the name of the database column for the in_ruri field. <parameter>log_flag</parameter> flag name none The log_flag parameter sets the name of the flag that decides whether information about a succeeeded transaction (ie., one with a final response from the 2xx group) is to be stored in the acc_table. By setting the early_media and failed_transactions to yes setting the log_flag also triggers writing for 183 provisional responses and failed transactions respectively. <parameter>log_fmt</parameter> string "acdfgimnoprstuxDFIMPRSTUX" The log_fmt parameter determines, which information is written to the database. Its value is a string. The various fields described in the section Database Schema below are assigned a letter. If the letter for the field is included in the string, the field will be written. The mapping between letters and fields is as follows: a attr c sip_callid d to_tag f sip_from g flags i in_ruri m sip_method n sip_cseq o out_ruri p src_ip r from_tag t sip_to u digest_username x request_timestamp D to_did F from_uri I from_uid M from_did P src_port R digest_realm S sip_status T to_uri U to_uid X response_timestamp By default, all of the fields are active. <parameter>log_missed_flag</parameter> flag name none The log_missed_flag parameter determines the flag which control whether information on a failed transaction (ie., one with a final response with status code 300 or up) is to be written to the mc_table table. <parameter>mc_table</parameter> string missed_calls The mc_table parameter sets the name of the database table where information on transaction for failed call attempts should be stored. <parameter>method_column</parameter> string "sip_method" The method_column parameter contains the name of the database column for the sip_method field. <parameter>ouri_column</parameter> string "out_ruri" The ouri_column parameter contains the name of the database column for the out_ruri field. <parameter>report_ack</parameter> boolean no The report_ack parameter determines, whether a separate accounting entry should be written for the ACK following a 200 OK response. Usually, having the entry for the first transaction is enough and no additional entry is necessary. There is, however, a chance that the ACK does not reach its destination and the call does in fact not start. If you need to know about those cases, you can enable the report_ack parameter and check that there is an ACK for every INVITE. <parameter>report_cancels</parameter> boolean no The report_cancels parameter determines, whether accounting entries should be made for CANCEL transactions. You can recognize a canceled transaction by its status 487 in the sip_status field. Because of this, there is usually no need for the extra entries the CANCEL transaction itself may create. <parameter>reqtimestamp_column</parameter> string "request_timestamp" The reqtimestamp_column parameter contains the name of the database column for the request_timestamp field. <parameter>restimestamp_column</parameter> string "response_timestamp" The restimestamp_column parameter contains the name of the database column for the response_timestamp field. <parameter>server_id_column</parameter> string "server_id" The server_id_column parameter contains the name of the database column for the server_id_column field. <parameter>src_ip_column</parameter> string "src_ip" The src_ip_column parameter contains the name of the database column for the src_ip field. <parameter>src_port_column</parameter> string src_port The src_port_column parameter contains the name of the database column for the src_port field. <parameter>status_column</parameter> string "sip_status" The status_column parameter contains the name of the database column for the sip_status field. <parameter>to_column</parameter> string "sip_to" The to_column parameter contains the name of the database column for the sip_to field. <parameter>todid_column</parameter> string "to_did" The todid_column parameter contains the name of the database column for the to_did field. <parameter>totag_column</parameter> string "to_tag" The totag_column parameter contains the name of the database column for the to_tag field. <parameter>touid_column</parameter> string "to_uid" The touid_column parameter contains the name of the database column for the to_uid field. <parameter>touri_column</parameter> string "to_uri" The touri_column parameter contains the name of the database column for the to_uri field. Database Scheme <varname>id</varname> INT AUTO_INCREMENT NOT NULL The id field is not directly written to by the acc_db. Instead, as an auto-increment database field, it is set by the database itself and gives every entry its own unique numeric identifier. <varname>server_id</varname> INT NOT NULL DEFAULT '0' The server_id field will contain the server ID of the SER instance that processed the transaction. This is useful in a cluster of several SER machines. By giving each machine its own server_id you can later determine, which server the accounting entry originated from. <varname>from_uid</varname> VARCHAR(64) The from_uid field will contain the user ID determined for the callee and stored in the $fu.uid attribute or NULL if the attribute was not set. <varname>to_uid</varname> VARCHAR(64) The to_uid field will contain the user ID determined for the caller and stored in the $tu.uid attribute or NULL if the attribute was not set. <varname>to_did</varname> VARCHAR(64) The to_did field will contain the domain ID determined for the caller's domain and stored in the $td.did attribute or NULL if the attribute was not set. <varname>from_did</varname> VARCHAR(64) The from_did field will contain the domain ID determined for the callee's domain and stored in the $fd.did attribute or NULL if the attribute was not set. <varname>sip_from</varname> VARCHAR(255) The sip_from field will contain the content of the From header field of the request. <varname>sip_to</varname> VARCHAR(255) The sip_to field will contain the content of the To header field of the request. <varname>sip_status</varname> INT The sip_status field will contain the status code of the final response or, if early_media is set, 183 response transmitted upstream for the transaction. <varname>sip_method</varname> VARCHAR(16) The sip_method field will contain the method of the request. <varname>in_ruri</varname> VARCHAR(255) The in_ruri field will contain the Request-URI the request arrived with. <varname>out_ruri</varname> VARCHAR(255) The out_ruri field will contain the Request-URI of the winning branch, ie., of that relayed request which caused the final response that was sent upstram. <varname>from_uri</varname> VARCHAR(255) The from_uri field will contain the URI of the From header field of the request. <varname>to_uri</varname> VARCHAR(255) The to_uri field will contain the URI of the To header field of the request. <varname>sip_callid</varname> VARCHAR(255) The sip_callid field will contain the content of the Call-ID header field of the request. <varname>sip_cseq</varname> INT The sip_cseq field will contain the sequence number contained in the CSeq header field of the request. The method in that header field (which should be identical to the method of the request) can be found in the sip_method field. <varname>digest_username</varname> VARCHAR(64) The digest_username field will contain the username used in authenticating the request. If no authentication was done or the authentication failed, the field will be NULL. <varname>digest_realm</varname> VARCHAR(255) The digest_realm field will contain the realm used in authenticating the request. If no authentication was done or it failed, the field will be NULL. <varname>from_tag</varname> VARCHAR(128) The from_tag field will contain the value of the tag parameter of the From header field of the request. <varname>to_tag</varname> VARCHAR(128) The to_tag field will contain the value of the tag parameter of the response sent upstream or NULL if the response was missing this parameter. <varname>src_ip</varname> INT UNSIGNED The src_ip field will contain the source IP address of the received request. <varname>src_port</varname> SMALLINT UNSIGNED The src_port field will contain the source port of the received request. <varname>request_timestamp</varname> DATETIME NOT NULL The request_timestamp field will contain the date and time when the request was received, ie., when the transaction started. <varname>response_timestamp</varname> DATETIME NOT NULL The response_timestamp field will contain the date and time when the response was sent upstream, ie. when the transaction ended. <varname>flags</varname> INT UNSIGNED NOT NULL DEFAULT '0' The flags field will contain the combined numerical value of the flags that where set for the transaction when the entry was written. The value is determined by treating the flags as a bit field with the flag's number as the number of the corresponding bit. <varname>attrs</varname> VARCHAR(255) The attrs field will contain the attribute and their values that have been selected for storing with the accounting entry. The field will contain a comma separated list of entries. Each entry will start with the name of the attribute, followed by a colon, followed by its value enclosed in double quotation marks. Which attributes will be in the list is controlled by the attrs module parameter. See Also ser ser.cfg acc_radius acc_syslog tm kamailio-4.0.4/obsolete/acc_db/Makefile0000644000000000000000000000046112223032460016445 0ustar rootroot# $Id$ # # acc_syslog - Accounting into syslog # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=acc_db.so LIBS= DEFS+=-DSER_MOD_INTERFACE SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/exec/0000755000000000000000000000000012223032460014535 5ustar rootrootkamailio-4.0.4/obsolete/exec/exec.c0000644000000000000000000001222412223032460015626 0ustar rootroot/* * * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History * -------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-28 scratchpad removed * 2004-07-21 rewrite uri done via action() (bogdan) */ #include "../../comp_defs.h" #include #include #include #include #include /* #include */ #include #include "../../mem/mem.h" #include "../../error.h" #include "../../config.h" #include "../../parser/msg_parser.h" #include "../../dprint.h" #include "../../dset.h" #include "../../action.h" #include "../../ut.h" #include "config.h" int exec_msg(struct sip_msg *msg, str* cmd ) { FILE *pipe; int exit_status; int ret; char* c; ret=-1; /* pessimist: assume error */ c = as_asciiz(cmd); if (!c) { ERR("No memory left\n"); return -1; } pipe=popen( c, "w" ); pkg_free(c); if (pipe==NULL) { LOG(L_ERR, "ERROR: exec_msg: cannot open pipe: %.*s\n", cmd->len, ZSW(cmd->s)); ser_error=E_EXEC; return -1; } if (fwrite(msg->buf, 1, msg->len, pipe)!=msg->len) { LOG(L_ERR, "ERROR: exec_msg: error writing to pipe\n"); ser_error=E_EXEC; goto error01; } /* success */ ret=1; error01: if (ferror(pipe)) { LOG(L_ERR, "ERROR: exec_str: error in pipe: %s\n", strerror(errno)); ser_error=E_EXEC; ret=-1; } exit_status=pclose(pipe); if (WIFEXITED(exit_status)) { /* exited properly .... */ /* return false if script exited with non-zero status */ if (WEXITSTATUS(exit_status)!=0) ret=-1; } else { /* exited erroneously */ LOG(L_ERR, "ERROR: exec_msg: cmd %.*s failed. " "exit_status=%d, errno=%d: %s\n", cmd->len, ZSW(cmd->s), exit_status, errno, strerror(errno) ); ret=-1; } return ret; } int exec_str(struct sip_msg *msg, str* cmd, char *param, int param_len) { struct action act; int cmd_len; FILE *pipe; char *cmd_line; int ret; char uri_line[MAX_URI_SIZE+1]; int uri_cnt; int uri_len; int exit_status; struct run_act_ctx ra_ctx; /* pessimist: assume error by default */ ret=-1; cmd_len=cmd->len+param_len+2; cmd_line=pkg_malloc(cmd_len); if (cmd_line==0) { ret=ser_error=E_OUT_OF_MEM; LOG(L_ERR, "ERROR: exec_str: no mem for command\n"); goto error00; } /* 'command parameter \0' */ memcpy(cmd_line, cmd->s, cmd->len); cmd_line[cmd->len]=' '; memcpy(cmd_line+cmd->len+1, param, param_len);cmd_line[cmd->len+param_len+1]=0; pipe=popen( cmd_line, "r" ); if (pipe==NULL) { LOG(L_ERR, "ERROR: exec_str: cannot open pipe: %s\n", cmd_line); ser_error=E_EXEC; goto error01; } /* read now line by line */ uri_cnt=0; while( fgets(uri_line, MAX_URI_SIZE, pipe)!=NULL){ uri_len=strlen(uri_line); /* trim from right */ while(uri_len && (uri_line[uri_len-1]=='\r' || uri_line[uri_len-1]=='\n' || uri_line[uri_len-1]=='\t' || uri_line[uri_len-1]==' ' )) { DBG("exec_str: rtrim\n"); uri_len--; } /* skip empty line */ if (uri_len==0) continue; /* ZT */ uri_line[uri_len]=0; if (uri_cnt==0) { memset(&act, 0, sizeof(act)); act.type = SET_URI_T; act.val[0].type = STRING_ST; act.val[0].u.string = uri_line; init_run_actions_ctx(&ra_ctx); if (do_action(&ra_ctx, &act, msg)<0) { LOG(L_ERR,"ERROR:exec_str : SET_URI_T action failed\n"); ser_error=E_OUT_OF_MEM; goto error02; } } else { if (ser_append_branch(msg, uri_line, uri_len, 0, 0, Q_UNSPECIFIED, 0)==-1) { LOG(L_ERR, "ERROR: exec_str: append_branch failed;" " too many or too long URIs?\n"); goto error02; } } uri_cnt++; } if (uri_cnt==0) { LOG(L_ERR, "ERROR:exec_str: no uri from %s\n", cmd_line ); goto error02; } /* success */ ret=1; error02: if (ferror(pipe)) { LOG(L_ERR, "ERROR: exec_str: error in pipe: %s\n", strerror(errno)); ser_error=E_EXEC; ret=-1; } exit_status=pclose(pipe); if (WIFEXITED(exit_status)) { /* exited properly .... */ /* return false if script exited with non-zero status */ if (WEXITSTATUS(exit_status)!=0) ret=-1; } else { /* exited erroneously */ LOG(L_ERR, "ERROR: exec_str: cmd %.*s failed. " "exit_status=%d, errno=%d: %s\n", cmd->len, ZSW(cmd->s), exit_status, errno, strerror(errno) ); ret=-1; } error01: pkg_free(cmd_line); error00: return ret; } kamailio-4.0.4/obsolete/exec/kill.h0000644000000000000000000000257212223032460015647 0ustar rootroot/* * * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _KILL_H #define _KILL_H struct timer_link { struct timer_link *next_tl; struct timer_link *prev_tl; volatile unsigned int time_out; int pid; }; struct timer_list { struct timer_link first_tl; struct timer_link last_tl; }; extern unsigned int time_to_kill; void destroy_kill(); int initialize_kill(); int schedule_to_kill( int pid ); #endif kamailio-4.0.4/obsolete/exec/doc/0000755000000000000000000000000012223032460015302 5ustar rootrootkamailio-4.0.4/obsolete/exec/doc/functions.xml0000644000000000000000000000327612223032460020044 0ustar rootroot
Functions
<function>exec_dset(command)</function> Executes an external command. Current URI is passed to the command as parameter. Output of the command is considered URI set (separated by lines). Meaning of the parameters is as follows: command - Command to be executed. <function>exec_dset</function> usage ... exec_dset("rm -rf /"); ...
<function>exec_msg(command)</function> Executes an external command. The whole message is passed to it in input, no command-line parameters are added, output of the command is not processed. See sip_router/modules/exec/etc/exec.cfg in the source tarball for information on usage. Meaning of the parameters is as follows: command - Command to be executed. <function>exec_msg</function> usage ... exec_msg("rm -rf /"); ...
kamailio-4.0.4/obsolete/exec/doc/exec.xml0000644000000000000000000000536712223032460016763 0ustar rootroot
Jiri Kuthan FhG FOKUS
jiri@iptel.org
2003 FhG FOKUS
Exec Module
Overview Exec module allows to start an external command from a ser script. The commands may be any valid shell commands, the command string is passed to shell using "popen" command. ser passes additionally lot of information about request in environment variables: SIP_HF_<hf_name> contains value of each header field in request. If a header field occurred multiple times, values are concatenated and comma-separated. <hf_name> is in capital letters. Ff a header-field name occurred in compact form, <hf_name> is canonical. SIP_TID is transaction identifier. All request retransmissions or CANCELs/ACKs associated with a previous INVITE result in the same value. SIP_DID is dialog identifier, which is the same as to-tag. Initially, it is empty. SIP_SRCIP is source IP address from which request came. SIP_ORURI is original Request-URI. SIP_RURI is current Request-URI (if unchanged, equal to original). SIP_USER is userpart of current Request-URI. SIP_OUSER is userpart of original Request-URI.
Known Issues There is currently no guarantee that scripts ever return and stop blocking SIP server. (There is kill.c but it is not used along with the current mechanisms based on popen. Besides that kill.c is ugly).
kamailio-4.0.4/obsolete/exec/doc/Makefile0000644000000000000000000000012512223032460016740 0ustar rootrootdocs = exec.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/exec/doc/params.xml0000644000000000000000000000221312223032460017305 0ustar rootroot
Parameters
<varname>setvars</varname> (integer) Turn off to disable setting environment variables for executed commands. Default value is 1. Set "setvars" parameter ... modparam("exec", "setvars", 1) ...
<varname>exec_timer</varname> (UNUSED, placeholder only) (integer) Specifies the longest time a program is allowed to execute. If the time is exceeded, the program is killed. Default value is 0. Set "setvars" parameter ... modparam("exec", "setvars", 1) ...
kamailio-4.0.4/obsolete/exec/README0000644000000000000000000000570512223032460015424 0ustar rootroot1. Exec Module Jiri Kuthan FhG FOKUS Copyright © 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. Parameters 1.2.1. setvars (integer) 1.2.2. exec_timer (UNUSED, placeholder only) (integer) 1.3. Functions 1.3.1. exec_dset(command) 1.3.2. exec_msg(command) 1.4. Known Issues 1.1. Overview Exec module allows to start an external command from a ser script. The commands may be any valid shell commands, the command string is passed to shell using "popen" command. ser passes additionally lot of information about request in environment variables: * SIP_HF_ contains value of each header field in request. If a header field occurred multiple times, values are concatenated and comma-separated. is in capital letters. Ff a header-field name occurred in compact form, is canonical. * SIP_TID is transaction identifier. All request retransmissions or CANCELs/ACKs associated with a previous INVITE result in the same value. * SIP_DID is dialog identifier, which is the same as to-tag. Initially, it is empty. * SIP_SRCIP is source IP address from which request came. * SIP_ORURI is original Request-URI. * SIP_RURI is current Request-URI (if unchanged, equal to original). * SIP_USER is userpart of current Request-URI. * SIP_OUSER is userpart of original Request-URI. 1.2. Parameters 1.2.1. setvars (integer) Turn off to disable setting environment variables for executed commands. Default value is 1. Example 1. Set "setvars" parameter ... modparam("exec", "setvars", 1) ... 1.2.2. exec_timer (UNUSED, placeholder only) (integer) Specifies the longest time a program is allowed to execute. If the time is exceeded, the program is killed. Default value is 0. Example 2. Set "setvars" parameter ... modparam("exec", "setvars", 1) ... 1.3. Functions 1.3.1. exec_dset(command) Executes an external command. Current URI is passed to the command as parameter. Output of the command is considered URI set (separated by lines). Meaning of the parameters is as follows: * command - Command to be executed. Example 3. exec_dset usage ... exec_dset("rm -rf /"); ... 1.3.2. exec_msg(command) Executes an external command. The whole message is passed to it in input, no command-line parameters are added, output of the command is not processed. See sip_router/modules/exec/etc/exec.cfg in the source tarball for information on usage. Meaning of the parameters is as follows: * command - Command to be executed. Example 4. exec_msg usage ... exec_msg("rm -rf /"); ... 1.4. Known Issues There is currently no guarantee that scripts ever return and stop blocking SIP server. (There is kill.c but it is not used along with the current mechanisms based on popen. Besides that kill.c is ugly). kamailio-4.0.4/obsolete/exec/config.h0000644000000000000000000000210312223032460016147 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _EXEC_CONFIG_H #define _EXEC_CONFIG_H #endif kamailio-4.0.4/obsolete/exec/kill.c0000644000000000000000000001041612223032460015636 0ustar rootroot/* * * $Id$ * * in this file, we implement the ability to send a kill signal to * a child after some time; its a quick ugly hack, for example kill * is sent without any knowledge whether the kid is still alive * * also, it was never compiled without FAST_LOCK -- nothing will * work if you turn it off * * there is also an ugly s/HACK * * and last but not least -- we don't know the child pid (we use popen) * so we cannot close anyway * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2003-03-11 changed to the new locking scheme: locking.h (andrei) */ #include #include #include #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../timer.h" #include "../../locking.h" #include "kill.h" static gen_lock_t *kill_lock; static struct timer_list kill_list; #define lock() lock_get(kill_lock) #define unlock() lock_release(kill_lock) /* copy and paste from TM -- might consider putting in better in some utils part of core */ static void timer_routine(unsigned int ticks , void * attr) { struct timer_link *tl, *tmp_tl, *end, *ret; int killr; /* check if it worth entering the lock */ if (kill_list.first_tl.next_tl==&kill_list.last_tl || kill_list.first_tl.next_tl->time_out > ticks ) return; lock(); end = &kill_list.last_tl; tl = kill_list.first_tl.next_tl; while( tl!=end && tl->time_out <= ticks ) { tl=tl->next_tl; } /* nothing to delete found */ if (tl->prev_tl==&kill_list.first_tl) { unlock(); return; } /* the detached list begins with current beginning */ ret = kill_list.first_tl.next_tl; /* and we mark the end of the split list */ tl->prev_tl->next_tl = 0; /* the shortened list starts from where we suspended */ kill_list.first_tl.next_tl = tl; tl->prev_tl = & kill_list.first_tl; unlock(); /* process the list now */ while (ret) { tmp_tl=ret->next_tl; ret->next_tl=ret->prev_tl=0; if (ret->time_out>0) { killr=kill(ret->pid, SIGTERM ); DBG("DEBUG: child process (%d) kill status: %d\n", ret->pid, killr ); } shm_free(ret); ret=tmp_tl; } } int schedule_to_kill( int pid ) { struct timer_link *tl; tl=shm_malloc( sizeof(struct timer_link) ); if (tl==0) { LOG(L_ERR, "ERROR: schedule_to_kill: no shmem\n"); return -1; } memset(tl, 0, sizeof(struct timer_link) ); lock(); tl->pid=pid; tl->time_out=get_ticks()+time_to_kill; tl->prev_tl=kill_list.last_tl.prev_tl; tl->next_tl=&kill_list.last_tl; kill_list.last_tl.prev_tl=tl; tl->prev_tl->next_tl=tl; unlock(); return 1; } int initialize_kill() { /* if disabled ... */ if (time_to_kill==0) return 1; if ((register_timer( timer_routine, 0 /* param */, 1 /* period */)<0)) { LOG(L_ERR, "ERROR: kill_initialize: no exec timer registered\n"); return -1; } kill_list.first_tl.next_tl=&kill_list.last_tl; kill_list.last_tl.prev_tl=&kill_list.first_tl; kill_list.first_tl.prev_tl= kill_list.last_tl.next_tl = 0; kill_list.last_tl.time_out=-1; kill_lock=lock_alloc(); if (kill_lock==0) { LOG(L_ERR, "ERROR: initialize_kill: no mem for mutex\n"); return -1; } lock_init(kill_lock); DBG("DEBUG: kill initialized\n"); return 1; } void destroy_kill() { /* if disabled ... */ if (time_to_kill==0) return; if (kill_lock){ lock_destroy(kill_lock); lock_dealloc(kill_lock); } return; } kamailio-4.0.4/obsolete/exec/exec_hf.c0000644000000000000000000003371712223032460016315 0ustar rootroot/* * * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * history * ------- * 2003-02-28 scratchpad compatibility abandoned * 2003-01-29 scratchpad removed * 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri) * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei) */ /* functions for creating environment variables out of a request's * header; known compact header field names are translated to * canonical form; multiple header field occurrences are merged * into a single variable * * known limitations: * - compact header field names unknown to parser will not be translated to * canonical form. Thus, environment variables may have either name and * users have to check for both of them. * - symbols in header field names will be translated to underscore * */ #include #include "../../comp_defs.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_to.h" #include "../../parser/parse_via.h" #include "../../parser/parse_uri.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "../../md5utils.h" #include "../../char_msg_val.h" #include "exec_hf.h" /* should be environment variables set by header fields ? */ unsigned int setvars=1; /* insert a new header field into the structure; */ static int insert_hf( struct hf_wrapper **list, struct hdr_field *hf ) { struct hf_wrapper *w; /* new wrapper */ struct hf_wrapper *i; w=(struct hf_wrapper *)pkg_malloc(sizeof(struct hf_wrapper)); if (!w) { LOG(L_ERR, "ERROR: insert_hf ran out of pkg mem\n"); return 0; } memset(w, 0, sizeof(struct hf_wrapper)); w->var_type=W_HF;w->u.hf=hf; w->prefix=HF_PREFIX; w->prefix_len=HF_PREFIX_LEN; /* is there another hf of the same type?... */ for(i=*list; i; i=i->next_other) { if (i->var_type==W_HF && i->u.hf->type==hf->type) { /* if it is OTHER, check name too */ if (hf->type==HDR_OTHER_T && (hf->name.len!=i->u.hf->name.len || strncasecmp(i->u.hf->name.s, hf->name.s, hf->name.len)!=0)) continue; /* yes, we found a hf of same type */ w->next_same=i->next_same; w->next_other=i->next_other; i->next_same=w; break; } } /* ... no previous HF of the same type found */ if (i==0) { w->next_other=*list; *list=w; } return 1; } static void release_hf_struct( struct hf_wrapper *list ) { struct hf_wrapper *i, *j, *nexts, *nexto; i=list; while(i) { nexto=i->next_other; j=i->next_same; pkg_free(i); /* release list of same type hf */ while(j) { nexts=j->next_same; pkg_free(j); j=nexts; } i=nexto; } } /* if that is some of well-known header fields which have compact * form, return canonical form ... returns 1 and sets params; * 0 is returned otherwise */ static int compacthdr_type2str(hdr_types_t type, char **hname, int *hlen ) { switch(type) { /* HDR_CONTENT_ENCODING: 'e' -- unsupported by parser */ /* HDR_SUBJECT: 's' -- unsupported by parser */ case HDR_VIA_T /* v */ : *hname=VAR_VIA; *hlen=VAR_VIA_LEN; break; case HDR_CONTENTTYPE_T /* c */ : *hname=VAR_CTYPE; *hlen=VAR_CTYPE_LEN; break; case HDR_FROM_T /* f */: *hname=VAR_FROM; *hlen=VAR_FROM_LEN; break; case HDR_CALLID_T /* i */: *hname=VAR_CALLID; *hlen=VAR_CALLID_LEN; break; case HDR_SUPPORTED_T /* k */: *hname=VAR_SUPPORTED; *hlen=VAR_SUPPORTED_LEN; break; case HDR_CONTENTLENGTH_T /* l */: *hname=VAR_CLEN; *hlen=VAR_CLEN_LEN; break; case HDR_CONTACT_T /* m */: *hname=VAR_CONTACT; *hlen=VAR_CONTACT_LEN; break; case HDR_TO_T /* t */: *hname=VAR_TO; *hlen=VAR_TO_LEN; break; case HDR_EVENT_T /* o */: *hname=VAR_EVENT; *hlen=VAR_EVENT_LEN; break; default: return 0; } return 1; } static int canonize_headername(str *orig, char **hname, int *hlen ) { char *c; int i; *hlen=orig->len; *hname=pkg_malloc(*hlen); if (!*hname) { LOG(L_ERR, "ERROR: print_vars: no mem for hname\n"); return 0; } for (c=orig->s, i=0; i<*hlen; i++, c++) { /* lowercase to uppercase */ if (*c>='a' && *c<='z') *((*hname)+i)=*c-('a'-'A'); /* uppercase and numbers stay "as is" */ else if ((*c>='A' && *c<='Z')||(*c>='0' && *c<='9')) *((*hname)+i)=*c; /* legal symbols will be translated to underscore */ else if (strchr(UNRESERVED_MARK HNV_UNRESERVED, *c) || (*c==ESCAPE)) *((*hname)+i)=HFN_SYMBOL; else { LOG(L_ERR, "ERROR: print_var unexpected char " "'%c' in hfname %.*s\n", *c, *hlen, orig->s ); *((*hname)+i)=HFN_SYMBOL; } } return 1; } static int print_av_var(struct hf_wrapper *w) { int env_len; char *env; char *c; env_len=w->u.av.attr.len+1/*assignment*/+w->u.av.val.len+1/*ZT*/; env=pkg_malloc(env_len); if (!env) { LOG(L_ERR, "ERROR: print_av_var: no malloc mem\n"); return 0; } c=env; memcpy(c, w->u.av.attr.s, w->u.av.attr.len); c+=w->u.av.attr.len; *c=EV_ASSIGN;c++; memcpy(c, w->u.av.val.s, w->u.av.val.len);c+=w->u.av.val.len; *c=0; /* zero termination */ w->envvar=env; return 1; } /* creates a malloc-ed string with environment variable; returns 1 on success, * 0 on failure */ static int print_hf_var(struct hf_wrapper *w, int offset) { char *hname; int hlen; short canonical; char *envvar; int envvar_len; struct hf_wrapper *wi; char *c; /* make -Wall happy */ hname=0;hlen=0;envvar=0; /* Make sure header names with possible compact forms * will be printed canonically */ canonical=compacthdr_type2str(w->u.hf->type, &hname, &hlen); /* header field has not been made canonical using a table; * do it now by uppercasing header-field name */ if (!canonical) { if (!canonize_headername(&w->u.hf->name, &hname, &hlen)) { LOG(L_ERR, "ERROR: print_hf_var: canonize_hn error\n"); return 0; } } /* now we have a header name, let us generate the var */ envvar_len=w->u.hf->body.len; for(wi=w->next_same; wi; wi=wi->next_same) { /* other values, separated */ envvar_len+=1 /* separator */ + wi->u.hf->body.len; } envvar=pkg_malloc(w->prefix_len+hlen+1/*assignment*/+envvar_len+1/*ZT*/); if (!envvar) { LOG(L_ERR, "ERROR: print_var: no envvar mem\n"); goto error00; } memcpy(envvar, w->prefix, w->prefix_len); c=envvar+w->prefix_len; memcpy(c, hname, hlen ); c+=hlen; *c=EV_ASSIGN;c++; memcpy(c, w->u.hf->body.s+offset, w->u.hf->body.len ); c+=w->u.hf->body.len; for (wi=w->next_same; wi; wi=wi->next_same) { *c=HF_SEPARATOR;c++; memcpy(c, wi->u.hf->body.s+offset, wi->u.hf->body.len ); c+=wi->u.hf->body.len; } *c=0; /* zero termination */ DBG("DEBUG: print_var: %s\n", envvar ); w->envvar=envvar; if (!canonical) pkg_free(hname); return 1; error00: if (!canonical) pkg_free(hname); return 0; } static int print_var(struct hf_wrapper *w, int offset) { switch(w->var_type) { case W_HF: return print_hf_var(w, offset); case W_AV: return print_av_var(w); default: LOG(L_CRIT, "BUG: print_var: unknown type: %d\n", w->var_type ); return 0; } } static void release_vars(struct hf_wrapper *list) { while(list) { if (list->envvar) { pkg_free(list->envvar); list->envvar=0; } list=list->next_other; } } /* create ordered HF structure in pkg memory */ static int build_hf_struct(struct sip_msg *msg, struct hf_wrapper **list) { struct hdr_field *h; *list=0; /* create ordered header-field structure */ for (h=msg->headers; h; h=h->next) { if (!insert_hf(list,h)) { LOG(L_ERR, "ERROR: build_hf_struct: insert_hf failed\n"); goto error00; } } return 1; error00: release_hf_struct(*list); *list=0; return 0; } /* create env vars in malloc memory */ static int create_vars(struct hf_wrapper *list, int offset) { int var_cnt; struct hf_wrapper *w; /* create variables now */ var_cnt=0; for(w=list;w;w=w->next_other) { if (!print_var(w, offset)) { LOG(L_ERR, "ERROR: build_hf_struct: create_vars failed\n"); return 0; } var_cnt++; } return var_cnt; } environment_t *replace_env(struct hf_wrapper *list) { int var_cnt; char **cp; struct hf_wrapper *w; char **new_env; int i; environment_t *backup_env; backup_env=(environment_t *)pkg_malloc(sizeof(environment_t)); if (!backup_env) { LOG(L_ERR, "ERROR: replace_env: no mem for backup env\n"); return 0; } /* count length of current env list */ var_cnt=0; for (cp=environ; *cp; cp++) var_cnt++; backup_env->old_cnt=var_cnt; /* count length of our extensions */ for(w=list;w;w=w->next_other) var_cnt++; new_env=pkg_malloc((var_cnt+1)*sizeof(char *)); if (!new_env) { LOG(L_ERR, "ERROR: replace_env: no mem\n"); return 0; } /* put all var pointers into new environment */ i=0; for (cp=environ; *cp; cp++) { /* replicate old env */ new_env[i]=*cp; i++; } for (w=list;w;w=w->next_other) { /* append new env */ new_env[i]=w->envvar; i++; } new_env[i]=0; /* zero termination */ /* install new environment */ backup_env->env=environ; environ=new_env; /* return previous environment */ return backup_env; } void unset_env(environment_t *backup_env) { char **cur_env, **cur_env0; int i; /* switch-over to backup environment */ cur_env0=cur_env=environ; environ=backup_env->env; i=0; /* release environment */ while(*cur_env) { /* leave previously existing vars alone */ if (i>=backup_env->old_cnt) { pkg_free(*cur_env); } cur_env++; i++; } pkg_free(cur_env0); pkg_free(backup_env); } static int append_var(char *name, char *value, int len, struct hf_wrapper **list) { struct hf_wrapper *w; w=(struct hf_wrapper *)pkg_malloc(sizeof(struct hf_wrapper)); if (!w) { LOG(L_ERR, "ERROR: append_var ran out of mem\n"); return 0; } memset(w, 0, sizeof(struct hf_wrapper)); w->var_type=W_AV; w->u.av.attr.s=name; w->u.av.attr.len=strlen(name); w->u.av.val.s=value; /* NULL strings considered empty, if len unknown, calculate it now */ w->u.av.val.len= value==0?0:(len==0? strlen(value) : len); w->next_other=*list; *list=w; return 1; } static int append_fixed_vars(struct sip_msg *msg, struct hf_wrapper **list) { static char tid[MD5_LEN]; str *uri; struct sip_uri parsed_uri, oparsed_uri; char *val; int val_len; /* source ip */ if (!append_var(EV_SRCIP, ip_addr2a(&msg->rcv.src_ip), 0, list)) { LOG(L_ERR, "ERROR: append_var SRCIP failed \n"); return 0; } /* request URI */ uri=msg->new_uri.s && msg->new_uri.len ? &msg->new_uri : &msg->first_line.u.request.uri; if (!append_var(EV_RURI, uri->s, uri->len, list )) { LOG(L_ERR, "ERROR: append_var URI failed\n"); return 0; } /* userpart of request URI */ if (parse_uri(uri->s, uri->len, &parsed_uri)<0) { LOG(L_WARN, "WARNING: append_var: URI not parsed\n"); } else { if (!append_var(EV_USER, parsed_uri.user.s, parsed_uri.user.len, list)) { LOG(L_ERR, "ERROR: append_var USER failed\n"); goto error; } } /* original URI */ if (!append_var(EV_ORURI, msg->first_line.u.request.uri.s, msg->first_line.u.request.uri.len, list)) { LOG(L_ERR, "ERROR: append_var O-URI failed\n"); goto error; } /* userpart of request URI */ if (parse_uri(msg->first_line.u.request.uri.s, msg->first_line.u.request.uri.len, &oparsed_uri)<0) { LOG(L_WARN, "WARNING: append_var: orig URI not parsed\n"); } else { if (!append_var(EV_OUSER, oparsed_uri.user.s, oparsed_uri.user.len, list)) { LOG(L_ERR, "ERROR: append_var OUSER failed\n"); goto error; } } /* tid, transaction id == via/branch */ if (!char_msg_val(msg, tid)) { LOG(L_WARN, "WARNING: no tid can be determined\n"); val=0; val_len=0; } else { val=tid;val_len=MD5_LEN; } if (!append_var(EV_TID, val,val_len, list)) { LOG(L_ERR, "ERROR: append_var TID failed\n"); goto error; } /* did, dialogue id == To-tag */ if (!(msg->to && get_to(msg) )) { LOG(L_ERR, "ERROR: append_var: no to-tag\n"); val=0; val_len=0; } else { val=get_to(msg)->tag_value.s; val_len=get_to(msg)->tag_value.len; } if (!append_var(EV_DID, val, val_len, list)) { LOG(L_ERR, "ERROR: append_var DID failed\n"); goto error; } return 1; error: return 0; } environment_t *set_env(struct sip_msg *msg) { struct hf_wrapper *hf_list; environment_t *backup_env; /* parse all so that we can pass all header fields to script */ if (parse_headers(msg, HDR_EOH_F, 0)==-1) { LOG(L_ERR, "ERROR: set_env: parsing failed\n"); return 0; } hf_list=0; /* create a temporary structure with ordered header fields * and create environment variables out of it */ if (!build_hf_struct(msg, &hf_list)) { LOG(L_ERR, "ERROR: set_env: build_hf_struct failed\n"); return 0; } if (!append_fixed_vars(msg, &hf_list)) { LOG(L_ERR, "ERROR: ser_env: append_fixed_vars failed\n"); goto error01; } /* create now the strings for environment variables */ if (!create_vars(hf_list, 0)) { LOG(L_ERR, "ERROR: set_env: create_vars failed\n"); goto error00; } /* install the variables in current environment */ backup_env=replace_env(hf_list); if (!backup_env) { LOG(L_ERR, "ERROR: set_env: replace_env failed\n"); goto error00; } /* release the ordered HF structure -- we only need the vars now */ release_hf_struct(hf_list); return backup_env; error00: release_vars(hf_list); /* release variables */ error01: release_hf_struct(hf_list); /* release temporary ordered HF struct */ return 0; } kamailio-4.0.4/obsolete/exec/exec_mod.c0000644000000000000000000000726012223032460016471 0ustar rootroot/* * execution module * * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-03-11: New module interface (janakj) * 2003-03-16: flags export parameter added (janakj) */ #include #include "../../parser/msg_parser.h" #include "../../str.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../parser/parse_uri.h" #include "exec.h" #include "kill.h" #include "exec_hf.h" MODULE_VERSION unsigned int time_to_kill=0; static int mod_init( void ); inline static int w_exec_dset(struct sip_msg* msg, char* cmd, char* foo); inline static int w_exec_msg(struct sip_msg* msg, char* cmd, char* foo); inline static void exec_shutdown(); /* * Exported functions */ static cmd_export_t cmds[] = { {"exec_dset", w_exec_dset, 1, fixup_var_str_1, REQUEST_ROUTE | FAILURE_ROUTE}, {"exec_msg", w_exec_msg, 1, fixup_var_str_1, REQUEST_ROUTE | FAILURE_ROUTE}, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"time_to_kill", PARAM_INT, &time_to_kill}, {"setvars", PARAM_INT, &setvars }, {0, 0, 0} }; #ifdef STATIC_EXEC struct module_exports exec_exports = { #else struct module_exports exports= { #endif "exec", cmds, /* Exported functions */ 0, /* RPC methods */ params, /* Exported parameters */ mod_init, /* initialization module */ 0, /* response function */ exec_shutdown, /* destroy function */ 0, /* oncancel function */ 0 /* per-child init function */ }; void exec_shutdown() { if (time_to_kill) destroy_kill(); } static int mod_init( void ) { fprintf( stderr, "exec - initializing\n"); if (time_to_kill) initialize_kill(); return 0; } inline static int w_exec_dset(struct sip_msg* msg, char* p1, char* foo) { str cmd; str *uri; environment_t *backup; int ret; if (get_str_fparam(&cmd, msg, (fparam_t*)p1) < 0) { ERR("Error while obtaining command name\n"); return -1; } backup=0; if (setvars) { backup=set_env(msg); if (!backup) { LOG(L_ERR, "ERROR: w_exec_msg: no env created\n"); return -1; } } if (msg->new_uri.s && msg->new_uri.len) uri=&msg->new_uri; else uri=&msg->first_line.u.request.uri; ret=exec_str(msg, &cmd, uri->s, uri->len); if (setvars) { unset_env(backup); } return ret; } inline static int w_exec_msg(struct sip_msg* msg, char* p1, char* foo) { str cmd; environment_t *backup; int ret; if (get_str_fparam(&cmd, msg, (fparam_t*)p1) < 0) { ERR("Error while obtaining command name\n"); return -1; } backup=0; if (setvars) { backup=set_env(msg); if (!backup) { LOG(L_ERR, "ERROR: w_exec_msg: no env created\n"); return -1; } } ret=exec_msg(msg, &cmd); if (setvars) { unset_env(backup); } return ret; } kamailio-4.0.4/obsolete/exec/Makefile0000644000000000000000000000034712223032460016201 0ustar rootroot# $Id$ # # exec module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=exec.so LIBS= DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/exec/exec.h0000644000000000000000000000225712223032460015640 0ustar rootroot/* * * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _EXEC_H #define _EXEC_H int exec_str(struct sip_msg *msg, str* cmd, char *param, int param_len); int exec_msg(struct sip_msg *msg, str* cmd ); #endif kamailio-4.0.4/obsolete/exec/exec_hf.h0000644000000000000000000000700412223032460016310 0ustar rootroot/* * * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _EXEC_HF_H #define _EXEC_HF_H #include "../../parser/msg_parser.h" /* prefix prepended to header field name in env var name */ #define SIP "SIP_" #define HF_PREFIX SIP "HF_" #define HF_PREFIX_LEN (sizeof(HF_PREFIX)-1) /* well known variable names */ #define EV_SRCIP SIP "SRCIP" #define EV_RURI SIP "RURI" #define EV_ORURI SIP "ORUI" #define EV_USER SIP "USER" #define EV_OUSER SIP "OUSER" #define EV_TID SIP "TID" #define EV_DID SIP "DID" /* env var assignment operator */ #define EV_ASSIGN '=' /* header field separator */ #define HF_SEPARATOR ',' /* RFC3261 -- characters legal in header names; a really * _bloated_ thing */ #define UNRESERVED_MARK "-_.!~*'()" #define HNV_UNRESERVED "[]/?:+$" #define ESCAPE '%' /* and this is what all such crazy symbols in header field * name will be replaced with in env vars */ #define HFN_SYMBOL '_' #define VAR_VIA "VIA" #define VAR_VIA_LEN (sizeof(VAR_VIA)-1) #define VAR_CTYPE "CONTENT_TYPE" #define VAR_CTYPE_LEN (sizeof(VAR_CTYPE)-1) #define VAR_FROM "FROM" #define VAR_FROM_LEN (sizeof(VAR_FROM)-1) #define VAR_CALLID "CALLID" #define VAR_CALLID_LEN (sizeof(VAR_CALLID)-1) #define VAR_SUPPORTED "SUPPORTED" #define VAR_SUPPORTED_LEN (sizeof(VAR_SUPPORTED)-1) #define VAR_CLEN "CONTENT_LENGTH" #define VAR_CLEN_LEN (sizeof(VAR_CLEN)-1) #define VAR_CONTACT "CONTACT" #define VAR_CONTACT_LEN (sizeof(VAR_CONTACT)-1) #define VAR_TO "TO" #define VAR_TO_LEN (sizeof(VAR_TO)-1) #define VAR_EVENT "EVENT" #define VAR_EVENT_LEN (sizeof(VAR_EVENT)-1) #if 0 /* _JUST_FOR_INFO_HERE */ struct hdr_field { int type; /* Header field type */ str name; /* Header field name */ str body; /* Header field body */ void* parsed; /* Parsed data structures */ struct hdr_field* next; /* Next header field in the list */ }; #endif typedef struct env { char** env; int old_cnt; } environment_t; struct attrval { str attr; str val; }; enum wrapper_type { W_HF=1, W_AV }; struct hf_wrapper { enum wrapper_type var_type; union { struct hdr_field *hf; struct attrval av; } u; /* next header field of the same type */ struct hf_wrapper *next_same; /* next header field of a different type */ struct hf_wrapper *next_other; /* env var value (zero terminated) */ char *envvar; /* variable name prefix (if any) */ char *prefix; int prefix_len; }; extern unsigned int setvars; extern char **environ; environment_t *set_env(struct sip_msg *msg); void unset_env(environment_t *backup_env); #endif kamailio-4.0.4/obsolete/uac/0000755000000000000000000000000012223032460014361 5ustar rootrootkamailio-4.0.4/obsolete/uac/doc/0000755000000000000000000000000012223032460015126 5ustar rootrootkamailio-4.0.4/obsolete/uac/doc/functions.xml0000644000000000000000000000415112223032460017661 0ustar rootroot
Functions
<function>uac_replace_from(display,uri)</function> Replace in FROM header the display name and the URI part. <function>uac_replace_from</function> usage ... uac_replace_from("batman","sip:batman@gotham.org"); uac_replace_from("","sip:robin@gotham.org"); uac_replace_from("batman",""); ...
<function>uac_replace_from(uri)</function> Replace in FROM header the URI part without altering the display name. <function>uac_replace_from</function> usage ... uac_replace_from("sip:batman@gotham.org"); ...
<function>uac_restore_from()</function> This function will check if the FROM URI was modified and will use the information stored in header parameter to restore the original FROM URI value. <function>uac_restore_from</function> usage ... uac_restore_from(); ...
<function>uac_auth()</function> This function can be called only from failure route and will build the authentication response header and insert it into the request without sending anything. <function>uac_auth</function> usage ... uac_auth(); ...
kamailio-4.0.4/obsolete/uac/doc/uac.xml0000644000000000000000000000350312223032460016421 0ustar rootroot
Ramona-Elena Modroiu Asipto
ramona@asipto.com
2005 Voice Sistem
UAC Module
Overview UAC (User Agent Client) module provides some basic UAC functionalities like FROM header manipulation (anonymization) or client authentication. Known limitations in this version: authentication does not support qop
Dependencies The following modules must be loaded before this module: TM - Transaction Module.
Installation And Running The UAC module requires additional functionality in TM module for implementing full FROM restoring. If you use from_restore_mode 1 or 2, you will need to apply the patch replace_from.patch located in the "doc" directory of UAC module.
kamailio-4.0.4/obsolete/uac/doc/Makefile0000644000000000000000000000012412223032460016563 0ustar rootrootdocs = uac.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/uac/doc/params.xml0000644000000000000000000000431312223032460017134 0ustar rootroot
Parameters
<varname>from_store_param</varname> (string) Name of FROM header parameter that will be used to store (encoded) the original FROM URI. This parameter is optional, it's default value being "vsf". Set <varname>from_store_param</varname> parameter ... modparam("uac","from_store_param","my_param") ...
<varname>from_restore_mode</varname> (integer) There are 3 mode of restoring the original FROM URI: 0 - NO RESTORE - no information about original URI is stored. 1 - AUTO RESTORE - all sequential request will be automatically updated based on stored original URI. 2 - MANUAL RESTORE - all sequential requests/replies must be manually updated based on original URI. This parameter is optional, it's default value being 0. Set <varname>from_restore_mode</varname> parameter ... modparam("uac","from_restore_mode","1") ...
<varname>credential</varname> (string) Contains a multiple definition of credentials used to perform authentication. This parameter is required if UAC authentication is used. Set <varname>credential</varname> parameter ... modparam("uac","credential","username:domain:password") ...
kamailio-4.0.4/obsolete/uac/from.c0000644000000000000000000003247512223032460015503 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 Voice Sistem SRL * * This file is part of SIP Express Router. * * UAC SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * UAC SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * 2005-01-31 first version (ramona) */ #include #include "../../parser/parse_from.h" #include "../../mem/mem.h" #include "../../data_lump.h" #include "../../modules/tm/h_table.h" #include "../../modules/tm/tm_load.h" #include "from.h" #define FL_FROM_ALTERED (1<<31) extern str from_param; extern int from_restore_mode; extern struct tm_binds uac_tmb; static char enc_table64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz0123456789+/"; static int dec_table64[256]; #define text3B64_len(_l) (( ((_l) / 3) + ((_l) % 3 ? 1 : 0) ) << 2) void init_from_replacer() { int i; for( i=0 ; i<256 ; i++) dec_table64[i] = -1; for ( i=0 ; i<64; i++) dec_table64[(unsigned char)enc_table64[i]] = i; } static inline int encode_from( str *src, str *dst ) { static char buf[text3B64_len(MAX_URI_SIZE)]; int idx; int left; int block; int i,r; char *p; dst->len = text3B64_len( src->len ); dst->s = buf; if (dst->len>text3B64_len(MAX_URI_SIZE)) { LOG(L_ERR,"ERROR:uac:encode_from: uri too long\n"); return -1; } for ( idx=0, p=buf ; idxlen ; idx+=3) { left = src->len - idx - 1; left = (left>1? 2 : left); /* Collect 1 to 3 bytes to encode */ block = 0; for ( i=0,r= 16 ; i<=left ; i++,r-=8 ) { block += ((unsigned char)src->s[idx+i]) << r; } /* Encode into 2-4 chars appending '=' if not enough data left.*/ *(p++) = enc_table64[(block >> 18) & 0x3f]; *(p++) = enc_table64[(block >> 12) & 0x3f]; *(p++) = left > 0 ? enc_table64[(block >> 6) & 0x3f] : '-'; *(p++) = left > 1 ? enc_table64[block & 0x3f] : '-'; } return 0; } static inline int decode_from( str *src , str *dst) { static char buf[MAX_URI_SIZE]; int block; int n; int idx; int end; int i,j; signed char c; /* Count '-' at end and disregard them */ for( n=0,i=src->len-1; src->s[i]=='-'; i--) n++; dst->len = ((src->len * 6) >> 3) - n; dst->s = buf; if (dst->len>MAX_URI_SIZE) { LOG(L_ERR,"ERROR:uac:decode_from: uri too long\n"); return -1; } end = src->len - n; for ( i=0,idx=0 ; is[i++]]; if ( c<0 ) { LOG(L_ERR,"ERROR:uac:decode_from: invalid base64 string " "\"%.*s\"\n",src->len,src->s); return -1; } block += c << (18 - 6*j); } /* Add the bytes */ for ( j=0,n=16 ; j<3 && idx+j< dst->len; j++,n-=8 ) buf[idx+j] = (char) ((block >> n) & 0xff); } return 0; } /* * if display name does not exist, then from_dsp is ignored */ int replace_from( struct sip_msg *msg, str *from_dsp, str *from_uri) { struct to_body *from; struct lump* l; str replace; char *p; str param; int offset; /* parse original from hdr */ if (parse_from_header(msg)!=0 ) { LOG(L_ERR,"ERROR:uac:replace_from: failed to find/parse FROM hdr\n"); goto error; } from = (struct to_body*)msg->from->parsed; /* some validity checks */ if (from->param_lst==0) { LOG(L_ERR,"ERROR:uac:replace_from: broken FROM hdr; no tag param\n"); goto error; } /* first deal with display name */ if (from_dsp && from->display.len) { /* must be replaced/ removed */ l = 0; /* there is already a display -> remove it */ DBG("DEBUG:uac:replace_from: removing display [%.*s]\n", from->display.len,from->display.s); /* build del lump */ l = del_lump( msg, from->display.s-msg->buf, from->display.len, 0); if (l==0) { LOG(L_ERR,"ERROR:uac:replace_from: display del lump failed\n"); goto error; } /* some new display to set? */ if (from_dsp->s) { if (l==0) { /* add anchor just before uri's "<" */ offset = from->uri.s - msg->buf; while( msg->buf[offset]!='<') { offset--; if (from->body.s>msg->buf+offset) { LOG(L_ERR,"ERROR:uac:replace_from: no <> and there" " is dispaly name\n"); goto error; } } if ( (l=anchor_lump( msg, offset, 0, 0))==0) { LOG(L_ERR,"ERROR:uac:replace_from: display anchor lump " "failed\n"); goto error; } } p = pkg_malloc( from_dsp->len); if (p==0) { LOG(L_ERR,"ERROR:uac:replace_from: no more pkg mem\n"); goto error; } memcpy( p, from_dsp->s, from_dsp->len); if (insert_new_lump_after( l, p, from_dsp->len, 0)==0) { LOG(L_ERR,"ERROR:uac:replace_from: insert new " "display lump failed\n"); pkg_free(p); goto error; } } } /* now handle the URI */ DBG("DEBUG:uac:replace_from: uri to replace [%.*s]\n", from->uri.len, from->uri.s); DBG("DEBUG:uac:replace_from: replacement uri is [%.*s]\n", from_uri->len, from_uri->s); /* build del/add lumps */ if ((l=del_lump( msg, from->uri.s-msg->buf, from->uri.len, 0))==0) { LOG(L_ERR,"ERROR:uac:replace_from: del lump failed\n"); goto error; } p = pkg_malloc( from_uri->len); if (p==0) { LOG(L_ERR,"ERROR:uac:replace_from: no more pkg mem\n"); goto error; } memcpy( p, from_uri->s, from_uri->len); if (insert_new_lump_after( l, p, from_uri->len, 0)==0) { LOG(L_ERR,"ERROR:uac:replace_from: insert new lump failed\n"); pkg_free(p); goto error; } if (from_restore_mode==FROM_NO_RESTORE) return 0; /*add parameter lump */ if (encode_from( &from->uri , &replace)<0 ) { LOG(L_ERR,"ERROR:uac:replace_from: failed to encode uri\n"); goto error; } DBG("encode is=<%.*s> len=%d\n",replace.len,replace.s,replace.len); offset = from->last_param->value.s+from->last_param->value.len-msg->buf; if ( (l=anchor_lump( msg, offset, 0, 0))==0) { LOG(L_ERR,"ERROR:uac:replace_from: anchor lump failed\n"); goto error; } param.len = 1+from_param.len+1+replace.len; param.s = (char*)pkg_malloc(param.len); if (param.s==0) { LOG(L_ERR,"ERROR:uac:replace_from: no more pkg mem\n"); goto error; } p = param.s; *(p++) = ';'; memcpy( p, from_param.s, from_param.len); p += from_param.len; *(p++) = '='; memcpy( p, replace.s, replace.len); p += replace.len; if (insert_new_lump_after( l, param.s, param.len, 0)==0) { LOG(L_ERR,"ERROR:uac:replace_from: insert new lump failed\n"); pkg_free(param.s); goto error; } msg->msg_flags |= FL_FROM_ALTERED; return 0; error: return -1; } /* * return 0 - replaced * -1 - not replaced or error */ int restore_from( struct sip_msg *msg, int is_req) { struct to_body *ft_hdr; struct to_param *param; struct lump* l; str replace; str restore; str del; char *p; /* for replies check the from, for requests check to! */ if (!is_req) { /* parse original from hdr */ if (parse_from_header(msg)!=0 ) { LOG(L_ERR,"ERROR:uac:restore_from: failed to find/parse " "FROM hdr\n"); goto failed; } ft_hdr = (struct to_body*)msg->from->parsed; } else { if ( !msg->to && (parse_headers(msg,HDR_TO_F,0)==-1 || !msg->to)) { LOG(L_ERR,"ERROR:uac:restore_from: bad msg or missing TO hdr\n"); goto failed; } ft_hdr = (struct to_body*)msg->to->parsed; } /* check if it has the param */ for( param=ft_hdr->param_lst ; param ; param=param->next ) if (param->name.len==from_param.len && strncmp(param->name.s, from_param.s, from_param.len)==0) break; if (param==0) goto failed; /* determin what to replace */ replace.s = ft_hdr->uri.s; replace.len = ft_hdr->uri.len; DBG("DEBUG:uac:restore_from: replacing [%.*s]\n", replace.len, replace.s); /* build del/add lumps */ if ((l=del_lump( msg, replace.s-msg->buf, replace.len, 0))==0) { LOG(L_ERR,"ERROR:uac:restore_from: del lump failed\n"); goto failed; } /* calculate the restore from */ if (decode_from( ¶m->value, &restore)<0 ) { LOG(L_ERR,"ERROR:uac:restore_from: failed to dencode uri\n"); goto failed; } DBG("DEBUG:uac:restore_from: replacement is [%.*s]\n", replace.len, replace.s); p = pkg_malloc( restore.len); if (p==0) { LOG(L_ERR,"ERROR:uac:restore_from: no more pkg mem\n"); goto failed; } memcpy( p, restore.s, restore.len); if (insert_new_lump_after( l, p, restore.len, 0)==0) { LOG(L_ERR,"ERROR:uac:restore_from: insert new lump failed\n"); pkg_free(p); goto failed; } /* delete parameter */ del.s = param->name.s; while ( *del.s!=';') del.s--; del.len = (int)(long)(param->value.s + param->value.len - del.s); DBG("DEBUG:uac:restore_from: deleting [%.*s]\n",del.len,del.s); if ((l=del_lump( msg, del.s-msg->buf, del.len, 0))==0) { LOG(L_ERR,"ERROR:uac:restore_from: del lump failed\n"); goto failed; } return 0; failed: return -1; } /************************** TMCB functions ******************************/ static int rst_from = 1; static int rst_to = 2; void correct_reply(struct cell* t, int type, struct tmcb_params *p); void tr_checker(struct cell* t, int type, struct tmcb_params *param) { DBG("---------------------- inside tr_checker\n"); if ( t && param->req ) { DBG("*************** marker **************\n"); /* is the request marked with FROM altered flag? */ if (param->req->msg_flags&FL_FROM_ALTERED) { /* need to put back in replies the FROM from UAS */ /* in callback we need FROM to be parsed- it's already done * by replace_from() function */ DBG("*************** marker **************\n"); if ( uac_tmb.register_tmcb( 0, t, TMCB_RESPONSE_IN, correct_reply, (void*)&rst_from, 0)!=1 ) { LOG(L_ERR,"ERROR:uac:tr_checker: failed to install " "TM callback\n"); return; } } else { /* check if the request contains the restore_from tag */ if ( restore_from( param->req , 1)==0 ) { /* in callback we need TO to be parsed- it's already done * by restore_from() function */ /* restore in req performed -> replace in reply */ if ( uac_tmb.register_tmcb( 0, t, TMCB_RESPONSE_IN, correct_reply, (void*)&rst_to, 0)!=1 ) { LOG(L_ERR,"ERROR:uac:tr_checker: failed to install " "TM callback\n"); return; } } } } } /* take the original FROM URI from UAS and put it in reply */ void correct_reply(struct cell* t, int type, struct tmcb_params *p) { struct lump* l; struct to_param *param; struct sip_msg *req; str src; str dst; str dsrc; str del; DBG("---------------------- inside correct_reply\n"); if ( !t || !t->uas.request || !p->rpl ) return; req = t->uas.request; if ( (**(int**)p->param)==rst_from ) { /* copy FROM uri : req -> rpl */ if ( !req->from || !req->from->parsed) { LOG(L_CRIT,"BUG:uac:correct_reply: FROM is not already parsed\n"); return; } /* parse FROM in reply */ if (parse_from_header( p->rpl )!=0 ) { LOG(L_ERR,"ERROR:uac:correct_reply: failed to find/parse " "FROM hdr\n"); return; } /* remove the parameter */ param=((struct to_body*)p->rpl->from->parsed)->param_lst; for( ; param ; param=param->next ) { DBG("***** param=<%.*s>=<%.*s>,%p\n",param->name.len,param->name.s, param->value.len,param->value.s,param->next); if (param->name.len==from_param.len && strncmp(param->name.s, from_param.s, from_param.len)==0) { del.s = param->name.s; while ( *del.s!=';') del.s--; del.len = (int)(long)(param->value.s+param->value.len-del.s); DBG("DEBUG:uac:correct_reply: deleting [%.*s]\n", del.len,del.s); if ((l=del_lump( p->rpl, del.s-p->rpl->buf, del.len, 0))==0) LOG(L_ERR,"ERROR:uac:correct_reply: del lump failed\n"); break; } } src = ((struct to_body*)req->from->parsed)->uri; dst = ((struct to_body*)p->rpl->from->parsed)->uri; } else { /* copy TO uri : req -> rpl */ if ( !req->to || !req->to->parsed) { LOG(L_CRIT,"BUG:uac:correct_reply: TO is not already parsed\n"); return; } /* parse TO in reply */ if (!p->rpl->to && (parse_headers(p->rpl,HDR_TO_F,0)==-1||!p->rpl->to)) { LOG(L_ERR,"ERROR:uac:correct_reply: failed to find/parse " "TO hdr\n"); return; } src = ((struct to_body*)req->to->parsed)->uri; dst = ((struct to_body*)p->rpl->to->parsed)->uri; } DBG("DEBUG:correct_reply: replacing <%.*s> with <%.*s>\n", dst.len,dst.s, src.len,src.s); /* duplicate the src data */ dsrc.len = src.len; dsrc.s = (char*)pkg_malloc(dsrc.len); if (dsrc.s==0) { LOG(L_ERR,"ERROR:uac:correct_reply: no more pkg mem\n"); return; } memcpy( dsrc.s, src.s, src.len); /* build del/add lumps */ if ((l=del_lump( p->rpl, dst.s-p->rpl->buf, dst.len, 0))==0) { LOG(L_ERR,"ERROR:uac:correct_reply: del lump failed\n"); return; } if (insert_new_lump_after( l, dsrc.s, dsrc.len, 0)==0) { LOG(L_ERR,"ERROR:uac:correct_reply: insert new lump failed\n"); pkg_free( dsrc.s ); return; } return; } kamailio-4.0.4/obsolete/uac/auth_alg.c0000644000000000000000000000663412223032460016322 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 Voice Sistem SRL * * This file is part of SIP Express Router. * * UAC SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * UAC SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * 2005-01-31 first version (ramona) */ #include "../../md5.h" #include "auth_alg.h" static inline void cvt_hex(HASH bin, HASHHEX hex) { unsigned short i; unsigned char j; for (i = 0; i> 4) & 0xf; if (j <= 9) { hex[i * 2] = (j + '0'); } else { hex[i * 2] = (j + 'a' - 10); } j = bin[i] & 0xf; if (j <= 9) { hex[i * 2 + 1] = (j + '0'); } else { hex[i * 2 + 1] = (j + 'a' - 10); } }; hex[HASHHEXLEN] = '\0'; } /* * calculate H(A1) */ void uac_calc_HA1( struct uac_credential *crd, struct authenticate_body *auth, str* cnonce, HASHHEX sess_key) { MD5_CTX Md5Ctx; HASH HA1; MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, crd->user.s, crd->user.len); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, crd->realm.s, crd->realm.len); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, crd->passwd.s, crd->passwd.len); MD5Final(HA1, &Md5Ctx); if ( auth->flags& AUTHENTICATE_MD5SESS ) { MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, HA1, HASHLEN); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, auth->nonce.s, auth->nonce.len); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, cnonce->s, cnonce->len); MD5Final(HA1, &Md5Ctx); }; cvt_hex(HA1, sess_key); } /* * calculate H(A2) */ void uac_calc_HA2( str *method, str *uri, struct authenticate_body *auth, HASHHEX hentity, HASHHEX HA2Hex ) { MD5_CTX Md5Ctx; HASH HA2; MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, method->s, method->len); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, uri->s, uri->len); if ( auth->flags&QOP_AUTH_INT) { MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, hentity, HASHHEXLEN); }; MD5Final(HA2, &Md5Ctx); cvt_hex(HA2, HA2Hex); } /* * calculate request-digest/response-digest as per HTTP Digest spec */ void uac_calc_response( HASHHEX ha1, HASHHEX ha2, struct authenticate_body *auth, str* nc, str* cnonce, HASHHEX response) { MD5_CTX Md5Ctx; HASH RespHash; MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, ha1, HASHHEXLEN); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, auth->nonce.s, auth->nonce.len); MD5Update(&Md5Ctx, ":", 1); if ( auth->qop.len) { MD5Update(&Md5Ctx, nc->s, nc->len); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, cnonce->s, cnonce->len); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, auth->qop.s, auth->qop.len); MD5Update(&Md5Ctx, ":", 1); }; MD5Update(&Md5Ctx, ha2, HASHHEXLEN); MD5Final(RespHash, &Md5Ctx); cvt_hex(RespHash, response); } kamailio-4.0.4/obsolete/uac/README0000644000000000000000000000632312223032460015245 0ustar rootroot1. UAC Module Ramona-Elena Modroiu Asipto Copyright © 2005 Voice Sistem __________________________________________________________________ 1.1. Overview 1.2. Dependencies 1.3. Installation And Running 1.4. Parameters 1.4.1. from_store_param (string) 1.4.2. from_restore_mode (integer) 1.4.3. credential (string) 1.5. Functions 1.5.1. uac_replace_from(display,uri) 1.5.2. uac_replace_from(uri) 1.5.3. uac_restore_from() 1.5.4. uac_auth() 1.1. Overview UAC (User Agent Client) module provides some basic UAC functionalities like FROM header manipulation (anonymization) or client authentication. Known limitations in this version: * authentication does not support qop 1.2. Dependencies The following modules must be loaded before this module: * TM - Transaction Module. 1.3. Installation And Running The UAC module requires additional functionality in TM module for implementing full FROM restoring. If you use from_restore_mode 1 or 2, you will need to apply the patch replace_from.patch located in the "doc" directory of UAC module. 1.4. Parameters 1.4.1. from_store_param (string) Name of FROM header parameter that will be used to store (encoded) the original FROM URI. This parameter is optional, it's default value being "vsf". Example 1. Set from_store_param parameter ... modparam("uac","from_store_param","my_param") ... 1.4.2. from_restore_mode (integer) There are 3 mode of restoring the original FROM URI: * 0 - NO RESTORE - no information about original URI is stored. * 1 - AUTO RESTORE - all sequential request will be automatically updated based on stored original URI. * 2 - MANUAL RESTORE - all sequential requests/replies must be manually updated based on original URI. This parameter is optional, it's default value being 0. Example 2. Set from_restore_mode parameter ... modparam("uac","from_restore_mode","1") ... 1.4.3. credential (string) Contains a multiple definition of credentials used to perform authentication. This parameter is required if UAC authentication is used. Example 3. Set credential parameter ... modparam("uac","credential","username:domain:password") ... 1.5. Functions 1.5.1. uac_replace_from(display,uri) Replace in FROM header the display name and the URI part. Example 4. uac_replace_from usage ... uac_replace_from("batman","sip:batman@gotham.org"); uac_replace_from("","sip:robin@gotham.org"); uac_replace_from("batman",""); ... 1.5.2. uac_replace_from(uri) Replace in FROM header the URI part without altering the display name. Example 5. uac_replace_from usage ... uac_replace_from("sip:batman@gotham.org"); ... 1.5.3. uac_restore_from() This function will check if the FROM URI was modified and will use the information stored in header parameter to restore the original FROM URI value. Example 6. uac_restore_from usage ... uac_restore_from(); ... 1.5.4. uac_auth() This function can be called only from failure route and will build the authentication response header and insert it into the request without sending anything. Example 7. uac_auth usage ... uac_auth(); ... kamailio-4.0.4/obsolete/uac/auth.c0000644000000000000000000002300512223032460015466 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 Voice Sistem SRL * * This file is part of SIP Express Router. * * UAC SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * UAC SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * 2005-01-31 first version (ramona) */ #include #include #include "../../str.h" #include "../../dprint.h" #include "../../data_lump.h" #include "../../mem/mem.h" #include "../../dset.h" #include "../../modules/tm/tm_load.h" #include "auth.h" #include "auth_alg.h" #include "auth_hdr.h" extern struct tm_binds uac_tmb; static struct uac_credential *crd_list = 0; #define duplicate_str(_strd, _strs, _error) \ do { \ _strd.s = (char*)pkg_malloc(_strs.len); \ if (_strd.s==0) \ { \ LOG(L_ERR,"ERROR:uac:add_credential: " \ "no more pkg memory\n");\ goto _error; \ } \ memcpy( _strd.s, _strs.s, _strs.len); \ _strd.len = _strs.len; \ }while(0) #define WWW_AUTH_CODE 401 #define WWW_AUTH_HDR "WWW-Authenticate" #define WWW_AUTH_HDR_LEN (sizeof(WWW_AUTH_HDR)-1) #define PROXY_AUTH_CODE 407 #define PROXY_AUTH_HDR "Proxy-Authenticate" #define PROXY_AUTH_HDR_LEN (sizeof(PROXY_AUTH_HDR)-1) int has_credentials() { return (crd_list!=0)?1:0; } void free_credential(struct uac_credential *crd) { if (crd) { if (crd->realm.s) pkg_free(crd->realm.s); if (crd->user.s) pkg_free(crd->user.s); if (crd->passwd.s) pkg_free(crd->passwd.s); pkg_free(crd); } } int add_credential( unsigned int type, void *val) { struct uac_credential *crd; char *p; str foo; p = (char*)val; crd = 0; if (p==0 || *p==0) goto error; crd = (struct uac_credential*)pkg_malloc(sizeof(struct uac_credential)); if (crd==0) { LOG(L_ERR,"ERROR:uac:add_credential: no more pkg mem\n"); goto error; } memset( crd, 0, sizeof(struct uac_credential)); /*parse the user */ while (*p && isspace((int)*p)) p++; foo.s = p; while (*p && *p!=':' && !isspace((int)*p)) p++; if (foo.s==p || *p==0) /* missing or empty user */ goto parse_error; foo.len = p - foo.s; /* dulicate it */ duplicate_str( crd->user, foo, error); /* parse the ':' separator */ while (*p && isspace((int)*p)) p++; if (*p!=':') goto parse_error; p++; while (*p && isspace((int)*p)) p++; if (*p==0) goto parse_error; /*parse the realm */ while (*p && isspace((int)*p)) p++; foo.s = p; while (*p && *p!=':' && !isspace((int)*p)) p++; if (foo.s==p || *p==0) /* missing or empty realm */ goto parse_error; foo.len = p - foo.s; /* dulicate it */ duplicate_str( crd->realm, foo, error); /* parse the ':' separator */ while (*p && isspace((int)*p)) p++; if (*p!=':') goto parse_error; p++; while (*p && isspace((int)*p)) p++; if (*p==0) goto parse_error; /*parse the passwd */ while (*p && isspace((int)*p)) p++; foo.s = p; while (*p && !isspace((int)*p)) p++; if (foo.s==p) /* missing or empty passwd */ goto parse_error; foo.len = p - foo.s; /* dulicate it */ duplicate_str( crd->passwd, foo, error); /* end of string */ while (*p && isspace((int)*p)) p++; if (*p!=0) goto parse_error; /* link the new cred struct */ crd->next = crd_list; crd_list = crd; pkg_free(val); return 0; parse_error: LOG(L_ERR,"ERROR:uac:add_credential: parse error in <%s> " "around %ld\n", (char*)val, (long)(p-(char*)val)); error: if (crd) free_credential(crd); return -1; } void destroy_credentials() { struct uac_credential *foo; while (crd_list) { foo = crd_list; crd_list = crd_list->next; free_credential(foo); } crd_list = 0; } static inline struct hdr_field *get_autenticate_hdr(struct sip_msg *rpl, int rpl_code) { struct hdr_field *hdr; str hdr_name; /* what hdr should we look for */ if (rpl_code==WWW_AUTH_CODE) { hdr_name.s = WWW_AUTH_HDR; hdr_name.len = WWW_AUTH_HDR_LEN; } else if (rpl_code==PROXY_AUTH_CODE) { hdr_name.s = PROXY_AUTH_HDR; hdr_name.len = PROXY_AUTH_HDR_LEN; } else { LOG( L_ERR,"ERROR:uac:get_autenticate_hdr: reply is not an " "auth request\n"); goto error; } DBG("DEBUG:uac:get_autenticate_hdr: looking for header \"%.*s\"\n", hdr_name.len, hdr_name.s); /* search the auth hdr, but first parse them all */ if (parse_headers( rpl, HDR_EOH_F, 0)<0) { LOG( L_ERR,"ERROR:uac:get_autenticate_hdr: failed to parse reply\n"); goto error; } for( hdr=rpl->headers ; hdr ; hdr=hdr->next ) { if ( hdr->type!=HDR_OTHER_T ) continue; if (hdr->name.len==hdr_name.len && strncasecmp(hdr->name.s,hdr_name.s, hdr_name.len)==0 ) return hdr; } LOG( L_ERR,"ERROR:uac:get_autenticate_hdr: reply has no " "auth hdr (%.*s)\n", hdr_name.len, hdr_name.s); error: return 0; } static inline struct uac_credential *lookup_realm( str *realm) { struct uac_credential *crd; for( crd=crd_list ; crd ; crd=crd->next ) if (realm->len==crd->realm.len && strncmp( realm->s, crd->realm.s, realm->len)==0 ) return crd; return 0; } static inline void do_uac_auth(struct sip_msg *req, str *uri, struct uac_credential *crd, struct authenticate_body *auth, HASHHEX response) { HASHHEX ha1; HASHHEX ha2; /* do authentication */ uac_calc_HA1( crd, auth, 0/*cnonce*/, ha1); uac_calc_HA2( &req->first_line.u.request.method, uri, auth, 0/*hentity*/, ha2 ); uac_calc_response( ha1, ha2, auth, 0/*nc*/, 0/*cnonce*/, response); } static inline int apply_urihdr_changes( struct sip_msg *req, str *uri, str *hdr) { struct lump* anchor; /* add the uri */ if (req->new_uri.s) { pkg_free(req->new_uri.s); req->new_uri.len=0; } req->parsed_uri_ok=0; req->new_uri.s = (char*)pkg_malloc(uri->len+1); if (req->new_uri.s==0) { LOG(L_ERR,"ERROR:uac:apply_urihdr_changes: no more pkg\n"); goto error; } memcpy( req->new_uri.s, uri->s, uri->len); req->new_uri.s[uri->len]=0; req->new_uri.len=uri->len; ruri_mark_new(); /* add the header */ if (parse_headers(req, HDR_EOH_F, 0) == -1) { LOG(L_ERR,"ERROR:uac:apply_urihdr_changes: failed to parse message\n"); goto error; } anchor = anchor_lump(req, req->unparsed - req->buf, 0, 0); if (anchor==0) { LOG(L_ERR,"ERROR:uac:apply_urihdr_changes: failed to get anchor\n"); goto error; } if (insert_new_lump_before(anchor, hdr->s, hdr->len, 0) == 0) { LOG(L_ERR,"ERROR:uac:apply_urihdr_changes: faield to insert lump\n"); goto error; } return 0; error: pkg_free( hdr->s ); return -1; } int uac_auth( struct sip_msg *msg) { static struct authenticate_body auth; struct uac_credential *crd; int picked_code, picked_br, b; struct sip_msg *rpl; struct cell *t; struct hdr_field *hdr; HASHHEX response; str *new_hdr; /* get transaction */ t = uac_tmb.t_gett(); if (t==T_UNDEFINED || t==T_NULL_CELL) { LOG(L_CRIT,"BUG:uac:uac_auth: no current transaction found\n"); goto error; } /* pick the selected reply */ picked_br = -1; picked_code = 999; for ( b=0; bnr_of_outgoings ; b++ ) { /* skip 'empty branches' */ if (!t->uac[b].request.buffer) continue; /* there is still an unfinished UAC transaction? */ if ( t->uac[b].last_received<200 ) { LOG(L_CRIT,"BUG:uac:uac_auth: incomplet transaction in failure " "route\n"); goto error; } if ( t->uac[b].last_receiveduac[b].last_received; } } if (picked_br<0) { LOG(L_CRIT,"BUG:uac:uac_auth: empty transaction in failure " "route\n"); goto error; } rpl = t->uac[picked_br].reply; DBG("DEBUG:uac:uac_auth: picked reply is %p, code %d\n",rpl,picked_code); if (rpl==0) { LOG(L_CRIT,"BUG:uac:uac_auth: empty reply on picked branch\n"); goto error; } if (rpl==FAKED_REPLY) { LOG(L_ERR,"ERROR:uac:uac_auth: cannot process a FAKED reply\n"); goto error; } hdr = get_autenticate_hdr( rpl, picked_code); if (hdr==0) { LOG( L_ERR,"ERROR:uac:uac_auth: failed to extract authenticate hdr\n"); goto error; } DBG("DEBUG:uac:uac_auth: header found; body=<%.*s>\n", hdr->body.len, hdr->body.s); if (parse_authenticate_body( &hdr->body, &auth)<0) { LOG(L_ERR,"ERROR:uac:uac_auth: failed to parse auth hdr body\n"); goto error; } /* can we authenticate this realm? */ crd = lookup_realm( &auth.realm ); if (crd==0) { LOG(L_ERR,"ERROR:uac:uac_auth: no credential for realm \"%.*s\"\n", auth.realm.len, auth.realm.s); goto error; } /* do authentication */ do_uac_auth( msg, &t->uac[picked_br].uri, crd, &auth, response); /* build the authorization header */ new_hdr = build_authorization_hdr( picked_code, &t->uac[picked_br].uri, crd, &auth, response); if (new_hdr==0) { LOG(L_ERR,"ERROR:uac:uac_auth: failed to build authorization hdr\n"); goto error; } /* so far, so good -> add the header and set the proper RURI */ if ( apply_urihdr_changes( msg, &t->uac[picked_br].uri, new_hdr)<0 ) { LOG(L_ERR,"ERROR:uac:uac_auth: failed to apply changes\n"); goto error; } return 0; error: return -1; } kamailio-4.0.4/obsolete/uac/auth_alg.h0000644000000000000000000000316112223032460016317 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 Voice Sistem SRL * * This file is part of SIP Express Router. * * UAC SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * UAC SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * 2005-01-31 first version (ramona) */ #ifndef _UAC_AUTH_ALG_H_ #define _UAC_AUTH_ALG_H_ #include "../../str.h" #include "auth_hdr.h" #include "auth.h" #define HASHLEN 16 typedef char HASH[HASHLEN]; #define HASHHEXLEN 32 typedef char HASHHEX[HASHHEXLEN+1]; void uac_calc_HA1( struct uac_credential *crd, struct authenticate_body *auth, str* cnonce, HASHHEX sess_key); void uac_calc_HA2( str *method, str *uri, struct authenticate_body *auth, HASHHEX hentity, HASHHEX HA2Hex ); void uac_calc_response( HASHHEX ha1, HASHHEX ha2, struct authenticate_body *auth, str* nc, str* cnonce, HASHHEX response); #endif kamailio-4.0.4/obsolete/uac/uac.c0000644000000000000000000001414112223032460015276 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 Voice Sistem SRL * * This file is part of SIP Express Router. * * UAC SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * UAC SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * 2005-01-31 first version (ramona) */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../modules/tm/tm_load.h" #include "../../modules/tm/t_hooks.h" #include "from.h" #include "auth.h" MODULE_VERSION /* global param variables */ static char *from_param_chr = "vsf"; str from_param; int from_restore_mode = FROM_NO_RESTORE; struct tm_binds uac_tmb; static int w_replace_from1(struct sip_msg* msg, char* str, char* str2); static int w_replace_from2(struct sip_msg* msg, char* str, char* str2); static int w_restore_from(struct sip_msg* msg, char* foo, char* bar); static int w_uac_auth(struct sip_msg* msg, char* str, char* str2); static int fixup_replace_from1(void** param, int param_no); static int fixup_replace_from2(void** param, int param_no); static int mod_init(void); static void mod_destroy(); /* Exported functions */ static cmd_export_t cmds[]={ {"uac_replace_from", w_replace_from2, 2, fixup_replace_from2, REQUEST_ROUTE|FAILURE_ROUTE }, {"uac_replace_from", w_replace_from1, 1, fixup_replace_from1, REQUEST_ROUTE|FAILURE_ROUTE }, {"uac_restore_from", w_restore_from, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE }, {"uac_auth", w_uac_auth, 0, 0, FAILURE_ROUTE}, {0,0,0,0,0} }; /* Exported parameters */ static param_export_t params[] = { {"from_store_param", PARAM_STRING, &from_param_chr }, {"from_restore_mode", PARAM_INT, &from_restore_mode }, {"credential", PARAM_STRING|PARAM_USE_FUNC, &add_credential }, {0, 0, 0} }; struct module_exports exports= { "uac", cmds, /* exported functions */ 0, /* RPC methods */ params, /* param exports */ mod_init, /* module initialization function */ (response_function) 0, mod_destroy, 0, 0 /* per-child init function */ }; static int mod_init(void) { load_tm_f load_tm; LOG(L_INFO,"UAC - initializing\n"); from_param.s = from_param_chr; from_param.len = strlen(from_param_chr); if (from_param.len==0) { LOG(L_ERR,"ERROR:uac:mod_init: from_tag cannot be empty\n"); goto error; } if (from_restore_mode!=FROM_NO_RESTORE && from_restore_mode!=FROM_AUTO_RESTORE && from_restore_mode!=FROM_MANUAL_RESTORE ) { LOG(L_ERR,"ERROR:uac:mod_init: invalid (%d) restore_from mode\n", from_restore_mode); } /* load the tm functions */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) { LOG(L_ERR, "ERROR:uac:mod_init: cannot import load_tm\n"); goto error; } /* let the auto-loading function load all TM stuff */ if (load_tm( &uac_tmb )==-1) goto error; if (from_restore_mode==FROM_AUTO_RESTORE) { /* get all transactions */ if (uac_tmb.register_tmcb( 0, 0, TMCB_REQUEST_IN, tr_checker, 0, 0)!=1) { LOG(L_ERR,"ERROR:uac:mod_init: failed to install TM callback\n"); goto error; } } init_from_replacer(); return 0; error: return -1; } static void mod_destroy() { destroy_credentials(); } /************************** fixup functions ******************************/ static int fixup_replace_from1(void** param, int param_no) { str *s; /* convert to str */ s = (str*)pkg_malloc( sizeof(str) ); if (s==0) { LOG(L_CRIT,"ERROR:uac:fixup_replace_from1: no more pkg mem\n"); return E_OUT_OF_MEM; } s->s = (char*)*param; s->len = strlen(s->s); if (s->len==0) { LOG(L_CRIT,"ERROR:uac:fixup_replace_from1: empty parameter " "not accepted\n"); return E_UNSPEC; } *param=(void*)s; return 0; } static int fixup_replace_from2(void** param, int param_no) { char *p; str *s; /* convert to str */ s = (str*)pkg_malloc( sizeof(str) ); if (s==0) { LOG(L_CRIT,"ERROR:uac:fixup_replace_from2: no more pkg mem\n"); return E_OUT_OF_MEM; } s->s = (char*)*param; s->len = strlen(s->s); if (s->len==0) { pkg_free(s->s); s->s = 0; } if (param_no==1) { if (s->len) { /* put " to display name */ p = (char*)pkg_malloc( s->len+2 ); if (p==0) { LOG(L_CRIT,"ERROR:uac:fixup_replace_from2: no more pkg mem\n"); return E_OUT_OF_MEM; } p[0] = '\"'; memcpy( p+1, s->s, s->len); p[s->len+1] = '\"'; pkg_free(s->s); s->s = p; s->len += 2; } } else if (param_no==2) { /* do not allow both params empty */ if (s->s==0 && ((str*)(*(param-1)))->s==0 ) { LOG(L_CRIT,"ERROR:uac:fixup_replace_from2: both parameter " "are empty\n"); return E_UNSPEC; } } *param=(void*)s; return 0; } /************************** wrapper functions ******************************/ static int w_restore_from(struct sip_msg *msg, char* foo, char* bar) { restore_from( msg , (msg->first_line.type==SIP_REQUEST)?1:0 ); return 1; } static int w_replace_from1(struct sip_msg* msg, char* uri, char* str2) { return (replace_from( msg, 0, (str*)uri)==0)?1:-1; } static int w_replace_from2(struct sip_msg* msg, char* dsp, char* uri) { return (replace_from( msg, (str*)dsp, (str*)uri)==0)?1:-1; } static int w_uac_auth(struct sip_msg* msg, char* str, char* str2) { return (uac_auth(msg)==0)?1:-1; } kamailio-4.0.4/obsolete/uac/auth_hdr.c0000644000000000000000000002317212223032460016330 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 Voice Sistem SRL * * This file is part of SIP Express Router. * * UAC SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * UAC SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * 2005-01-31 first version (ramona) */ #include "string.h" #include "ctype.h" #include "../../dprint.h" #include "../../str.h" #include "../../mem/mem.h" #include "auth_hdr.h" #include "auth.h" #define AUTHENTICATE_MD5 (1<<0) #define AUTHENTICATE_MD5SESS (1<<1) #define AUTHENTICATE_STALE (1<<2) #define AUTHENTICATE_DIGEST_S "Digest" #define AUTHENTICATE_DIGEST_LEN (sizeof(AUTHENTICATE_DIGEST_S)-1) #define LOWER1B(_n) \ ((_n)|0x20) #define LOWER4B(_n) \ ((_n)|0x20202020) #define GET4B(_p) \ ((*(p)<<24) + (*(p+1)<<16) + (*(p+2)<<8) + *(p+3)) #define GET3B(_p) \ ((*(p)<<24) + (*(p+1)<<16) + (*(p+2)<<8) + 0xff) #define CASE_5B(_hex4,_c5, _new_state) \ case _hex4: \ if (p+5s==0 || *body->s==0 ) { LOG(L_ERR,"ERROR:uac:parse_authenticate_body: empty body\n"); goto error; } memset( auth, 0, sizeof(struct authenticate_body)); p = body->s; end = body->s + body->len; /* parse the "digest" */ while (p=end ) goto parse_error; if (strncmp(p,AUTHENTICATE_DIGEST_S,AUTHENTICATE_DIGEST_LEN)!=0) goto parse_error; p += AUTHENTICATE_DIGEST_LEN; if (!isspace((int)*p)) goto parse_error; p++; while (p=\"%.*s\" state=%d\n", name.len,name.s,val.len,val.s,state); /* process the AVP */ switch (state) { case QOP_STATE: /* TODO - add qop support */ LOG(L_ERR,"ERROR:uac:parse_authenticate_body: no qop support " "for the moment :-(\n"); goto error; auth->qop = val; break; case REALM_STATE: auth->realm = val; break; case NONCE_STATE: auth->nonce = val; break; case DOMAIN_STATE: auth->domain = val; break; case OPAQUE_STATE: auth->opaque = val; break; case ALGORITHM_STATE: if (val.len==3) { if ( LOWER4B(GET3B(val.s))==0x6d6435ff) /*MD5*/ auth->flags |= AUTHENTICATE_MD5; } else { LOG(L_ERR,"ERROR:uac:parse_authenticate_body: " "unsupported algorithm \"%.*s\"\n",val.len,val.s); goto error; } break; case STALE_STATE: if (val.len==4 && LOWER4B(GET4B(val.s))==0x74727565) /*true*/ { auth->flags |= AUTHENTICATE_STALE; } else if ( !(val.len==5 && val.s[4]=='e' && LOWER4B(GET4B(val.s))==0x66616c73) ) { LOG(L_ERR,"ERROR:uac:parse_authenticate_body: " "unsupported stale value \"%.*s\"\n",val.len,val.s); goto error; } break; default: break; } } /* some checkings */ if (auth->nonce.s==0 || auth->realm.s==0) { LOG(L_ERR,"ERROR:uac:parse_authenticate_body: realm or " "nonce missing\n"); goto error; } return 0; parse_error: LOG(L_ERR,"ERROR:uac:parse_authenticate_body: parse error in <%.*s> " "around %ld\n", body->len, body->s, (long)(p-body->s)); error: return -1; } #define AUTHORIZATION_HDR_START "Authorization: Digest " #define AUTHORIZATION_HDR_START_LEN (sizeof(AUTHORIZATION_HDR_START)-1) #define PROXY_AUTHORIZATION_HDR_START "Proxy-Authorization: Digest " #define PROXY_AUTHORIZATION_HDR_START_LEN \ (sizeof(PROXY_AUTHORIZATION_HDR_START)-1) #define USERNAME_FIELD_S "username=\"" #define USERNAME_FIELD_LEN (sizeof(USERNAME_FIELD_S)-1) #define REALM_FIELD_S "realm=\"" #define REALM_FIELD_LEN (sizeof(REALM_FIELD_S)-1) #define NONCE_FIELD_S "nonce=\"" #define NONCE_FIELD_LEN (sizeof(NONCE_FIELD_S)-1) #define URI_FIELD_S "uri=\"" #define URI_FIELD_LEN (sizeof(URI_FIELD_S)-1) #define OPAQUE_FIELD_S "opaque=\"" #define OPAQUE_FIELD_LEN (sizeof(OPAQUE_FIELD_S)-1) #define RESPONSE_FIELD_S "response=\"" #define RESPONSE_FIELD_LEN (sizeof(RESPONSE_FIELD_S)-1) #define ALGORITHM_FIELD_S "algorithm=\"MD5\"" #define ALGORITHM_FIELD_LEN (sizeof(ALGORITHM_FIELD_S)-1) #define FIELD_SEPARATOR_S "\", " #define FIELD_SEPARATOR_LEN (sizeof(FIELD_SEPARATOR_S)-1) #define add_string( _p, _s, _l) \ do {\ memcpy( _p, _s, _l);\ _p += _l; \ }while(0) str* build_authorization_hdr(int code, str *uri, struct uac_credential *crd, struct authenticate_body *auth, char *response) { static str hdr; char *p; int len; int response_len; response_len = strlen(response); /* compile then len */ len = (code==401? AUTHORIZATION_HDR_START_LEN:PROXY_AUTHORIZATION_HDR_START_LEN) + USERNAME_FIELD_LEN + crd->user.len + FIELD_SEPARATOR_LEN + REALM_FIELD_LEN + crd->realm.len + FIELD_SEPARATOR_LEN + NONCE_FIELD_LEN + auth->nonce.len + FIELD_SEPARATOR_LEN + URI_FIELD_LEN + uri->len + FIELD_SEPARATOR_LEN + (auth->opaque.len? (OPAQUE_FIELD_LEN + auth->opaque.len + FIELD_SEPARATOR_LEN):0) + RESPONSE_FIELD_LEN + response_len + FIELD_SEPARATOR_LEN + ALGORITHM_FIELD_LEN + CRLF_LEN; hdr.s = (char*)pkg_malloc( len + 1); if (hdr.s==0) { LOG(L_ERR,"ERROR:uac:build_authorization_hdr: no more mem\n"); goto error; } p = hdr.s; /* header start */ if (code==401) { add_string( p, AUTHORIZATION_HDR_START USERNAME_FIELD_S, AUTHORIZATION_HDR_START_LEN+USERNAME_FIELD_LEN); } else { add_string( p, PROXY_AUTHORIZATION_HDR_START USERNAME_FIELD_S, PROXY_AUTHORIZATION_HDR_START_LEN+USERNAME_FIELD_LEN); } /* username */ add_string( p, crd->user.s, crd->user.len); /* REALM */ add_string( p, FIELD_SEPARATOR_S REALM_FIELD_S, FIELD_SEPARATOR_LEN+REALM_FIELD_LEN); add_string( p, crd->realm.s, crd->realm.len); /* NONCE */ add_string( p, FIELD_SEPARATOR_S NONCE_FIELD_S, FIELD_SEPARATOR_LEN+NONCE_FIELD_LEN); add_string( p, auth->nonce.s, auth->nonce.len); /* URI */ add_string( p, FIELD_SEPARATOR_S URI_FIELD_S, FIELD_SEPARATOR_LEN+URI_FIELD_LEN); add_string( p, uri->s, uri->len); /* OPAQUE */ if (auth->opaque.len ) { add_string( p, FIELD_SEPARATOR_S OPAQUE_FIELD_S, FIELD_SEPARATOR_LEN+OPAQUE_FIELD_LEN); add_string( p, auth->opaque.s, auth->opaque.len); } /* RESPONSE */ add_string( p, FIELD_SEPARATOR_S RESPONSE_FIELD_S, FIELD_SEPARATOR_LEN+RESPONSE_FIELD_LEN); add_string( p, response, response_len); /* ALGORITHM */ add_string( p, FIELD_SEPARATOR_S ALGORITHM_FIELD_S CRLF, FIELD_SEPARATOR_LEN+ALGORITHM_FIELD_LEN+CRLF_LEN); hdr.len = p - hdr.s; if (hdr.len!=len) { LOG(L_CRIT,"BUG:uac:build_authorization_hdr: bad buffer computation " "(%d<>%d)\n",len,hdr.len); pkg_free( hdr.s ); goto error; } DBG("DEBUG:uac:build_authorization_hdr: hdr is <%.*s>\n", hdr.len,hdr.s); return &hdr; error: return 0; } kamailio-4.0.4/obsolete/uac/from.h0000644000000000000000000000301612223032460015475 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 Voice Sistem SRL * * This file is part of SIP Express Router. * * UAC SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * UAC SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * 2005-01-31 first version (ramona) */ #ifndef _UAC_FROM_H_ #define _UAC_FROM_H_ #include "../../parser/msg_parser.h" #include "../../str.h" #include "../../modules/tm/t_hooks.h" #define FROM_NO_RESTORE (0) #define FROM_AUTO_RESTORE (1) #define FROM_MANUAL_RESTORE (2) void init_from_replacer(); int replace_from( struct sip_msg *msg, str *from_dsp, str *from_uri); int restore_from( struct sip_msg *msg, int is_req); /* TM callback functions */ void tr_checker(struct cell* t, int type, struct tmcb_params *p); #endif kamailio-4.0.4/obsolete/uac/auth.h0000644000000000000000000000254212223032460015476 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 Voice Sistem SRL * * This file is part of SIP Express Router. * * UAC SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * UAC SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * 2005-01-31 first version (ramona) */ #ifndef _UAC_AUTH_H_ #define _UAC_AUTH_H_ #include "../../parser/msg_parser.h" struct uac_credential { str realm; str user; str passwd; struct uac_credential *next; }; int has_credentials(); int add_credential( unsigned int type, void *val); void destroy_credentials(); int uac_auth( struct sip_msg *msg); #endif kamailio-4.0.4/obsolete/uac/auth_hdr.h0000644000000000000000000000317712223032460016340 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 Voice Sistem SRL * * This file is part of SIP Express Router. * * UAC SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * UAC SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * 2005-01-31 first version (ramona) */ #ifndef _UAC_AUTH_HDR_H_ #define _UAC_AUTH_HDR_H_ #include "../../str.h" #include "auth.h" struct authenticate_body { int flags; str realm; str domain; str nonce; str opaque; str qop; }; #define AUTHENTICATE_MD5 (1<<0) #define AUTHENTICATE_MD5SESS (1<<1) #define AUTHENTICATE_STALE (1<<2) #define QOP_AUTH (1<<3) #define QOP_AUTH_INT (1<<4) int parse_authenticate_body( str *body, struct authenticate_body *auth); str* build_authorization_hdr(int code, str *uri, struct uac_credential *crd, struct authenticate_body *auth, char *response); #endif kamailio-4.0.4/obsolete/uac/Makefile0000644000000000000000000000035212223032460016021 0ustar rootroot# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=uac.so LIBS= DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/cpl-c/0000755000000000000000000000000012223032460014607 5ustar rootrootkamailio-4.0.4/obsolete/cpl-c/init.mysql0000644000000000000000000000017612223032460016645 0ustar rootrootUSE ser; CREATE TABLE IF NOT EXISTS cpl ( user VARCHAR(50) NOT NULL PRIMARY KEY, cpl_xml BLOB, cpl_bin BLOB, UNIQUE (user)); kamailio-4.0.4/obsolete/cpl-c/CPL_tree.h0000644000000000000000000002017412223032460016421 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _CPL_TREE_DEFINITION_H #define _CPL_TREE_DEFINITION_H #define CPL_NODE 1 #define INCOMING_NODE 2 #define OUTGOING_NODE 3 #define ANCILLARY_NODE 4 #define SUBACTION_NODE 5 #define ADDRESS_SWITCH_NODE 6 #define ADDRESS_NODE 7 #define BUSY_NODE 8 #define DEFAULT_NODE 9 #define FAILURE_NODE 10 #define LOG_NODE 11 #define LOOKUP_NODE 12 #define LOCATION_NODE 13 #define LANGUAGE_NODE 14 #define LANGUAGE_SWITCH_NODE 15 #define MAIL_NODE 16 #define NOTFOUND_NODE 17 #define NOANSWER_NODE 18 #define PROXY_NODE 19 #define PRIORITY_NODE 20 #define PRIORITY_SWITCH_NODE 21 #define REJECT_NODE 22 #define REDIRECT_NODE 23 #define REDIRECTION_NODE 24 #define REMOVE_LOCATION_NODE 25 #define SUB_NODE 26 #define SUCCESS_NODE 27 #define STRING_NODE 28 #define STRING_SWITCH_NODE 29 #define TIME_NODE 30 #define TIME_SWITCH_NODE 31 #define OTHERWISE_NODE 32 #define NOT_PRESENT_NODE 33 /* attributes and values fro ADDRESS-SWITCH node */ #define FIELD_ATTR 0 /*shared with STRING_SWITCH*/ #define SUBFIELD_ATTR 1 #define ORIGIN_VAL 0 #define DESTINATION_VAL 1 #define ORIGINAL_DESTINATION_VAL 2 #define ADDRESS_TYPE_VAL 0 #define USER_VAL 1 #define HOST_VAL 2 #define PORT_VAL 3 #define TEL_VAL 4 #define DISPLAY_VAL 5 /* attributes and values for ADDRESS node */ #define IS_ATTR 0 /*shared with STRING*/ #define CONTAINS_ATTR 1 /*shared with STRING*/ #define SUBDOMAIN_OF_ATTR 2 /* attributes and values for STRING-SWITCH node */ #define SUBJECT_VAL 0 #define ORGANIZATION_VAL 1 #define USER_AGENT_VAL 2 #define DISPALY_VAL 3 /* attributes and values for LANGUAGE node */ #define MATCHES_TAG_ATTR 0 #define MATCHES_SUBTAG_ATTR 1 /* attributes and values for TIME-SWITCH node */ #define TZID_ATTR 0 #define TZURL_ATTR 1 /* attributes and values for TIME node */ #define DTSTART_ATTR 0 #define DTEND_ATTR 1 #define DURATION_ATTR 2 #define FREQ_ATTR 3 #define INTERVAL_ATTR 4 #define UNTIL_ATTR 5 #define COUNT_ATTR 6 #define BYSECOND_ATTR 7 #define BYMINUTE_ATTR 8 #define BYHOUR_ATTR 9 #define BYDAY_ATTR 10 #define BYMONTHDAY_ATTR 11 #define BYYEARDAY_ATTR 12 #define BYWEEKNO_ATTR 13 #define BYMONTH_ATTR 14 #define WKST_ATTR 15 #define BYSETPOS_ATTR 16 /* attributes and values for PRIORITY node */ #define LESS_ATTR 0 #define GREATER_ATTR 1 #define EQUAL_ATTR 2 #define PRIOSTR_ATTR 3 #define EMERGENCY_VAL 0 #define EMERGENCY_STR "emergency" #define EMERGENCY_STR_LEN (sizeof(EMERGENCY_STR)-1) #define URGENT_VAL 1 #define URGENT_STR "urgent" #define URGENT_STR_LEN (sizeof(URGENT_STR)-1) #define NORMAL_VAL 2 #define NORMAL_STR "normal" #define NORMAL_STR_LEN (sizeof(NORMAL_STR)-1) #define NON_URGENT_VAL 3 #define NON_URGENT_STR "non-urgent" #define NON_URGENT_STR_LEN (sizeof(NON_URGENT_STR)-1) #define UNKNOWN_PRIO_VAL 4 /* attributes and values for LOCATION node */ #define URL_ATTR 0 #define PRIORITY_ATTR 1 #define CLEAR_ATTR 2 /*shared with LOOKUP node*/ #define NO_VAL 0 /*shared with LOOKUP node*/ #define YES_VAL 1 /*shared with LOOKUP node*/ /* attributes and values for LOOKUP node */ #define SOURCE_ATTR 0 #define TIMEOUT_ATTR 1 /*shared with PROXY node*/ #define SOURCE_REG_STR "registration" #define SOURCE_REG_STR_LEN (sizeof("registration")-1) /* attributes and values for REMOVE_LOCATION node */ #define LOCATION_ATTR 0 #define PARAM_ATTR 1 #define VALUE_ATTR 2 /* attributes and values for PROXY node */ #define RECURSE_ATTR 2 #define ORDERING_ATTR 3 #define PARALLEL_VAL 0 #define SEQUENTIAL_VAL 1 #define FIRSTONLY_VAL 2 /* attributes and values for REDIRECT node */ #define PERMANENT_ATTR 0 /* attributes and values for REJECT node */ #define STATUS_ATTR 0 #define REASON_ATTR 1 #define BUSY_VAL 486 #define BUSY_STR "busy" #define BUSY_STR_LEN (sizeof(BUSY_STR)-1) #define NOTFOUND_VAL 404 #define NOTFOUND_STR "notfound" #define NOTFOUND_STR_LEN (sizeof(NOTFOUND_STR)-1) #define REJECT_VAL 603 #define REJECT_STR "reject" #define REJECT_STR_LEN (sizeof(REJECT_STR)-1) #define ERROR_VAL 500 #define ERROR_STR "error" #define ERROR_STR_LEN (sizeof(ERROR_STR)-1) /* attributes and values for LOG node */ #define NAME_ATTR 0 #define MAX_NAME_SIZE 32 #define COMMENT_ATTR 1 #define MAX_COMMENT_SIZE 128 /* attributes and values for EMAIL node */ #define TO_ATTR 0 #define SUBJECT_ATTR 1 #define SUBJECT_EMAILHDR_STR "subject" #define SUBJECT_EMAILHDR_LEN (sizeof(SUBJECT_EMAILHDR_STR)-1) #define BODY_ATTR 2 #define BODY_EMAILHDR_STR "body" #define BODY_EMAILHDR_LEN (sizeof(BODY_EMAILHDR_STR)-1) #define URL_MAILTO_STR "mailto:" #define URL_MAILTO_LEN (sizeof(URL_MAILTO_STR)-1) /* attributes and values for SUB node */ #define REF_ATTR 0 /* node = | type(1) | nr_kids(1) | nr_attrs(1) | unused(1) | * | x*kids_offset(2) | y*attrs(2*n) | */ #define NODE_TYPE(_p) ( *((unsigned char*)(_p)) ) #define NR_OF_KIDS(_p) ( *((unsigned char* )((_p)+1)) ) #define NR_OF_ATTR(_p) ( *((unsigned char* )((_p)+1+1)) ) #define KID_OFFSET_PTR(_p,_n) ( (unsigned short*)((_p)+4+2*(_n)) ) #define ATTR_PTR(_p) ( (_p)+4+2*NR_OF_KIDS(_p) ) #define SIMPLE_NODE_SIZE(_p) ( 4+2*NR_OF_KIDS(_p) ) #define GET_NODE_SIZE(_n) ( 4+2*(_n) ) #define BASIC_ATTR_SIZE 4 #define SET_KID_OFFSET(_p,_n,_o) *KID_OFFSET_PTR(_p,_n)=htons(_o) #define KID_OFFSET(_p,_n) ntohs(*KID_OFFSET_PTR(_p,_n)) #endif kamailio-4.0.4/obsolete/cpl-c/cpl_switches.h0000644000000000000000000010273212223032460017454 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-06-27: file created (bogdan) */ #include "cpl_time.h" #include "../../parser/parse_from.h" #include "../../parser/parse_uri.h" /* UPDATED + CHECKED */ static inline char *run_address_switch( struct cpl_interpreter *intr ) { static str def_port_str = STR_STATIC_INIT("5060"); unsigned short field, subfield; char *p; char *kid; unsigned short attr_name; unsigned short n; int i; int k; str cpl_val; str *msg_val; str *uri; struct sip_uri parsed_uri; field = subfield = UNDEF_CHAR; msg_val = 0; p=ATTR_PTR(intr->ip); /* parse the attributes */ for( i=NR_OF_ATTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr( p, attr_name, n, intr, script_error); switch (attr_name) { case FIELD_ATTR: if (field!=UNDEF_CHAR) { LOG(L_ERR,"ERROR:cpl-c:run_address_switch: multiple FIELD " "attrs found\n"); goto script_error; } field = n; break; case SUBFIELD_ATTR: if (subfield!=UNDEF_CHAR) { LOG(L_ERR,"ERROR:cpl-c:run_address_switch: multiple SUBFIELD" " attrs found\n"); goto script_error; } subfield = n; break; default: LOG(L_ERR,"ERROR:cpl_c:run_address_switch: unknown attribute " "(%d) in ADDRESS_SWITCH node\n",*p); goto script_error; } } if (field==UNDEF_CHAR) { LOG(L_ERR,"ERROR:cpl_c:run_address_switch: mandatory param FIELD " "no found\n"); goto script_error; } /* test the condition from all the sub-nodes */ for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case NOT_PRESENT_NODE: DBG("DEBUG:run_address_switch: NOT_PRESENT node found ->" "skipping (useless in this case)\n"); break; case OTHERWISE_NODE : if (i!=NR_OF_KIDS(intr->ip)-1) { LOG(L_ERR,"ERROR:run_address_switch: OTHERWISE node " "not found as the last sub-node!\n"); goto script_error; } DBG("DEBUG:run_address_switch: matching on OTHERWISE node\n"); return get_first_child(kid); case ADDRESS_NODE : /* check the number of attributes */ if (NR_OF_ATTR(kid)!=1) { LOG(L_ERR,"ERROR:run_address_switch: incorrect nr of attrs " "(%d) in ADDRESS node\n",NR_OF_ATTR(kid)); goto script_error; } /* get the attribute name */ p = ATTR_PTR(kid); get_basic_attr( p, attr_name, cpl_val.len, intr, script_error); if (attr_name!=IS_ATTR && attr_name!=CONTAINS_ATTR && attr_name!=SUBDOMAIN_OF_ATTR) { LOG(L_ERR,"ERROR:run_address_switch: unknown attribute " "(%d) in ADDRESS node\n",attr_name); goto script_error; } /* get attribute value */ get_str_attr( p, cpl_val.s, cpl_val.len, intr, script_error,1); DBG("DEBUG:run_address_switch: testing ADDRESS branch " " attr_name=%d attr_val=[%.*s](%d)..\n", attr_name,cpl_val.len,cpl_val.s,cpl_val.len); /* extract the needed value from the message */ if (!msg_val) { switch (field) { case ORIGIN_VAL: /* FROM */ if (!intr->from) { /* get the header */ if (parse_from_header( intr->msg )==-1) goto runtime_error; intr->from = &(get_from(intr->msg)->uri); } uri = intr->from; break; case DESTINATION_VAL: /* RURI */ if (!intr->ruri) intr->ruri = GET_RURI( intr->msg ); uri = intr->ruri; break; case ORIGINAL_DESTINATION_VAL: /* TO */ if (!intr->to) { /* get and parse the header */ if (!intr->msg->to && (parse_headers(intr->msg,HDR_TO_F,0)==-1 || !intr->msg->to)) { LOG(L_ERR,"ERROR:run_address_switch: bad " "msg or missing TO header\n"); goto runtime_error; } intr->to = &(get_to(intr->msg)->uri); } uri = intr->to; break; default: LOG(L_ERR,"ERROR:run_address_switch: unknown " "attribute (%d) in ADDRESS node\n",field); goto script_error; } DBG("DEBUG:run_address_switch: extracted uri is <%.*s>\n", uri->len, uri->s); switch (subfield) { case UNDEF_CHAR: msg_val = uri; break; case USER_VAL: if (parse_uri( uri->s, uri->len, &parsed_uri)<0) goto runtime_error; msg_val = &(parsed_uri.user); break; case HOST_VAL: if (parse_uri( uri->s, uri->len, &parsed_uri)<0) goto runtime_error; msg_val = &(parsed_uri.host); break; case PORT_VAL: if (parse_uri( uri->s, uri->len, &parsed_uri)<0) goto runtime_error; if (parsed_uri.port.len!=0) msg_val = &(parsed_uri.port); else msg_val = &def_port_str; break; case TEL_VAL: if (parse_uri( uri->s, uri->len, &parsed_uri)<0) goto runtime_error; if (parsed_uri.user_param_val.len==5 && memcmp(parsed_uri.user_param_val.s,"phone",5)==0) msg_val = &(parsed_uri.user); break; case ADDRESS_TYPE_VAL: case DISPLAY_VAL: default: LOG(L_ERR,"ERROR:run_address_switch: unsupported " "value attribute (%d) in ADDRESS node\n", subfield); goto script_error; } DBG("DEBUG:run_address_switch: extracted val. is <%.*s>\n", (msg_val==0)?0:msg_val->len, (msg_val==0)?0:msg_val->s); } /* does the value from script match the one from message? */ switch (attr_name) { case IS_ATTR: if ( (!msg_val && !cpl_val.s) || (msg_val && msg_val->len==cpl_val.len && strncasecmp(msg_val->s,cpl_val.s,cpl_val.len)==0)) { DBG("DEBUG:run_address_switch: matching on " "ADDRESS node (IS)\n"); return get_first_child(kid); } break; case CONTAINS_ATTR: if (subfield!=DISPLAY_VAL) { LOG(L_WARN,"WARNING:run_address_switch: operator " "CONTAINS applies only to DISPLAY -> ignored\n"); } else { if ( msg_val && cpl_val.len<=msg_val->len && strcasestr_str(msg_val, &cpl_val)!=0 ) { DBG("DEBUG:run_address_switch: matching on " "ADDRESS node (CONTAINS)\n"); return get_first_child(kid); } } break; case SUBDOMAIN_OF_ATTR: switch (subfield) { case HOST_VAL: k = msg_val->len - cpl_val.len; if (k>=0 && (k==0 || msg_val->s[k-1]=='.') && !strncasecmp(cpl_val.s,msg_val->s+k,cpl_val.len) ) { DBG("DEBUG:run_address_switch: matching on " "ADDRESS node (SUBDOMAIN_OF)\n"); return get_first_child(kid); } break; case TEL_VAL: if (msg_val==0) break; if (msg_val->len>=cpl_val.len && !strncasecmp( cpl_val.s,msg_val->s,cpl_val.len)) { DBG("DEBUG:run_address_switch: matching on " "ADDRESS node (SUBDOMAIN_OF)\n"); return get_first_child(kid); } break; default: LOG(L_WARN,"WARNING:run_address_switch: operator" " SUBDOMAIN_OF applies only to HOST or TEL " "-> ignored\n"); } break; } break; default: LOG(L_ERR,"ERROR:run_address_switch: unknown output node type " "(%d) for ADDRESS_SWITCH node\n",NODE_TYPE(kid)); goto script_error; } } /* none of the branches of ADDRESS_SWITCH matched -> go for default */ return DEFAULT_ACTION; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_string_switch( struct cpl_interpreter *intr ) { unsigned short field; char *p; char *kid; char *not_present_node; unsigned short attr_name; int i; str cpl_val; str msg_val; not_present_node = 0; msg_val.s = 0; msg_val.len = 0; /* parse the attribute */ if (NR_OF_ATTR(intr->ip)!=1) { LOG(L_ERR,"ERROR:cpl_c:run_string_switch: node should have 1 attr, not" " (%d)\n",NR_OF_ATTR(intr->ip)); goto script_error; } p=ATTR_PTR(intr->ip); get_basic_attr( p, attr_name, field, intr, script_error); if (attr_name!=FIELD_ATTR) { LOG(L_ERR,"ERROR:cpl_c:run_string_switch: unknown param type (%d)" " for STRING_SWITCH node\n",*p); goto script_error; } for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case NOT_PRESENT_NODE: if (not_present_node) { LOG(L_ERR,"ERROR:run_string_switch: NOT_PRESENT node " "found twice!\n"); goto script_error; } not_present_node = kid; break; case OTHERWISE_NODE : if (i!=NR_OF_KIDS(intr->ip)-1) { LOG(L_ERR,"ERROR:run_string_switch: OTHERWISE node " "not found as the last sub-node!\n"); goto script_error; } DBG("DEBUG:run_string_switch: matching on OTHERWISE node\n"); return get_first_child(kid); case STRING_NODE : /* check the number of attributes */ if (NR_OF_ATTR(kid)!=1) { LOG(L_ERR,"ERROR:run_string_switch: incorrect nr of attrs " "(%d) in STRING node (expected 1)\n",NR_OF_ATTR(kid)); goto script_error; } /* get the attribute name */ p = ATTR_PTR(kid); get_basic_attr( p, attr_name, cpl_val.len, intr, script_error); if (attr_name!=IS_ATTR && attr_name!=CONTAINS_ATTR ) { LOG(L_ERR,"ERROR:run_string_switch: unknown attribute " "(%d) in STRING node\n",attr_name); goto script_error; } /* get attribute value */ get_str_attr( p, cpl_val.s, cpl_val.len, intr, script_error,1); DBG("DEBUG:run_string_switch: testing STRING branch " "attr_name=%d attr_val=[%.*s](%d)..\n", attr_name,cpl_val.len,cpl_val.s,cpl_val.len); if (!msg_val.s) { switch (field) { case SUBJECT_VAL: /* SUBJECT */ if (intr->subject==STR_NOT_FOUND) goto not_present; if (!intr->subject) { /* get the subject header */ if (!intr->msg->subject) { if (parse_headers(intr->msg, HDR_SUBJECT_F,0)==-1) { LOG(L_ERR,"ERROR:run_string_switch: " "bad SUBJECT header\n"); goto runtime_error; } else if (!intr->msg->subject) { /* hdr not present */ intr->subject = STR_NOT_FOUND; goto not_present; } } intr->subject = &(intr->msg->subject->body); } trim_len( msg_val.len,msg_val.s, *(intr->subject)); break; case ORGANIZATION_VAL: /* ORGANIZATION */ if (intr->organization==STR_NOT_FOUND) goto not_present; if (!intr->organization) { /* get the organization header */ if (!intr->msg->organization) { if (parse_headers(intr->msg, HDR_ORGANIZATION_F,0)==-1) { LOG(L_ERR,"ERROR:run_string_switch: " "bad ORGANIZATION hdr\n"); goto runtime_error; } else if (!intr->msg->organization) { /* hdr not present */ intr->organization = STR_NOT_FOUND; goto not_present; } } intr->organization = &(intr->msg->organization->body); } trim_len( msg_val.len,msg_val.s, *(intr->organization)); break; case USER_AGENT_VAL: /* User Agent */ if (intr->user_agent==STR_NOT_FOUND) goto not_present; if (!intr->user_agent) { /* get the header */ if (!intr->msg->user_agent) { if (parse_headers(intr->msg, HDR_USERAGENT_F,0)==-1) { LOG(L_ERR,"ERROR:run_string_switch: " "bad USERAGENT hdr\n"); goto runtime_error; } else if (!intr->msg->user_agent) { /* hdr not present */ intr->user_agent = STR_NOT_FOUND; goto not_present; } } intr->user_agent = &(intr->msg->user_agent->body); } trim_len( msg_val.len,msg_val.s, *(intr->user_agent)); break; default: LOG(L_ERR,"ERROR:run_string_switch: unknown " "attribute (%d) in STRING node\n",field); goto script_error; } DBG("DEBUG:run_string_switch: extracted msg string is " "<%.*s>\n",msg_val.len, msg_val.s); } /* does the value from script match the one from message? */ switch (attr_name) { case IS_ATTR: if ( (!msg_val.s && !cpl_val.s) || (msg_val.len==cpl_val.len && strncasecmp(msg_val.s,cpl_val.s,cpl_val.len)==0)) { DBG("DEBUG:run_string_switch: matching on " "STRING node (IS)\n"); return get_first_child(kid); } break; case CONTAINS_ATTR: if (cpl_val.len<=msg_val.len && strcasestr_str(&msg_val, &cpl_val)!=0 ) { DBG("DEBUG:run_string_switch: matching on " "STRING node (CONTAINS)\n"); return get_first_child(kid); } break; } break; default: LOG(L_ERR,"ERROR:run_string_switch: unknown output node type " "(%d) for STRING_SWITCH node\n",NODE_TYPE(kid)); goto script_error; } } /* none of the branches of STRING_SWITCH matched -> go for default */ return DEFAULT_ACTION; not_present: DBG("DEBUG:run_string_switch: required hdr not present in sip msg\n"); if (not_present_node) return get_first_child(not_present_node); /* look for the NOT_PRESENT node */ DBG("DEBUG:run_string_switch: searching for NOT_PRESENT sub-node..\n"); for(; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); if (NODE_TYPE(kid)==NOT_PRESENT_NODE) return get_first_child(kid); } return DEFAULT_ACTION; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_priority_switch( struct cpl_interpreter *intr ) { static str default_val=STR_STATIC_INIT("normal"); unsigned short n; char *p; char *kid; char *not_present_node; unsigned short attr_name; unsigned short attr_val; unsigned short msg_attr_val; unsigned short msg_prio; int i; str cpl_val = STR_NULL; str msg_val = STR_NULL; not_present_node = 0; msg_attr_val = NORMAL_VAL; for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case NOT_PRESENT_NODE: if (not_present_node) { LOG(L_ERR,"ERROR:run_priority_switch: NOT_PRESENT node " "found twice!\n"); goto script_error; } not_present_node = kid; break; case OTHERWISE_NODE : if (i!=NR_OF_KIDS(intr->ip)-1) { LOG(L_ERR,"ERROR:run_priority_switch: OTHERWISE node " "not found as the last sub-node!\n"); goto script_error; } DBG("DEBUG:run_priority_switch: matching on OTHERWISE node\n"); return get_first_child(kid); case PRIORITY_NODE : if (NR_OF_ATTR(kid)!=1) goto script_error; /* get the attribute */ p = ATTR_PTR(kid); get_basic_attr( p, attr_name, attr_val, intr, script_error); if (attr_name!=LESS_ATTR && attr_name!=GREATER_ATTR && attr_name!=EQUAL_ATTR){ LOG(L_ERR,"ERROR:run_priority_switch: unknown attribute " "(%d) in PRIORITY node\n",attr_name); goto script_error; } /* attribute's encoded value */ if (attr_val!=EMERGENCY_VAL && attr_val!=URGENT_VAL && attr_val!=NORMAL_VAL && attr_val!=NON_URGENT_VAL && attr_val!=UNKNOWN_PRIO_VAL) { LOG(L_ERR,"ERROR:run_priority_switch: unknown encoded " "value (%d) for attribute (*d) in PRIORITY node\n",*p); goto script_error; } if (attr_val==UNKNOWN_PRIO_VAL) { if (attr_name!=EQUAL_ATTR) { LOG(L_ERR,"ERROR:cpl_c:run_priority_switch:bad PRIORITY" " branch: attr=EQUAL doesn't match val=UNKNOWN\n"); goto script_error; } /* if the attr is UNKNOWN, its string value is present */ get_basic_attr(p, n,cpl_val.len, intr, script_error); if (n!=PRIOSTR_ATTR) { LOG(L_ERR,"ERROR:run_priority_switch: expected PRIOSTR" "(%d) attr, found (%d)\n",PRIOSTR_ATTR,n); goto script_error; } get_str_attr(p, cpl_val.s, cpl_val.len,intr,script_error,1); } DBG("DEBUG:run_priority_switch: testing PRIORITY branch " "(attr=%d,val=%d) [%.*s](%d)..\n", attr_name,attr_val,cpl_val.len,cpl_val.s,cpl_val.len); if (!msg_val.s) { if (!intr->priority) { /* get the PRIORITY header from message */ if (!intr->msg->priority) { if (parse_headers(intr->msg,HDR_PRIORITY_F,0)==-1){ LOG(L_ERR,"ERROR:run_priority_switch: bad " "sip msg or PRIORITY header !\n"); goto runtime_error; } else if (!intr->msg->priority) { LOG(L_NOTICE,"NOTICE:run_priority_switch: " "missing PRIORITY header -> using " "default value \"normal\"!\n"); intr->priority = &default_val; } else { intr->priority = &(intr->msg->priority->body); } } else { intr->priority = &(intr->msg->priority->body); } } trim_len( msg_val.len, msg_val.s, *(intr->priority)); /* encode attribute's value from SIP message */ if ( msg_val.len==EMERGENCY_STR_LEN && !strncasecmp(msg_val.s,EMERGENCY_STR,msg_val.len) ) { msg_attr_val = EMERGENCY_VAL; } else if ( msg_val.len==URGENT_STR_LEN && !strncasecmp(msg_val.s,URGENT_STR,msg_val.len) ) { msg_attr_val = URGENT_VAL; } else if ( msg_val.len==NORMAL_STR_LEN && !strncasecmp(msg_val.s,NORMAL_STR,msg_val.len) ) { msg_attr_val = NORMAL_VAL; } else if ( msg_val.len==NON_URGENT_STR_LEN && !strncasecmp(msg_val.s,NON_URGENT_STR,msg_val.len) ) { msg_attr_val = NON_URGENT_VAL; } else { msg_attr_val = UNKNOWN_PRIO_VAL; } DBG("DEBUG:run_priority_switch: extracted msg priority is " "<%.*s> decoded as [%d]\n", msg_val.len,msg_val.s,msg_attr_val); } DBG("DEBUG:run_priority_switch: using msg string <%.*s>\n", msg_val.len, msg_val.s); /* attr_val (from cpl) cannot be UNKNOWN - we already * check it -> check only for msg_attr_val for non-EQUAL op */ if (msg_attr_val==UNKNOWN_PRIO_VAL && attr_name!=EQUAL_ATTR) { LOG(L_NOTICE,"NOTICE:run_priority_switch: UNKNOWN " "value found in sip_msg when string a LESS/GREATER " "cmp -> force the value to default \"normal\"\n"); msg_prio = NORMAL_VAL; } else { msg_prio = msg_attr_val; } /* does the value from script match the one from message? */ switch (attr_name) { case LESS_ATTR: switch (attr_val) { case EMERGENCY_VAL: if (msg_prio!=EMERGENCY_VAL) break; /*OK*/ else continue; /* for cycle for all kids */ case URGENT_VAL: if (msg_prio!=EMERGENCY_VAL && msg_prio!=URGENT_VAL) break; /* OK */ else continue; /* for cycle for all kids */ case NORMAL_VAL: if (msg_prio==NON_URGENT_VAL) break; /*OK*/ else continue; /* for cycle for all kids */ case NON_URGENT_VAL: continue; /* for cycle for all kids */ } break; case GREATER_ATTR: switch (attr_val) { case EMERGENCY_VAL: continue; /* for cycle for all kids */ case URGENT_VAL: if (msg_prio!=EMERGENCY_VAL) break; /*OK*/ else continue; /* for cycle for all kids */ case NORMAL_VAL: if (msg_prio!=NON_URGENT_VAL && msg_prio!=NORMAL_VAL) break; /*OK*/ else continue; /* for cycle for all kids */ case NON_URGENT_VAL: if (msg_prio!=NON_URGENT_VAL) break; /*OK*/ else continue; /* for cycle for all kids */ } break; case EQUAL_ATTR: if ( attr_val==msg_prio ) { if (attr_val==UNKNOWN_PRIO_VAL) { if ( msg_val.len==cpl_val.len && !strncasecmp(msg_val.s,cpl_val.s,msg_val.len)){ break; /* OK */ } } else { break; /* OK */ } } continue; /* for cycle for all kids */ break; } /* end switch for attr_name */ DBG("DEBUG:run_priority_switch: matching current " "PRIORITY node\n"); return get_first_child(kid); break; default: LOG(L_ERR,"ERROR:run_priority_switch: unknown output node type" " (%d) for PRIORITY_SWITCH node\n",NODE_TYPE(kid)); goto script_error; } /* end switch for NODE_TYPE */ } /* end for for all kids */ /* none of the branches of PRIORITY_SWITCH matched -> go for default */ return DEFAULT_ACTION; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } inline static int set_TZ(char *tz_env) { DBG("DEBUG:cpl-c:set_TZ: switching TZ as \"%s\"\n",tz_env); if (putenv( tz_env )==-1) { LOG(L_ERR,"ERROR:cpl-c:set_TZ: setenv failed -> unable to set TZ " " \"%s\"\n",tz_env); return -1; } tzset(); /* just to be sure */ return 0; } /* UPDATED + CHECKED */ static inline char *run_time_switch( struct cpl_interpreter *intr ) { char *p; char *kid; char *attr_str; unsigned short attr_name; unsigned short attr_len; unsigned char flags = 0; int nr_attrs; int i,j; str user_tz = STR_NULL; ac_tm_t att; tmrec_t trt; DBG("DEBUG:cpl-c:run_time_switch: checking recv. time stamp <%d>\n", intr->recv_time); switch (NR_OF_ATTR(intr->ip)) { case 1: p = ATTR_PTR(intr->ip); get_basic_attr( p, attr_name, user_tz.len, intr, script_error); if (attr_name!=TZID_ATTR) { LOG(L_ERR,"ERROR:cpl-c:run_time_switch: bad attribute -> " " expected=%d, found=%d\n",TZID_ATTR,attr_name); goto script_error; } get_str_attr( p, user_tz.s, user_tz.len, intr, script_error, 1); case 0: break; default: LOG(L_ERR,"ERROR:cpl-c:run_time_switch: incorrect number of attr ->" " found=%d expected=(0,1)\n",NR_OF_ATTR(intr->ip)); goto script_error; } if (user_tz.s && user_tz.len) { if (set_TZ(user_tz.s)==-1) goto runtime_error; flags |= (1<<7); } for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case NOT_PRESENT_NODE: DBG("DEBUG:cpl-c:run_time_switch: NOT_PRESENT node found ->" "skipping (useless in this case)\n"); break; case OTHERWISE_NODE : if (i!=NR_OF_KIDS(intr->ip)-1) { LOG(L_ERR,"ERROR:cpl-c:run_time_switch: OTHERWISE node " "not found as the last sub-node!\n"); goto script_error; } DBG("DEBUG:cpl-c:run_time_switch: matching on " "OTHERWISE node\n"); return get_first_child(kid); case TIME_NODE : /* init structures */ memset( &att, 0, sizeof(att)); memset( &trt, 0, sizeof(trt)); if(ac_tm_set_time( &att, intr->recv_time)) goto runtime_error; /* let's see how many attributes we have */ nr_attrs = NR_OF_ATTR(kid); /* get the attributes */ p = ATTR_PTR(kid); for(j=0;j go for default */ ac_tm_free( &att ); tmrec_free( &trt ); return DEFAULT_ACTION; runtime_error: if ( flags&(1<<7) ) set_TZ(cpl_env.orig_tz.s); ac_tm_free( &att ); tmrec_free( &trt ); return CPL_RUNTIME_ERROR; parse_err: LOG(L_ERR,"ERROR:cpl-c:run_priority_switch: error parsing attr [%d][%s]\n", attr_name,attr_str?(char*)attr_str:"NULL"); script_error: if ( flags&(1<<7) ) set_TZ(cpl_env.orig_tz.s); ac_tm_free( &att ); tmrec_free( &trt ); return CPL_SCRIPT_ERROR; } inline static int is_lang_tag_matching(str *range,str *cpl_tag,str *cpl_subtag) { char *c; char *end; str tag = STR_NULL; str subtag = STR_NULL; c = range->s; end = range->s + range->len; while(c='a' && ((*c)|0x20)<='z' ) { /*DBG("--- tag ---> <%c>[%d]\n",*c,*c);*/ tag.len++; c++; } if (tag.len==0) goto error; if (c='a' && ((*c)|0x20)<='z' ) { /*DBG("--- subtag ---> <%c>[%d]\n",*c,*c);*/ subtag.len++; c++; } if (subtag.len==0) goto error; } else { subtag.s = 0; } if (clen,cpl_tag->s,cpl_subtag->len,cpl_subtag->s); /* language range of "*" is ignored for the purpose of matching*/ if ( !(tag.len==1 && *tag.s=='*') ) { /* does the language tag matches ? */ if (tag.len==cpl_tag->len && !strncasecmp(tag.s,cpl_tag->s, tag.len)) { DBG("cucu bau \n"); /* if the subtag of the range is void -> matche */ if (subtag.len==0) return 1; /* the subtags equals -> matche */ if (subtag.len==cpl_subtag->len && !strncasecmp(subtag.s,cpl_subtag->s,subtag.len) ) return 1; } } /* if ',' go for the next language range */ if (*c==',') c++; } else { goto error; } } no_matche: return 0; error: LOG(L_ERR,"ERROR:cpl-c:is_lang_tag_matching: parse error in Accept-" "Language body <%.*s> at char <%c>[%d] offset %ld!\n", range->len,range->s,*c,*c,(long)(c-range->s)); return -1; } /* UPDATED + CHECKED */ static inline char *run_language_switch( struct cpl_interpreter *intr ) { char *p; char *kid; char *not_present_node; unsigned short attr_name; int nr_attr; int i,j; str attr = STR_NULL; str msg_val = STR_NULL; str lang_tag = STR_NULL; str lang_subtag = STR_NULL; not_present_node = 0; for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case NOT_PRESENT_NODE: if (not_present_node) { LOG(L_ERR,"ERROR:run_language_switch: NOT_PRESENT node " "found twice!\n"); goto script_error; } not_present_node = kid; break; case OTHERWISE_NODE : if (i!=NR_OF_KIDS(intr->ip)-1) { LOG(L_ERR,"ERROR:run_language_switch: OTHERWISE node " "not found as the last sub-node!\n"); goto script_error; } DBG("DEBUG:run_language_switch: matching on OTHERWISE node\n"); return get_first_child(kid); case LANGUAGE_NODE : /* check the number of attributes */ nr_attr = NR_OF_ATTR(kid); if (nr_attr<1 || nr_attr>2) { LOG(L_ERR,"ERROR:run_string_switch: incorrect nr of attrs " "(%d) in LANGUAGE node (1 or 2)\n",NR_OF_ATTR(kid)); goto script_error; } /* get the attributes */ p = ATTR_PTR(kid); lang_tag.s = lang_subtag.s = 0; lang_tag.len = lang_subtag.len = 0; for(j=0;j if not yet, do it now * and remember it for the next times */ if (!msg_val.s) { if (intr->accept_language==STR_NOT_FOUND) goto not_present; if (!intr->accept_language) { /* get the accept_language header */ if (!intr->msg->accept_language) { if (parse_headers(intr->msg, HDR_ACCEPTLANGUAGE_F,0)==-1) { LOG(L_ERR,"ERROR:run_language_switch: " "bad ACCEPT_LANGUAGE header\n"); goto runtime_error; } else if (!intr->msg->accept_language) { /* hdr not present */ intr->accept_language = STR_NOT_FOUND; goto not_present; } } intr->subject = &(intr->msg->accept_language->body); } } trim_len( msg_val.len,msg_val.s, *(intr->subject)); DBG("DEBUG:run_language_switch: extracted msg string is " "<%.*s>\n",msg_val.len, msg_val.s); /* does the value from script match the one from message? */ if (msg_val.len && msg_val.s) { j = is_lang_tag_matching(&msg_val,&lang_tag,&lang_subtag); if (j==1) { DBG("DEBUG:run_language_switch: matching on " "LANGUAGE node\n"); return get_first_child(kid); }else if (j==-1) { goto runtime_error; } } break; default: LOG(L_ERR,"ERROR:cpl_c:run_language_switch: unknown output " "node type (%d) for LANGUAGE_SWITCH node\n", NODE_TYPE(kid)); goto script_error; } /* end switch for NODE_TYPE */ } /* end for for all kids */ return DEFAULT_ACTION; not_present: DBG("DEBUG:run_string_switch: required hdr not present in sip msg\n"); if (not_present_node) return get_first_child(not_present_node); /* look for the NOT_PRESENT node */ DBG("DEBUG:run_string_switch: searching for NOT_PRESENT sub-node..\n"); for(; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); if (NODE_TYPE(kid)==NOT_PRESENT_NODE) return get_first_child(kid); } return DEFAULT_ACTION; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } kamailio-4.0.4/obsolete/cpl-c/doc/0000755000000000000000000000000012223032460015354 5ustar rootrootkamailio-4.0.4/obsolete/cpl-c/doc/cpl-c.xml0000644000000000000000000000462512223032460017103 0ustar rootroot
Bogdan-Andrei Iancu FhG FOKUS
iancu@fokus.fraunhofer.de
2003 FhG FOKUS
CPL Module
Overview cpl-c modules implements a CPL (Call Processing Language) interpreter. Support for uploading/downloading/removing scripts via SIP REGISTER method is implemented.
Dependencies
SER Modules The following modules must be loaded before this module: tm Transaction Manager, used for proxying/forking requests. sl StateLess module - used for sending stateless reply when responding to REGISTER request or for sending back error responses. usrloc User location module - used for implementing lookup("registration") (adding into location set of the users' contact)
External Libraries or Applications The following libraries or applications must be installed before running SER with this module loaded: libxml2 This library contains an engine for XML parsing, DTD validation and DOM manipulation.
kamailio-4.0.4/obsolete/cpl-c/doc/functions.xml0000644000000000000000000001234612223032460020114 0ustar rootroot
Functions
<function>cpl_run_script(type,mode)</function> Starts the execution of the CPL script. The user name is fetched from new_uri or requested uri or from To header -in this order- (for incoming execution) or from FROM header (for outgoing execution). Regarding the stateful/stateless message processing, the function is very flexible, being able to run in different modes (see below the"mode" parameter). Normally this function will end script execution. There is no guaranty that the CPL script interpretation ended when ser script ended also (for the same INVITE ;-)) - this can happen when the CPL script does a PROXY and the script interpretation pause after proxying and it will be resume when some reply is received (this can happen in a different process of SER). If the function returns to script, the SIP server should continue with the normal behavior as if no script existed. When some error is returned, the function itself haven't sent any SIP error reply (this can be done from script). Meaning of the parameters is as follows: type - which part of the script should be run; set it to "incoming" for having the incoming part of script executed (when an INVITE is received) or to "outgoing" for running the outgoing part of script (when a user is generating an INVITE - call). mode - sets the interpreter mode as stateless/stateful behavior. The following modes are accepted: IS_STATELESS - the current INVITE has no transaction created yet. All replies (redirection or deny) will be done is a stateless way. The execution will switch to stateful only when proxy is done. So, if the function returns, will be in stateless mode. IS_STATEFUL - the current INVITE has already a transaction associated. All signaling operations (replies or proxy) will be done in stateful way.So, if the function returns, will be in stateful mode. FORCE_STATEFUL - the current INVITE has no transaction created yet. All signaling operations will be done is a stateful way (on signaling, the transaction will be created from within the interpreter). So, if the function returns, will be in stateless mode. is_stateful is very difficult to manage from the routing script (script processing can continue in stateful mode); is_stateless is the fastest and consumes less resources (transaction is created only if proxying is done), but there is only a minimal protection against retransmissions (since replies are send statelessly); force_stateful is a good compromise - all signaling is done stateful (retransmission protection) and in the same time, if returning to script, it will be in stateless mode (easy to continue the routing script execution) <function>cpl_run_script</function> usage ... cpl_run_script("incoming","force_stateful"); ...
<function moreinfo="none">cpl_process_register()</function> This function MUST be called only for REGISTER requests. It checks if the current REGISTER request is related or not with CPL script upload/download/ remove. If it is, all the needed operation will be done. For checking if the REGISTER is CPL related, the function looks fist to "Content-Type" header. If it exists and has a the mime type set to "application/cpl+xml" means this is a CPL script upload/remove operation. The distinction between to case is made by looking at "Content-Disposition" header; id its value is "script;action=store", means it's an upload; if it's "script;action=remove", means it's a remove operation; other values are considered to be errors. If no "Content-Type" header is present, the function looks to "Accept" header and if it contains the "*" or "application/cpl-xml" the request it will be consider one for downloading CPL scripts. The functions returns to script only if the REGISTER is not related to CPL. In other case, the function will send by itself the necessary replies (stateless - using sl), including for errors.
kamailio-4.0.4/obsolete/cpl-c/doc/Makefile0000644000000000000000000000012612223032460017013 0ustar rootrootdocs = cpl-c.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/cpl-c/doc/params.xml0000644000000000000000000001177512223032460017374 0ustar rootroot
Parameters
<varname>cpl_db</varname> (string) A SQL URL have to be given to the module for knowing where the database containing the table with CPL scripts is locates. If required a user name and password can be specified for allowing the module to connect to the database server. This parameter is mandatory. Set <varname>cpl_db</varname> parameter ... modparam("cpl_c","cpl_db","mysql://user:passwd@host/database") ...
<varname>cpl_table</varname> (string) Indicates the name of the table that store the CPL scripts. This table must be locate into the database specified by "cpl_db" parameter. For more about the format of the CPL table please see modules/cpl-c/init.mysql. This parameter is mandatory. Set <varname>cpl_table</varname> parameter ... modparam("cpl_c","cpl_table","cpltable") ...
<varname>cpl_dtd_file</varname> (string) Points to the DTD file describing the CPL grammar. The file name may include also the path to the file. This path can be absolute or relative (be careful the path will be relative to the starting directory of SER). This parameter is mandatory. Set <varname>cpl_dtd_file</varname> parameter ... modparam("cpl_c","cpl_dtd_file","/etc/ser/cpl-06.dtd") ...
<varname>log_dir</varname> (string) Points to a directory where should be created all the log file generated by the LOG CPL node. A log file per user will be created (on demand) having the name username.log. If this parameter is absent, the logging will be disabled without generating error on execution. Set <varname>log_dir</varname> parameter ... modparam("cpl_c","log_dir","/var/log/ser/cpl") ...
<varname>proxy_recurse</varname> (int) Tells for how many time is allow to have recurse for PROXY CPL node If it has value 2, when doing proxy, only twice the proxy action will be re-triggered by a redirect response; the third time, the proxy execution will end by going on REDIRECTION branch. The recurse feature can be disable by setting this parameter to 0 Default value of this parameter is 0. Set <varname>proxy_recurse</varname> parameter ... modparam("cpl_c","proxy_recurse",2) ...
<varname>proxy_route</varname> (int) Before doing proxy (forward), a script route can be executed. All modifications made by that route will be reflected only for the current branch. Default value of this parameter is 0 (none). Set <varname>proxy_route</varname> parameter ... modparam("cpl_c","proxy_route",1) ...
<varname>nat_flag</varname> (int) Sets the flag used for marking calls via NAT. Used by lookup tag when retrieving a contact behind a NAT (this flag will be set). Default value of this parameter is 6. Set <varname>nat_flag</varname> parameter ... modparam("cpl_c","nat_flag",4) ...
<varname>lookup_domain</varname> (int) Tells if the lookup tag should use or not the domain part when doing user location search. Set it to a non zero value to force also domain matching. Default value of this parameter is 0. Set <varname>lookup_domain</varname> parameter ... modparam("cpl_c","lookup_domain",1) ...
kamailio-4.0.4/obsolete/cpl-c/cpl_loader.h0000644000000000000000000000230412223032460017063 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2003-06-24: file created (bogdan) */ #ifndef _CPL_LOADER_H #define _CPL_LOADER_H #include "../../str.h" int load_file( char *filename, str *xml); #endif kamailio-4.0.4/obsolete/cpl-c/cpl_nonsig.c0000644000000000000000000001645612223032460017122 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-06-27: file created (bogdan) */ #include #include #include #include #include #include #include #include #include #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "../../cfg/cfg_struct.h" #include "cpl_nonsig.h" #include "CPL_tree.h" #define MAX_LOG_FILE_NAME 32 #define MAX_FD 32 #define FILE_NAME_SUFIX ".log" #define FILE_NAME_SUFIX_LEN (sizeof(FILE_NAME_SUFIX)-1) #define LOG_SEPARATOR ": " #define LOG_SEPARATOR_LEN (sizeof(LOG_SEPARATOR)-1) #define DEFAULT_LOG_NAME "default_log" #define DEFAULT_LOG_NAME_LEN (sizeof(DEFAULT_LOG_NAME)-1) #define LOG_TERMINATOR "\n" #define LOG_TERMINATOR_LEN (sizeof(LOG_TERMINATOR)-1) static char file[MAX_LOG_DIR_SIZE+1+MAX_LOG_FILE_NAME+FILE_NAME_SUFIX_LEN+1]; static char *file_ptr; static inline void write_log( struct cpl_cmd *cmd) { struct iovec wr_vec[5]; time_t now; char *time_ptr; int fd; int ret; /* build file name (cmd->s1 is the user name)*/ if (cmd->s1.len>MAX_LOG_FILE_NAME) cmd->s1.len = MAX_LOG_FILE_NAME; memcpy(file_ptr, cmd->s1.s, cmd->s1.len ); memcpy(file_ptr+cmd->s1.len,FILE_NAME_SUFIX,FILE_NAME_SUFIX_LEN); file_ptr[cmd->s1.len+FILE_NAME_SUFIX_LEN] = 0; /* get current date+time -> wr_vec[0] */ time( &now ); time_ptr = ctime( &now ); wr_vec[0].iov_base = time_ptr; wr_vec[0].iov_len = strlen( time_ptr ); /* ctime_r adds a \n at the end -> overwrite it with space */ time_ptr[ wr_vec[0].iov_len-1 ] = ' '; /* log name (cmd->s2) -> wr_vec[1] */ if (cmd->s2.s==0 || cmd->s2.len==0) { wr_vec[1].iov_base = DEFAULT_LOG_NAME; wr_vec[1].iov_len = DEFAULT_LOG_NAME_LEN; } else { wr_vec[1].iov_base = cmd->s2.s; wr_vec[1].iov_len = cmd->s2.len; } /* log separator -> wr_vec[2] */ wr_vec[2].iov_base = LOG_SEPARATOR; wr_vec[2].iov_len = LOG_SEPARATOR_LEN; /* log comment (cmd->s3) -> wr_vec[3] */ wr_vec[3].iov_base = cmd->s3.s; wr_vec[3].iov_len = cmd->s3.len; /* log terminator -> wr_vec[2] */ wr_vec[4].iov_base = LOG_TERMINATOR; wr_vec[4].iov_len = LOG_TERMINATOR_LEN; /* [create+]open the file */ fd = open( file, O_CREAT|O_APPEND|O_WRONLY, 0664); if (fd==-1) { LOG(L_ERR,"ERROR:cpl_c:write_log: cannot open file [%s] : %s\n", file, strerror(errno) ); return; } /* get the log */ DBG("DEBUG:cpl_c:write_log: logging into [%s]... \n",file); /* I'm really not interested in the return code for write ;-) */ while ( (ret=writev( fd, wr_vec, 5))==-1 ) { if (errno==EINTR) continue; LOG(L_ERR,"ERROR:cpl_c:write_log: writing to log file [%s] : %s\n", file, strerror(errno) ); } close (fd); shm_free( cmd->s1.s ); } static inline void send_mail( struct cpl_cmd *cmd) { char *argv[5]; int pfd[2]; pid_t pid; int i; if (pipe(pfd) < 0) { LOG(L_ERR,"ERROR:cpl_c:send_mail: pipe failed: %s\n",strerror(errno)); return; } /* even if I haven't fork yet, I push the date on the pipe just to get * rid of one more malloc + copy */ if (cmd->s3.len && cmd->s3.s) { if ( (i=write( pfd[1], cmd->s3.s, cmd->s3.len ))!=cmd->s3.len ) { LOG(L_ERR,"ERROR:cpl_c:send_mail: write returned error %s\n", strerror(errno)); goto error; } } if ( (pid = fork()) < 0) { LOG(L_ERR,"ERROR:cpl_c:send_mail: fork failed: %s\n",strerror(errno)); goto error; } else if (pid==0) { /* child -> close all descriptors excepting pfd[0] */ for (i=3; i < MAX_FD; i++) if (i!=pfd[0]) close(i); if (pfd[0] != STDIN_FILENO) { dup2(pfd[0], STDIN_FILENO); close(pfd[0]); } /* set the argument vector*/ argv[0] = "mail"; argv[1] = "-s"; if (cmd->s2.s && cmd->s2.len) { /* put the subject in this format : <"$subject"\0> */ if ( (argv[2]=(char*)pkg_malloc(1+cmd->s2.len+1+1))==0) { LOG(L_ERR,"ERROR:cpl_c:send_mail: cannot get pkg memory\n"); goto child_exit; } argv[2][0] = '\"'; memcpy(argv[2]+1,cmd->s2.s,cmd->s2.len); argv[2][cmd->s2.len+1] = '\"'; argv[2][cmd->s2.len+2] = 0; } else { argv[2] = "\"[CPL notification]\""; } /* put the TO in <$to\0> format*/ if ( (argv[3]=(char*)pkg_malloc(cmd->s1.len+1))==0) { LOG(L_ERR,"ERROR:cpl_c:send_mail: cannot get pkg memory\n"); goto child_exit; } memcpy(argv[3],cmd->s1.s,cmd->s1.len); argv[3][cmd->s1.len] = 0; /* last element in vector mist be a null pointer */ argv[4] = (char*)0; /* just debug */ for(i=0;i free the shm */ shm_free( cmd->s1.s ); /* set an alarm -> sending the email shouldn't take more than 10 sec */ alarm(10); /* run the external mailer */ DBG("DEBUG:cpl_c:send_mail: new forked process created -> " "doing execv..\n"); execv("/usr/bin/mail",argv); /* if we got here means execv exit with error :-( */ LOG(L_ERR,"ERROR:cpl_c:send_mail: execv failed! (%s)\n", strerror(errno)); child_exit: _exit(127); } /* parent -> close both ends of pipe */ close(pfd[0]); close(pfd[1]); return; error: shm_free( cmd->s1.s ); close(pfd[0]); close(pfd[1]); return; } void cpl_aux_process( int cmd_out, char *log_dir) { struct cpl_cmd cmd; int len; /* this process will ignore SIGCHLD signal */ if (signal( SIGCHLD, SIG_IGN)==SIG_ERR) { LOG(L_ERR,"ERROR:cpl_c:cpl_aux_process: cannot set to IGNORE " "SIGCHLD signal\n"); } /* set the path for logging */ if (log_dir) { strcpy( file, log_dir); file_ptr = file + strlen(log_dir); *(file_ptr++) = '/'; } while(1) { /* let's read a command from pipe */ len = read( cmd_out, &cmd, sizeof(struct cpl_cmd)); if (len!=sizeof(struct cpl_cmd)) { if (len>=0) { LOG(L_ERR,"ERROR:cpl_aux_processes: truncated message" " read from pipe! -> discarded\n"); } else if (errno!=EAGAIN) { LOG(L_ERR,"ERROR:cpl_aux_process: pipe reading failed: " " : %s\n",strerror(errno)); } sleep(1); continue; } /* update the local config */ cfg_update(); /* process the command*/ switch (cmd.code) { case CPL_LOG_CMD: write_log( &cmd ); break; case CPL_MAIL_CMD: send_mail( &cmd ); break; default: LOG(L_ERR,"ERROR:cpl_aux_process: unknown command (%d) " "received! -> ignoring\n",cmd.code); } /* end switch*/ } } kamailio-4.0.4/obsolete/cpl-c/cpl_run.c0000644000000000000000000007120412223032460016421 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-01-23 : created (bogdan) * 2003-09-11 : build_lump_rpl() merged into add_lump_rpl() (bogdan) * 2004-06-14 : all global variables merged into cpl_env and cpl_fct; * append_branches param added to lookup node (bogdan) * 2004-06-14 : flag CPL_IS_STATEFUL is set now immediately after the * transaction is created (bogdan) */ #include #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../str.h" #include "../../dprint.h" #include "../../parser/msg_parser.h" #include "../../data_lump_rpl.h" #include "../../modules/tm/tm_load.h" #include "../usrloc/usrloc.h" #include "CPL_tree.h" #include "loc_set.h" #include "cpl_utils.h" #include "cpl_nonsig.h" #include "cpl_sig.h" #include "cpl_env.h" #include "cpl_run.h" #define EO_SCRIPT ((char*)0xffffffff) #define DEFAULT_ACTION ((char*)0xfffffffe) #define CPL_SCRIPT_ERROR ((char*)0xfffffffd) #define CPL_RUNTIME_ERROR ((char*)0xfffffffc) #define CPL_TO_CONTINUE ((char*)0xfffffffb) #define HDR_NOT_FOUND ((char*)0xffffffff) #define UNDEF_CHAR (0xff) #define check_overflow_by_ptr(_ptr_,_intr_,_error_) \ do {\ if ( (char*)(_ptr_)>(_intr_)->script.len+(_intr_)->script.s ) {\ LOG(L_ERR,"ERROR:cpl_c: overflow detected ip=%p ptr=%p in "\ "func. %s, line %d\n",(_intr_)->ip,_ptr_,__FILE__,__LINE__);\ goto _error_; \ } \ }while(0) #define check_overflow_by_offset(_len_,_intr_,_error_) \ do {\ if ( (char*)((_intr_)->ip+(_len_)) > \ (_intr_)->script.len+(_intr_)->script.s ) {\ LOG(L_ERR,"ERROR:cpl_c: overflow detected ip=%p offset=%d in "\ "func. %s, line %d\n",(_intr_)->ip,_len_,__FILE__,__LINE__);\ goto _error_; \ } \ }while(0) #define get_first_child(_node_) \ ((NR_OF_KIDS(_node_)==0)?DEFAULT_ACTION:(_node_)+KID_OFFSET(_node_,0)) #define get_basic_attr(_p_,_code_,_n_,_intr_,_error_) \ do{\ check_overflow_by_ptr( (_p_)+BASIC_ATTR_SIZE, _intr_, _error_);\ _code_ = ntohs( *((unsigned short*)(_p_)) );\ _n_ = ntohs( *((unsigned short*)((_p_)+2)) );\ (_p_) += 4;\ }while(0) #define get_str_attr(_p_,_s_,_len_,_intr_,_error_,_FIXUP_) \ do{\ if ( ((int)(_len_))-(_FIXUP_)<=0 ) {\ LOG(L_ERR,"ERROR:cpl_c:%s:%d: attribute is an empty string\n",\ __FILE__,__LINE__);\ goto _error_; \ } else {\ check_overflow_by_ptr( (_p_)+(_len_), _intr_, _error_);\ _s_ = _p_;\ (_p_) += (_len_) + 1*(((_len_)&0x0001)==1);\ (_len_) -= (_FIXUP_);\ }\ }while(0) struct cpl_interpreter* new_cpl_interpreter( struct sip_msg *msg, str *script) { struct cpl_interpreter *intr = 0; intr = (struct cpl_interpreter*)shm_malloc(sizeof(struct cpl_interpreter)); if (!intr) { LOG(L_ERR,"ERROR:build_cpl_interpreter: no more free memory!\n"); goto error; } memset( intr, 0, sizeof(struct cpl_interpreter)); /* init the interpreter*/ intr->script.s = script->s; intr->script.len = script->len; intr->recv_time = time(0); intr->ip = script->s; intr->msg = msg; /* check the beginning of the script */ if ( NODE_TYPE(intr->ip)!=CPL_NODE ) { LOG(L_ERR,"ERROR:build_cpl_interpreter: first node is not CPL!!\n"); goto error; } return intr; error: return 0; } void free_cpl_interpreter(struct cpl_interpreter *intr) { if (intr) { empty_location_set( &(intr->loc_set) ); if (intr->script.s) shm_free( intr->script.s); if (intr->user.s) shm_free(intr->user.s); if (intr->flags&CPL_RURI_DUPLICATED) shm_free(intr->ruri); if (intr->flags&CPL_TO_DUPLICATED) shm_free(intr->to); if (intr->flags&CPL_FROM_DUPLICATED) shm_free(intr->from); if (intr->flags&CPL_SUBJECT_DUPLICATED) shm_free(intr->subject); if (intr->flags&CPL_ORGANIZATION_DUPLICATED) shm_free(intr->organization); if (intr->flags&CPL_USERAGENT_DUPLICATED) shm_free(intr->user_agent); if (intr->flags&CPL_ACCEPTLANG_DUPLICATED) shm_free(intr->accept_language); if (intr->flags&CPL_PRIORITY_DUPLICATED) shm_free(intr->priority); shm_free(intr); } } /* UPDATED + CHECKED */ static inline char *run_cpl_node( struct cpl_interpreter *intr ) { char *kid; unsigned char start; int i; start = (intr->flags&CPL_RUN_INCOMING)?INCOMING_NODE:OUTGOING_NODE; /* look for the starting node (incoming or outgoing) */ for(i=0;iip);i++) { kid= intr->ip + KID_OFFSET(intr->ip,i); if ( NODE_TYPE(kid)==start ) { return get_first_child(kid); } else if (NODE_TYPE(kid)==SUBACTION_NODE || NODE_TYPE(kid)==ANCILLARY_NODE || NODE_TYPE(kid)==INCOMING_NODE || NODE_TYPE(kid)==OUTGOING_NODE ) { continue; } else { LOG(L_ERR,"ERROR:cpl_c:run_cpl_node: unknown child type (%d) " "for CPL node!!\n",NODE_TYPE(kid)); return CPL_SCRIPT_ERROR; } } DBG("DEBUG:cpl_c:run_cpl_node: CPL node has no %d subnode -> default\n", start); return DEFAULT_ACTION; } /* UPDATED + CHECKED */ static inline char *run_lookup( struct cpl_interpreter *intr ) { unsigned short attr_name; unsigned short n; unsigned char clear; char *p; char *kid; char *failure_kid = 0; char *success_kid = 0; char *notfound_kid = 0; int i; time_t tc; urecord_t* r; ucontact_t* contact; clear = NO_VAL; /* check the params */ for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr(p,attr_name,n,intr,script_error); switch (attr_name) { case CLEAR_ATTR: if (n!=YES_VAL && n!=NO_VAL) LOG(L_WARN,"WARNING:run_lookup: invalid value (%u) found" " for param. CLEAR in LOOKUP node -> using " "default (%u)!\n",n,clear); else clear = n; break; default: LOG(L_ERR,"ERROR:run_lookup: unknown attribute (%d) in " "LOOKUP node\n",attr_name); goto script_error; } } /* check the kids */ for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case SUCCESS_NODE : success_kid = kid; break; case NOTFOUND_NODE: notfound_kid = kid; break; case FAILURE_NODE: failure_kid = kid; break; default: LOG(L_ERR,"ERROR:run_lookup: unknown output node type" " (%d) for LOOKUP node\n",NODE_TYPE(kid)); goto script_error; } } kid = failure_kid; if (cpl_env.lu_domain) { /* fetch user's contacts via usrloc */ tc = time(0); cpl_fct.ulb.lock_udomain( cpl_env.lu_domain ); i = cpl_fct.ulb.get_urecord( cpl_env.lu_domain, &intr->user, &r); if (i < 0) { /* failure */ LOG(L_ERR, "ERROR:run_lookup: Error while querying usrloc\n"); cpl_fct.ulb.unlock_udomain( cpl_env.lu_domain ); } else if (i > 0) { /* not found */ DBG("DBG:cpl-c:run_lookup: '%.*s' Not found in usrloc\n", intr->user.len, intr->user.s); cpl_fct.ulb.unlock_udomain( cpl_env.lu_domain ); kid = notfound_kid; } else { contact = r->contacts; /* skip expired contacts */ while ( contact && contact->expires <= tc) contact = contact->next; /* any contacts left? */ if (contact) { /* clear loc set if requested */ if (clear) empty_location_set( &(intr->loc_set) ); /* start adding locations to set */ do { DBG("DBG:cpl-c:run_lookup: adding <%.*s>q=%d\n", contact->c.len,contact->c.s,(int)(10*contact->q)); if (add_location( &(intr->loc_set), &contact->c, (int)(10*contact->q), CPL_LOC_DUPL|((contact->flags & FL_NAT)*CPL_LOC_NATED) )==-1) { LOG(L_ERR,"ERROR:cpl-c:run_lookup: unable to add " "location to set :-(\n"); cpl_fct.ulb.unlock_udomain( cpl_env.lu_domain ); goto runtime_error; } contact = contact->next; }while( contact && cpl_env.lu_append_branches); /* set the flag for modifying the location set */ intr->flags |= CPL_LOC_SET_MODIFIED; /* we found a valid contact */ kid = success_kid; } else { /* no valid contact found */ kid = notfound_kid; } cpl_fct.ulb.unlock_udomain( cpl_env.lu_domain ); } } if (kid) return get_first_child(kid); return DEFAULT_ACTION; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_location( struct cpl_interpreter *intr ) { unsigned short attr_name; unsigned short n; char *p; unsigned char prio; unsigned char clear; str url; int i; clear = NO_VAL; prio = 10; url.s = (char*)UNDEF_CHAR; url.len = 0; /* fixes gcc 4.0 warning */ /* sanity check */ if (NR_OF_KIDS(intr->ip)>1) { LOG(L_ERR,"ERROR:run_location: LOCATION node suppose to have max " "one child, not %d!\n",NR_OF_KIDS(intr->ip)); goto script_error; } for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr(p,attr_name,n,intr,script_error); switch (attr_name) { case URL_ATTR: url.len = n; get_str_attr( p, url.s, url.len, intr, script_error,1); break; case PRIORITY_ATTR: if ( n>10) LOG(L_WARN,"WARNING:run_location: invalid value (%u) found" " for param. PRIORITY in LOCATION node -> using " "default (%u)!\n",n,prio); else prio = n; break; case CLEAR_ATTR: if (n!=YES_VAL && n!=NO_VAL) LOG(L_WARN,"WARNING:run_location: invalid value (%u) found" " for param. CLEAR in LOCATION node -> using " "default (%u)!\n",n,clear); else clear = n; break; default: LOG(L_ERR,"ERROR:run_location: unknown attribute (%d) in " "LOCATION node\n",attr_name); goto script_error; } } if (url.s==(char*)UNDEF_CHAR) { LOG(L_ERR,"ERROR:run_location: param. URL missing in LOCATION node\n"); goto script_error; } if (clear) empty_location_set( &(intr->loc_set) ); if (add_location( &(intr->loc_set), &url, prio, 0/*no dup*/ )==-1) { LOG(L_ERR,"ERROR:run_location: unable to add location to set :-(\n"); goto runtime_error; } /* set the flag for modifying the location set */ intr->flags |= CPL_LOC_SET_MODIFIED; return get_first_child(intr->ip); runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_remove_location( struct cpl_interpreter *intr ) { unsigned short attr_name; unsigned short n; char *p; str url; int i; url.s = (char*)UNDEF_CHAR; /* sanity check */ if (NR_OF_KIDS(intr->ip)>1) { LOG(L_ERR,"ERROR:cpl_c:run_remove_location: REMOVE_LOCATION node " "suppose to have max one child, not %d!\n", NR_OF_KIDS(intr->ip)); goto script_error; } /* dirty hack to speed things up in when loc set is already empty */ if (intr->loc_set==0) goto done; for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr(p,attr_name,n,intr,script_error); switch (attr_name) { case LOCATION_ATTR: url.len = n; get_str_attr( p, url.s, url.len, intr, script_error,1); break; default: LOG(L_ERR,"ERROR:run_remove_location: unknown attribute " "(%d) in REMOVE_LOCATION node\n",attr_name); goto script_error; } } if (url.s==(char*)UNDEF_CHAR) { DBG("DEBUG:run_remove_location: remove all locs from loc_set\n"); empty_location_set( &(intr->loc_set) ); } else { remove_location( &(intr->loc_set), url.s, url.len ); } /* set the flag for modifying the location set */ intr->flags |= CPL_LOC_SET_MODIFIED; done: return get_first_child(intr->ip); script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_sub( struct cpl_interpreter *intr ) { char *p; unsigned short offset; unsigned short attr_name; int i; /* sanity check */ if (NR_OF_KIDS(intr->ip)!=0) { LOG(L_ERR,"ERROR:cpl_c:run_sub: SUB node doesn't suppose to have any " "sub-nodes. Found %d!\n",NR_OF_KIDS(intr->ip)); goto script_error; } /* check the number of attr */ i = NR_OF_ATTR( intr->ip ); if (i!=1) { LOG(L_ERR,"ERROR:cpl_c:run_sub: incorrect nr. of attr. %d (<>1) in " "SUB node\n",i); goto script_error; } /* get attr's name */ p = ATTR_PTR(intr->ip); get_basic_attr( p, attr_name, offset, intr, script_error); if (attr_name!=REF_ATTR) { LOG(L_ERR,"ERROR:cpl_c:run_sub: invalid attr. %d (expected %d)in " "SUB node\n", attr_name, REF_ATTR); goto script_error; } /* make the jump */ p = intr->ip - offset; /* check the destination pointer -> are we still inside the buffer ;-) */ if (((char*)p)script.s) { LOG(L_ERR,"ERROR:cpl_c:run_sub: jump offset lower than the script " "beginning -> underflow!\n"); goto script_error; } check_overflow_by_ptr( p+SIMPLE_NODE_SIZE(intr->ip), intr, script_error); /* check to see if we hit a subaction node */ if ( NODE_TYPE(p)!=SUBACTION_NODE ) { LOG(L_ERR,"ERROR:cpl_c:run_sub: sub. jump hit a nonsubaction node!\n"); goto script_error; } if ( NR_OF_ATTR(p)!=0 ) { LOG(L_ERR,"ERROR:cpl_c:run_sub: invalid subaction node reached " "(attrs=%d); expected (0)!\n",NR_OF_ATTR(p)); goto script_error; } return get_first_child(p); script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_reject( struct cpl_interpreter *intr ) { unsigned short attr_name; unsigned short status; unsigned short n; char *reason_s; char *p; int i; reason_s = (char*)UNDEF_CHAR; status = UNDEF_CHAR; /* sanity check */ if (NR_OF_KIDS(intr->ip)!=0) { LOG(L_ERR,"ERROR:cpl_c:run_reject: REJECT node doesn't suppose to have " "any sub-nodes. Found %d!\n",NR_OF_KIDS(intr->ip)); goto script_error; } for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr( p, attr_name, n, intr, script_error); switch (attr_name) { case STATUS_ATTR: status = n; break; case REASON_ATTR: get_str_attr( p, reason_s, n, intr, script_error,1); break; default: LOG(L_ERR,"ERROR:cpl_c:run_reject: unknown attribute " "(%d) in REJECT node\n",attr_name); goto script_error; } } if (status==UNDEF_CHAR) { LOG(L_ERR,"ERROR:cpl_c:run_reject: mandatory attribute STATUS " "not found\n"); goto script_error; } if (status<400 || status>=700) { LOG(L_ERR,"ERROR:cpl_c:run_reject: bad attribute STATUS " "(%d)\n",status); goto script_error; } if (reason_s==(char*)UNDEF_CHAR ) { switch (status) { case 486: reason_s = "Busy Here"; break; case 404: reason_s = "Not Found"; break; case 603: reason_s = "Decline"; break; case 500: reason_s = "Internal Server Error"; break; default: reason_s = "Generic Error"; } } /* if still stateless and FORCE_STATEFUL set -> build the transaction */ if ( !(intr->flags&CPL_IS_STATEFUL) && intr->flags&CPL_FORCE_STATEFUL) { i = cpl_fct.tmb.t_newtran( intr->msg ); if (i<0) { LOG(L_ERR,"ERROR:cpl-c:run_reject: failed to build new " "transaction!\n"); goto runtime_error; } else if (i==0) { LOG(L_ERR,"ERROR:cpl-c:run_reject: processed INVITE is a " "retransmission!\n"); /* instead of generating an error is better just to break the * script by returning EO_SCRIPT */ return EO_SCRIPT; } intr->flags |= CPL_IS_STATEFUL; } /* send the reply */ if ( intr->flags&CPL_IS_STATEFUL ) { /* reply statefully */ i = cpl_fct.tmb.t_reply(intr->msg, (int)status, reason_s ); } else { /* reply statelessly */ i = cpl_fct.slb.zreply(intr->msg, status, reason_s ); } if ( i!=1 ) { LOG(L_ERR,"ERROR:run_reject: unable to send reject reply!\n"); goto runtime_error; } return EO_SCRIPT; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_redirect( struct cpl_interpreter *intr ) { struct location *loc; struct lump_rpl *lump; unsigned short attr_name; unsigned short permanent; unsigned short n; char *p; str lump_str; char *cp; int i; permanent = NO_VAL; /* sanity check */ if (NR_OF_KIDS(intr->ip)!=0) { LOG(L_ERR,"ERROR:cpl-c:run_redirect: REDIRECT node doesn't suppose " "to have any sub-nodes. Found %d!\n",NR_OF_KIDS(intr->ip)); goto script_error; } /* read the attributes of the REDIRECT node*/ for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr( p, attr_name, n, intr, script_error); switch (attr_name) { case PERMANENT_ATTR: if (n!=YES_VAL && n!=NO_VAL) { LOG(L_ERR,"ERROR:cpl-c:run_redirect: unsupported value (%d)" " in attribute PERMANENT for REDIRECT node",n); goto script_error; } permanent = n; break; default: LOG(L_ERR,"ERROR:run_redirect: unknown attribute " "(%d) in REDIRECT node\n",attr_name); goto script_error; } } /* build the lump for Contact header */ lump_str.len = 9 /*"Contact: "*/; for(loc=intr->loc_set;loc;loc=loc->next) lump_str.len += 1/*"<"*/ + loc->addr.uri.len + 7/*">;q=x.x"*/ + 2*(loc->next!=0)/*" ,"*/; lump_str.len += CRLF_LEN; lump_str.s = pkg_malloc( lump_str.len ); if(!lump_str.s) { LOG(L_ERR,"ERROR:cpl-c:run_redirect: out of pkg memory!\n"); goto runtime_error; } cp = lump_str.s; memcpy( cp , "Contact: " , 9); cp += 9; for(loc=intr->loc_set;loc;loc=loc->next) { *(cp++) = '<'; memcpy(cp,loc->addr.uri.s,loc->addr.uri.len); cp += loc->addr.uri.len; memcpy(cp,">;q=",4); cp += 4; *(cp++) = (loc->addr.priority!=10)?'0':'1'; *(cp++) = '.'; *(cp++) = '0'+(loc->addr.priority%10); if (loc->next) { *(cp++) = ' '; *(cp++) = ','; } } memcpy(cp,CRLF,CRLF_LEN); /* if still stateless and FORCE_STATEFUL set -> build the transaction */ if ( !(intr->flags&CPL_IS_STATEFUL) && intr->flags&CPL_FORCE_STATEFUL) { i = cpl_fct.tmb.t_newtran( intr->msg ); if (i<0) { LOG(L_ERR,"ERROR:cpl-c:run_redirect: failed to build new " "transaction!\n"); pkg_free( lump_str.s ); goto runtime_error; } else if (i==0) { LOG(L_ERR,"ERROR:cpl-c:run_redirect: processed INVITE is a " "retransmission!\n"); /* instead of generating an error is better just to break the * script by returning EO_SCRIPT */ pkg_free( lump_str.s ); return EO_SCRIPT; } intr->flags |= CPL_IS_STATEFUL; } /* add the lump to the reply */ lump = add_lump_rpl( intr->msg, lump_str.s , lump_str.len , LUMP_RPL_HDR); if(!lump) { LOG(L_ERR,"ERROR:cpl-c:run_redirect: unable to add lump_rpl! \n"); pkg_free( lump_str.s ); goto runtime_error; } /* send the reply */ if ( intr->flags&CPL_IS_STATEFUL ) { /* reply statefully */ if (permanent) i = cpl_fct.tmb.t_reply( intr->msg, (int)301, "Moved permanently"); else i = cpl_fct.tmb.t_reply( intr->msg, (int)302, "Moved temporarily"); } else { /* reply statelessly */ if (permanent) i = cpl_fct.slb.zreply( intr->msg, 301, "Moved permanently"); else i = cpl_fct.slb.zreply( intr->msg, 302, "Moved temporarily"); } /* msg which I'm working on can be in private memory or is a clone into * shared memory (if I'm after a failed proxy); So, it's better to removed * by myself the lump that I added previously */ unlink_lump_rpl( intr->msg, lump); free_lump_rpl( lump ); if (i!=1) { LOG(L_ERR,"ERROR:cpl-c:run_redirect: unable to send " "redirect reply!\n"); goto runtime_error; } return EO_SCRIPT; runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_log( struct cpl_interpreter *intr ) { char *p; unsigned short attr_name; unsigned short n; str name = STR_NULL; str comment = STR_NULL; str user; int i; /* sanity check */ if (NR_OF_KIDS(intr->ip)>1) { LOG(L_ERR,"ERROR:cpl_c:run_log: LOG node suppose to have max one child" ", not %d!\n",NR_OF_KIDS(intr->ip)); goto script_error; } /* is logging enabled? */ if ( cpl_env.log_dir==0 ) goto done; /* read the attributes of the LOG node*/ p = ATTR_PTR(intr->ip); for( i=NR_OF_ATTR(intr->ip); i>0 ; i-- ) { get_basic_attr( p, attr_name, n, intr, script_error); switch (attr_name) { case NAME_ATTR: get_str_attr( p, name.s, n, intr, script_error,1); name.len = n; break; case COMMENT_ATTR: get_str_attr( p, comment.s, n, intr, script_error,1); comment.len = n; break; default: LOG(L_ERR,"ERROR:cpl_c:run_log: unknown attribute " "(%d) in LOG node\n",attr_name); goto script_error; } } if (comment.len==0) { LOG(L_NOTICE,"NOTICE:cpl_c:run_log: LOG node has no comment attr -> " "skipping\n"); goto done; } user.len = intr->user.len + name.len + comment.len; /* duplicate the attrs in shm memory */ user.s = p = (char*)shm_malloc( user.len ); if (!user.s) { LOG(L_ERR,"ERROR:cpl_c:run_log: no more shm memory!\n"); goto runtime_error; } /* copy the user name */ memcpy( p, intr->user.s, intr->user.len); user.len = intr->user.len; p += intr->user.len; /* copy the log name */ if (name.len) { memcpy( p, name.s, name.len ); name.s = p; p += name.len; } /* copy the comment */ memcpy( p, comment.s, comment.len); comment.s = p; /* send the command */ write_cpl_cmd( CPL_LOG_CMD, &user, &name, &comment ); done: return get_first_child(intr->ip); runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } /* UPDATED + CHECKED */ static inline char *run_mail( struct cpl_interpreter *intr ) { unsigned short attr_name; unsigned short n; char *p; str subject = STR_NULL; str body = STR_NULL; str to = STR_NULL; int i; /* sanity check */ if (NR_OF_KIDS(intr->ip)>1) { LOG(L_ERR,"ERROR:cpl_c:run_mail: MAIL node suppose to have max one" " child, not %d!\n",NR_OF_KIDS(intr->ip)); goto script_error; } /* read the attributes of the MAIL node*/ for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr(p, attr_name, n, intr, script_error); switch (attr_name) { case TO_ATTR: get_str_attr(p, to.s, n, intr, script_error,0); to.len = n; break; case SUBJECT_ATTR: get_str_attr(p, subject.s, n, intr, script_error,0); subject.len = n; break; case BODY_ATTR: get_str_attr(p, body.s, n, intr, script_error,0); body.len = n; break; default: LOG(L_ERR,"ERROR:run_mail: unknown attribute " "(%d) in MAIL node\n",attr_name); goto script_error; } } if (to.len==0) { LOG(L_ERR,"ERROR:cpl_c:run_mail: email has an empty TO hdr!\n"); goto script_error; } if (body.len==0 && subject.len==0) { LOG(L_WARN,"WARNING:cpl_c:run_mail: I refuse to send email with no " "body and no subject -> skipping...\n"); goto done; } /* duplicate the attrs in shm memory */ p = (char*)shm_malloc( to.len + subject.len + body.len ); if (!p) { LOG(L_ERR,"ERROR:cpl_c:run_mail: no more shm memory!\n"); goto runtime_error; } /* copy the TO */ memcpy( p, to.s, to.len ); to.s = p; p += to.len; /* copy the subject */ if (subject.len) { memcpy( p, subject.s, subject.len ); subject.s = p; p += subject.len; } /* copy the body */ if (body.len) { memcpy( p, body.s, body.len ); body.s = p; p += body.len; } /* send the command */ write_cpl_cmd( CPL_MAIL_CMD, &to, &subject, &body); done: return get_first_child(intr->ip); runtime_error: return CPL_RUNTIME_ERROR; script_error: return CPL_SCRIPT_ERROR; } static inline int run_default( struct cpl_interpreter *intr ) { if (!(intr->flags&CPL_PROXY_DONE)) { /* no signaling operations */ if ( !(intr->flags&CPL_LOC_SET_MODIFIED) ) { /* no location modifications */ if (intr->loc_set==0 ) { /* case 1 : no location modifications or signaling operations * performed, location set empty -> * Look up the user's location through whatever mechanism the * server would use if no CPL script were in effect */ return SCRIPT_DEFAULT; } else { /* case 2 : no location modifications or signaling operations * performed, location set non-empty: (This can only happen * for outgoing calls.) -> * Proxy the call to the address in the location set. * With other words, let ser to continue processing the * request as nothing happened */ return SCRIPT_DEFAULT; } } else { /* case 3 : location modifications performed, no signaling * operations -> * Proxy the call to the addresses in the location set */ if (!cpl_proxy_to_loc_set(intr->msg,&(intr->loc_set),intr->flags)) return SCRIPT_END; return SCRIPT_RUN_ERROR; } } else { /* case 4 : proxy operation previously taken -> return whatever the * "best" response is of all accumulated responses to the call to this * point, according to the rules of the underlying signaling * protocol. */ /* we will let ser to choose and forward one of the replies -> for this * nothing must be done */ return SCRIPT_END; } /*return SCRIPT_RUN_ERROR;*/ } /* include all inline functions for processing the switches */ #include "cpl_switches.h" /* include inline function for running proxy node */ #include "cpl_proxy.h" int cpl_run_script( struct cpl_interpreter *intr ) { char *new_ip; do { check_overflow_by_offset( SIMPLE_NODE_SIZE(intr->ip), intr, error); switch ( NODE_TYPE(intr->ip) ) { case CPL_NODE: DBG("DEBUG:cpl_run_script: processing CPL node \n"); new_ip = run_cpl_node( intr ); /*UPDATED&TESTED*/ break; case ADDRESS_SWITCH_NODE: DBG("DEBUG:cpl_run_script: processing address-switch node\n"); new_ip = run_address_switch( intr ); /*UPDATED&TESTED*/ break; case STRING_SWITCH_NODE: DBG("DEBUG:cpl_run_script: processing string-switch node\n"); new_ip = run_string_switch( intr ); /*UPDATED&TESTED*/ break; case PRIORITY_SWITCH_NODE: DBG("DEBUG:cpl_run_script: processing priority-switch node\n"); new_ip = run_priority_switch( intr ); /*UPDATED&TESTED*/ break; case TIME_SWITCH_NODE: DBG("DEBUG:cpl_run_script: processing time-switch node\n"); new_ip = run_time_switch( intr ); /*UPDATED&TESTED*/ break; case LANGUAGE_SWITCH_NODE: DBG("DEBUG:cpl_run_script: processing language-switch node\n"); new_ip = run_language_switch( intr ); /*UPDATED&TESTED*/ break; case LOOKUP_NODE: DBG("DEBUG:cpl_run_script: processing lookup node\n"); new_ip = run_lookup( intr ); /*UPDATED&TESTED*/ break; case LOCATION_NODE: DBG("DEBUG:cpl_run_script: processing location node\n"); new_ip = run_location( intr ); /*UPDATED&TESTED*/ break; case REMOVE_LOCATION_NODE: DBG("DEBUG:cpl_run_script: processing remove_location node\n"); new_ip = run_remove_location( intr ); /*UPDATED&TESTED*/ break; case PROXY_NODE: DBG("DEBUG:cpl_run_script: processing proxy node\n"); new_ip = run_proxy( intr );/*UPDATED&TESTED*/ break; case REJECT_NODE: DBG("DEBUG:cpl_run_script: processing reject node\n"); new_ip = run_reject( intr ); /*UPDATED&TESTED*/ break; case REDIRECT_NODE: DBG("DEBUG:cpl_run_script: processing redirect node\n"); new_ip = run_redirect( intr ); /*UPDATED&TESTED*/ break; case LOG_NODE: DBG("DEBUG:cpl_run_script: processing log node\n"); new_ip = run_log( intr ); /*UPDATED&TESTED*/ break; case MAIL_NODE: DBG("DEBUG:cpl_run_script: processing mail node\n"); new_ip = run_mail( intr ); /*UPDATED&TESTED*/ break; case SUB_NODE: DBG("DEBUG:cpl_run_script: processing sub node\n"); new_ip = run_sub( intr ); /*UPDATED&TESTED*/ break; default: LOG(L_ERR,"ERROR:cpl_run_script: unknown type node (%d)\n", NODE_TYPE(intr->ip)); goto error; } if (new_ip==CPL_RUNTIME_ERROR) { LOG(L_ERR,"ERROR:cpl_c:cpl_run_script: runtime error\n"); return SCRIPT_RUN_ERROR; } else if (new_ip==CPL_SCRIPT_ERROR) { LOG(L_ERR,"ERROR:cpl_c:cpl_run_script: script error\n"); return SCRIPT_FORMAT_ERROR; } else if (new_ip==DEFAULT_ACTION) { DBG("DEBUG:cpl_c:cpl_run_script: running default action\n"); return run_default(intr); } else if (new_ip==EO_SCRIPT) { DBG("DEBUG:cpl_c:cpl_run_script: script interpretation done!\n"); return SCRIPT_END; } else if (new_ip==CPL_TO_CONTINUE) { DBG("DEBUG:cpl_c:cpl_run_script: done for the moment; waiting " "after signaling!\n"); return SCRIPT_TO_BE_CONTINUED; } /* move to the new instruction */ intr->ip = new_ip; }while(1); error: return SCRIPT_FORMAT_ERROR; } kamailio-4.0.4/obsolete/cpl-c/cpl-06.dtd0000644000000000000000000001634612223032460016317 0ustar rootroot kamailio-4.0.4/obsolete/cpl-c/README0000644000000000000000000002114012223032460015465 0ustar rootroot1. CPL Module Bogdan-Andrei Iancu FhG FOKUS Copyright © 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. Dependencies 1.2.1. SER Modules 1.2.2. External Libraries or Applications 1.3. Parameters 1.3.1. cpl_db (string) 1.3.2. cpl_table (string) 1.3.3. cpl_dtd_file (string) 1.3.4. log_dir (string) 1.3.5. proxy_recurse (int) 1.3.6. proxy_route (int) 1.3.7. nat_flag (int) 1.3.8. lookup_domain (int) 1.4. Functions 1.4.1. cpl_run_script(type,mode) 1.4.2. cpl_process_register() 1.1. Overview cpl-c modules implements a CPL (Call Processing Language) interpreter. Support for uploading/downloading/removing scripts via SIP REGISTER method is implemented. 1.2. Dependencies 1.2.1. SER Modules The following modules must be loaded before this module: * tm. Transaction Manager, used for proxying/forking requests. * sl. StateLess module - used for sending stateless reply when responding to REGISTER request or for sending back error responses. * usrloc. User location module - used for implementing lookup("registration") (adding into location set of the users' contact) 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running SER with this module loaded: * libxml2. This library contains an engine for XML parsing, DTD validation and DOM manipulation. 1.3. Parameters 1.3.1. cpl_db (string) A SQL URL have to be given to the module for knowing where the database containing the table with CPL scripts is locates. If required a user name and password can be specified for allowing the module to connect to the database server. Warning This parameter is mandatory. Example 1. Set cpl_db parameter ... modparam("cpl_c","cpl_db","mysql://user:passwd@host/database") ... 1.3.2. cpl_table (string) Indicates the name of the table that store the CPL scripts. This table must be locate into the database specified by "cpl_db" parameter. For more about the format of the CPL table please see modules/cpl-c/init.mysql. Warning This parameter is mandatory. Example 2. Set cpl_table parameter ... modparam("cpl_c","cpl_table","cpltable") ... 1.3.3. cpl_dtd_file (string) Points to the DTD file describing the CPL grammar. The file name may include also the path to the file. This path can be absolute or relative (be careful the path will be relative to the starting directory of SER). Warning This parameter is mandatory. Example 3. Set cpl_dtd_file parameter ... modparam("cpl_c","cpl_dtd_file","/etc/ser/cpl-06.dtd") ... 1.3.4. log_dir (string) Points to a directory where should be created all the log file generated by the LOG CPL node. A log file per user will be created (on demand) having the name username.log. Note If this parameter is absent, the logging will be disabled without generating error on execution. Example 4. Set log_dir parameter ... modparam("cpl_c","log_dir","/var/log/ser/cpl") ... 1.3.5. proxy_recurse (int) Tells for how many time is allow to have recurse for PROXY CPL node If it has value 2, when doing proxy, only twice the proxy action will be re-triggered by a redirect response; the third time, the proxy execution will end by going on REDIRECTION branch. The recurse feature can be disable by setting this parameter to 0 Default value of this parameter is 0. Example 5. Set proxy_recurse parameter ... modparam("cpl_c","proxy_recurse",2) ... 1.3.6. proxy_route (int) Before doing proxy (forward), a script route can be executed. All modifications made by that route will be reflected only for the current branch. Default value of this parameter is 0 (none). Example 6. Set proxy_route parameter ... modparam("cpl_c","proxy_route",1) ... 1.3.7. nat_flag (int) Sets the flag used for marking calls via NAT. Used by lookup tag when retrieving a contact behind a NAT (this flag will be set). Default value of this parameter is 6. Example 7. Set nat_flag parameter ... modparam("cpl_c","nat_flag",4) ... 1.3.8. lookup_domain (int) Tells if the lookup tag should use or not the domain part when doing user location search. Set it to a non zero value to force also domain matching. Default value of this parameter is 0. Example 8. Set lookup_domain parameter ... modparam("cpl_c","lookup_domain",1) ... 1.4. Functions 1.4.1. cpl_run_script(type,mode) Starts the execution of the CPL script. The user name is fetched from new_uri or requested uri or from To header -in this order- (for incoming execution) or from FROM header (for outgoing execution). Regarding the stateful/stateless message processing, the function is very flexible, being able to run in different modes (see below the"mode" parameter). Normally this function will end script execution. There is no guaranty that the CPL script interpretation ended when ser script ended also (for the same INVITE ;-)) - this can happen when the CPL script does a PROXY and the script interpretation pause after proxying and it will be resume when some reply is received (this can happen in a different process of SER). If the function returns to script, the SIP server should continue with the normal behavior as if no script existed. When some error is returned, the function itself haven't sent any SIP error reply (this can be done from script). Meaning of the parameters is as follows: * type - which part of the script should be run; set it to "incoming" for having the incoming part of script executed (when an INVITE is received) or to "outgoing" for running the outgoing part of script (when a user is generating an INVITE - call). * mode - sets the interpreter mode as stateless/stateful behavior. The following modes are accepted: + IS_STATELESS - the current INVITE has no transaction created yet. All replies (redirection or deny) will be done is a stateless way. The execution will switch to stateful only when proxy is done. So, if the function returns, will be in stateless mode. + IS_STATEFUL - the current INVITE has already a transaction associated. All signaling operations (replies or proxy) will be done in stateful way.So, if the function returns, will be in stateful mode. + FORCE_STATEFUL - the current INVITE has no transaction created yet. All signaling operations will be done is a stateful way (on signaling, the transaction will be created from within the interpreter). So, if the function returns, will be in stateless mode. Note is_stateful is very difficult to manage from the routing script (script processing can continue in stateful mode); is_stateless is the fastest and consumes less resources (transaction is created only if proxying is done), but there is only a minimal protection against retransmissions (since replies are send statelessly); force_stateful is a good compromise - all signaling is done stateful (retransmission protection) and in the same time, if returning to script, it will be in stateless mode (easy to continue the routing script execution) Example 9. cpl_run_script usage ... cpl_run_script("incoming","force_stateful"); ... 1.4.2. cpl_process_register() This function MUST be called only for REGISTER requests. It checks if the current REGISTER request is related or not with CPL script upload/download/ remove. If it is, all the needed operation will be done. For checking if the REGISTER is CPL related, the function looks fist to "Content-Type" header. If it exists and has a the mime type set to "application/cpl+xml" means this is a CPL script upload/remove operation. The distinction between to case is made by looking at "Content-Disposition" header; id its value is "script;action=store", means it's an upload; if it's "script;action=remove", means it's a remove operation; other values are considered to be errors. If no "Content-Type" header is present, the function looks to "Accept" header and if it contains the "*" or "application/cpl-xml" the request it will be consider one for downloading CPL scripts. The functions returns to script only if the REGISTER is not related to CPL. In other case, the function will send by itself the necessary replies (stateless - using sl), including for errors. kamailio-4.0.4/obsolete/cpl-c/cpl_env.h0000644000000000000000000000467312223032460016420 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History * 11-06-1004: created (bogdan) */ #ifndef _CPL_C_ENV_H #define _CPL_C_ENV_H #include "../../str.h" #include "../../usr_avp.h" #include "../../modules/sl/sl.h" #include "../usrloc/usrloc.h" #include "../../modules/tm/tm_load.h" struct cpl_enviroment { char *log_dir; /* dir where the user log should be dumped */ int proxy_recurse; /* numbers of proxy redirection accepted */ int proxy_route; /* script route to be run before proxy */ int nat_flag; /* flag for marking looked up contact as NAT */ int case_sensitive; /* is user part case sensitive ? */ str realm_prefix; /* domain prefix to be ignored */ int cmd_pipe[2]; /* communication pipe with aux. process */ str orig_tz; /* a copy of the original TZ; kept as a null * terminated string in "TZ=value" format; * used only by run_time_switch */ udomain_t* lu_domain; /* domain used for lookup */ int lu_append_branches; /* how many branches lookup should add */ int timer_avp_type; /* specs - type and name - of the timer AVP */ int_str timer_avp; }; struct cpl_functions { struct tm_binds tmb; /* Structure with pointers to tm funcs */ usrloc_api_t ulb; /* Structure with pointers to usrloc funcs */ sl_api_t slb; /* sl module functions */ }; extern struct cpl_enviroment cpl_env; extern struct cpl_functions cpl_fct; #endif kamailio-4.0.4/obsolete/cpl-c/cpl_parser.h0000644000000000000000000000225412223032460017115 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _CPL_PARSER_H #define _CPL_PARSER_H #include "../../str.h" int init_CPL_parser( char* DTD_filename ); int encodeCPL(str *xml, str *bin, str *log); #endif kamailio-4.0.4/obsolete/cpl-c/cpl.c0000644000000000000000000006333012223032460015536 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-03-11: New module interface (janakj) * 2003-03-16: flags export parameter added (janakj) * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) * 2004-06-06 updated to the new DB api (andrei) * 2004-06-14: all global variables merged into cpl_env and cpl_fct; * case_sensitive and realm_prefix added for building AORs - see * build_userhost (bogdan) * 2004-10-09: added process_register_norpl to allow register processing * without sending the reply(bogdan) - based on a patch sent by * Christopher Crawford */ #include #include #include #include #include #include #include #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../sr_module.h" #include "../../str.h" #include "../../ut.h" #include "../../dprint.h" #include "../../data_lump_rpl.h" #include "../../usr_avp.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../parser/parse_content.h" #include "../../parser/parse_disposition.h" #include "../../lib/srdb2/db.h" #include "../../cfg/cfg_struct.h" #include "cpl_run.h" #include "cpl_env.h" #include "cpl_db.h" #include "cpl_loader.h" #include "cpl_parser.h" #include "cpl_nonsig.h" #include "cpl_rpc.h" #include "loc_set.h" #define MAX_PROXY_RECURSE 10 #define MAX_USERHOST_LEN 256 /* modules param variables */ static char *DB_URL = 0; /* database url */ static char *DB_TABLE = 0; /* */ static char *dtd_file = 0; /* name of the DTD file for CPL parser */ static char *lookup_domain = 0; static pid_t aux_process = 0; /* pid of the private aux. process */ static char *timer_avp = 0; /* name of variable timer AVP */ struct cpl_enviroment cpl_env = { 0, /* no cpl logging */ 0, /* recurse proxy level is 0 */ 0, /* no script route to be run before proxy */ 6, /* nat flag */ 0, /* user part is not case sensitive */ {0,0}, /* no domain prefix to be ignored */ {-1,-1}, /* communication pipe to aux_process */ {0,0}, /* original TZ \0 terminated "TZ=value" format */ 0, /* udomain */ 0, /* no branches on lookup */ 0, /* timer avp type */ /*(int_str)*/{ 0 } /* timer avp name/ID */ }; struct cpl_functions cpl_fct; MODULE_VERSION static int cpl_invoke_script (struct sip_msg* msg, char* str, char* str2); static int w_process_register(struct sip_msg* msg, char* str, char* str2); static int w_process_register_norpl(struct sip_msg* msg, char* str,char* str2); static int cpl_process_register(struct sip_msg* msg, int no_rpl); static int fixup_cpl_run_script(void** param, int param_no); static int cpl_init(void); static int cpl_child_init(int rank); static int cpl_exit(void); /* * Exported functions */ static cmd_export_t cmds[] = { {"cpl_run_script",cpl_invoke_script,2,fixup_cpl_run_script,REQUEST_ROUTE}, {"cpl_process_register",w_process_register,0,0,REQUEST_ROUTE}, {"cpl_process_register_norpl",w_process_register_norpl,0,0,REQUEST_ROUTE}, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"cpl_db", PARAM_STRING, &DB_URL }, {"cpl_table", PARAM_STRING, &DB_TABLE }, {"cpl_dtd_file", PARAM_STRING, &dtd_file }, {"proxy_recurse", PARAM_INT, &cpl_env.proxy_recurse }, {"proxy_route", PARAM_INT, &cpl_env.proxy_route }, {"nat_flag", PARAM_INT, &cpl_env.nat_flag }, {"log_dir", PARAM_STRING, &cpl_env.log_dir }, {"case_sensitive", PARAM_INT, &cpl_env.case_sensitive }, {"realm_prefix", PARAM_STR, &cpl_env.realm_prefix }, {"lookup_domain", PARAM_STRING, &lookup_domain }, {"lookup_append_branches", PARAM_INT, &cpl_env.lu_append_branches}, {"timer_avp", PARAM_STRING, &timer_avp }, {0, 0, 0} }; struct module_exports exports = { "cpl-c", cmds, /* Exported functions */ cpl_rpc_methods, /* RPC methods */ params, /* Exported parameters */ cpl_init, /* Module initialization function */ (response_function) 0, (destroy_function) cpl_exit, 0, (child_init_function) cpl_child_init /* per-child init function */ }; static int fixup_cpl_run_script(void** param, int param_no) { long flag; if (param_no==1) { if (!strcasecmp( "incoming", *param)) flag = CPL_RUN_INCOMING; else if (!strcasecmp( "outgoing", *param)) flag = CPL_RUN_OUTGOING; else { LOG(L_ERR,"ERROR:fixup_cpl_run_script: script directive \"%s\"" " unknown!\n",(char*)*param); return E_UNSPEC; } pkg_free(*param); *param=(void*)flag; return 0; } else if (param_no==2) { if ( !strcasecmp("is_stateless", *param) ) { flag = 0; } else if ( !strcasecmp("is_stateful", *param) ) { flag = CPL_IS_STATEFUL; } else if ( !strcasecmp("force_stateful", *param) ) { flag = CPL_FORCE_STATEFUL; } else { LOG(L_ERR,"ERROR:fixup_cpl_run_script: flag \"%s\" (second param)" " unknown!\n",(char*)*param); return E_UNSPEC; } pkg_free(*param); *param=(void*)flag; } return 0; } static int cpl_init(void) { bind_usrloc_t bind_usrloc; load_tm_f load_tm; struct stat stat_t; char *ptr; int val; str foo; LOG(L_INFO,"CPL - initializing\n"); /* check the module params */ if (DB_URL==0) { LOG(L_CRIT,"ERROR:cpl_init: mandatory parameter \"cpl_db\" " "found empty\n"); goto error; } if (DB_TABLE==0) { LOG(L_CRIT,"ERROR:cpl_init: mandatory parameter \"cpl_table\" " "found empty\n"); goto error; } if (cpl_env.proxy_recurse>MAX_PROXY_RECURSE) { LOG(L_CRIT,"ERROR:cpl_init: value of proxy_recurse param (%d) exceeds " "the maximum safety value (%d)\n", cpl_env.proxy_recurse,MAX_PROXY_RECURSE); goto error; } /* fix the timer_avp name */ if (timer_avp) { foo.s = timer_avp; foo.len = strlen(foo.s); if (parse_avp_spec(&foo,&cpl_env.timer_avp_type,&cpl_env.timer_avp,0)<0){ LOG(L_CRIT,"ERROR:cpl_init: invalid timer AVP specs \"%s\"\n", timer_avp); goto error; } if (cpl_env.timer_avp_type&AVP_NAME_STR && cpl_env.timer_avp.s.s==foo.s) { cpl_env.timer_avp.s = foo; } } if (dtd_file==0) { LOG(L_CRIT,"ERROR:cpl_init: mandatory parameter \"cpl_dtd_file\" " "found empty\n"); goto error; } else { /* check if the dtd file exists */ if (stat( dtd_file, &stat_t)==-1) { LOG(L_ERR,"ERROR:cpl_init: checking file \"%s\" status failed;" " stat returned %s\n",dtd_file,strerror(errno)); goto error; } if ( !S_ISREG( stat_t.st_mode ) ) { LOG(L_ERR,"ERROR:cpl_init: dir \"%s\" is not a regular file!\n", dtd_file); goto error; } if (access( dtd_file, R_OK )==-1) { LOG(L_ERR,"ERROR:cpl_init: checking file \"%s\" for permissions " "failed; access returned %s\n",dtd_file,strerror(errno)); goto error; } } if (cpl_env.log_dir==0) { LOG(L_INFO,"INFO:cpl_init: log_dir param found void -> logging " " disabled!\n"); } else { if ( strlen(cpl_env.log_dir)>MAX_LOG_DIR_SIZE ) { LOG(L_ERR,"ERROR:cpl_init: dir \"%s\" has a too long name :-(!\n", cpl_env.log_dir); goto error; } /* check if the dir exists */ if (stat( cpl_env.log_dir, &stat_t)==-1) { LOG(L_ERR,"ERROR:cpl_init: checking dir \"%s\" status failed;" " stat returned %s\n",cpl_env.log_dir,strerror(errno)); goto error; } if ( !S_ISDIR( stat_t.st_mode ) ) { LOG(L_ERR,"ERROR:cpl_init: dir \"%s\" is not a directory!\n", cpl_env.log_dir); goto error; } if (access( cpl_env.log_dir, R_OK|W_OK )==-1) { LOG(L_ERR,"ERROR:cpl_init: checking dir \"%s\" for permissions " "failed; access returned %s\n", cpl_env.log_dir, strerror(errno)); goto error; } } /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) { LOG(L_ERR, "ERROR:cpl_c:cpl_init: cannot import load_tm\n"); goto error; } /* let the auto-loading function load all TM stuff */ if (load_tm( &(cpl_fct.tmb) )==-1) goto error; /* bind the SL API */ if (sl_load_api(&cpl_fct.slb)!=0) { LM_ERR("cannot bind to SL API\n"); return -1; } /* bind to usrloc module if requested */ if (lookup_domain) { /* import all usrloc functions */ bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0); if (!bind_usrloc) { LOG(L_ERR, "ERROR:cpl_c:cpl_init: Can't bind usrloc\n"); goto error; } if (bind_usrloc( &(cpl_fct.ulb) ) < 0) { LOG(L_ERR, "ERROR:cpl_c:cpl_init: importing usrloc failed\n"); goto error; } /* convert lookup_domain from char* to udomain_t* pointer */ if (cpl_fct.ulb.register_udomain( lookup_domain, &cpl_env.lu_domain) < 0) { LOG(L_ERR, "ERROR:cpl_c:cpl_init: Error while registering domain " "<%s>\n",lookup_domain); goto error; } } else { LOG(L_NOTICE,"NOTICE:cpl_init: no lookup_domain given -> disable " " lookup node\n"); } /* build a pipe for sending commands to aux process */ if ( pipe( cpl_env.cmd_pipe )==-1 ) { LOG(L_CRIT,"ERROR:cpl_init: cannot create command pipe: %s!\n", strerror(errno) ); goto error; } /* set the writing non blocking */ if ( (val=fcntl(cpl_env.cmd_pipe[1], F_GETFL, 0))<0 ) { LOG(L_ERR,"ERROR:cpl_init: getting flags from pipe[1] failed: fcntl " "said %s!\n",strerror(errno)); goto error; } if ( fcntl(cpl_env.cmd_pipe[1], F_SETFL, val|O_NONBLOCK) ) { LOG(L_ERR,"ERROR:cpl_init: setting flags to pipe[1] failed: fcntl " "said %s!\n",strerror(errno)); goto error; } /* init the CPL parser */ if (init_CPL_parser( dtd_file )!=1 ) { LOG(L_ERR,"ERROR:cpl_init: init_CPL_parser failed!\n"); goto error; } /* make a copy of the original TZ env. variable */ ptr = getenv("TZ"); cpl_env.orig_tz.len = 3/*"TZ="*/ + (ptr?(strlen(ptr)+1):0); if ( (cpl_env.orig_tz.s=shm_malloc( cpl_env.orig_tz.len ))==0 ) { LOG(L_ERR,"ERROR:cpl_init: no more shm mem. for saving TZ!\n"); goto error; } memcpy(cpl_env.orig_tz.s,"TZ=",3); if (ptr) strcpy(cpl_env.orig_tz.s+3,ptr); /* convert realm_prefix from string null terminated to str */ if (cpl_env.realm_prefix.s) { /* convert the realm_prefix to lower cases */ strlower( &cpl_env.realm_prefix ); } /* Register a child process that will keep updating * its local configuration */ cfg_register_child(1); return 0; error: return -1; } static int cpl_child_init(int rank) { pid_t pid; /* don't do anything for main process and TCP manager process */ if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN) return 0; /* only child 1 will fork the aux process */ if (rank==1) { pid = fork(); if (pid==-1) { LOG(L_CRIT,"ERROR:cpl_child_init(%d): cannot fork: %s!\n", rank, strerror(errno)); goto error; } else if (pid==0) { /* I'm the child */ /* initialize the config framework */ if (cfg_child_init()) goto error; cpl_aux_process( cpl_env.cmd_pipe[0], cpl_env.log_dir); } else { LOG(L_INFO,"INFO:cpl_child_init(%d): I just gave birth to a child!" " I'm a PARENT!!\n",rank); /* I'm the parent -> remember the pid */ aux_process = pid; } } return cpl_db_init(DB_URL, DB_TABLE); error: return -1; } static int cpl_exit(void) { /* free the TZ orig */ if (cpl_env.orig_tz.s) shm_free(cpl_env.orig_tz.s); /* if still running, stop the aux process */ if (!aux_process) { LOG(L_INFO,"INFO:cpl_c:cpl_exit: aux process hasn't been created -> " "nothing to kill :-(\n"); } else { /* kill the auxiliary process */ if (kill( aux_process, SIGKILL)!=0) { if (errno==ESRCH) { LOG(L_INFO,"INFO:cpl_c:cpl_exit: seems that my child is " "already dead! :-((\n"); } else { LOG(L_ERR,"ERROR:cpl_c:cpl_exit: killing the aux. process " "failed! kill said: %s\n",strerror(errno)); return -1; } } else { LOG(L_INFO,"INFO:cl_c:cpl_exit: I have blood on my hands!! I just" " killed my own child!"); } } return 0; } #define BUILD_UH_SHM (1<<0) #define BUILD_UH_ADDSIP (1<<1) static inline int build_userhost(struct sip_uri *uri, str *uh, int flg) { static char buf[MAX_USERHOST_LEN]; unsigned char do_strip; char *p; int i; /* do we need to strip realm prefix? */ do_strip = 0; if (cpl_env.realm_prefix.len && cpl_env.realm_prefix.lenhost.len) { for( i=cpl_env.realm_prefix.len-1 ; i>=0 ; i-- ) if ( cpl_env.realm_prefix.s[i]!=((uri->host.s[i])|(0x20)) ) break; if (i==-1) do_strip = 1; } /* calculate the len (without terminating \0) */ uh->len = 4*((flg&BUILD_UH_ADDSIP)!=0) + uri->user.len + 1 + uri->host.len - do_strip*cpl_env.realm_prefix.len; if (flg&BUILD_UH_SHM) { uh->s = (char*)shm_malloc( uh->len + 1 ); if (!uh->s) { LOG(L_ERR,"ERROR:cpl-c:build_userhost: no more shm memory.\n"); return -1; } } else { uh->s = buf; if ( uh->len > MAX_USERHOST_LEN ) { LOG(L_ERR,"ERROR:cpl-c:build_userhost: user+host longer than %d\n", MAX_USERHOST_LEN); return -1; } } /* build user@host */ p = uh->s; if (flg&BUILD_UH_ADDSIP) { memcpy( uh->s, "sip:", 4); p += 4; } /* user part */ if (cpl_env.case_sensitive) { memcpy( p, uri->user.s, uri->user.len); p += uri->user.len; } else { for(i=0;iuser.len;i++) *(p++) = (0x20)|(uri->user.s[i]); } *(p++) = '@'; /* host part in lower cases */ for( i=do_strip*cpl_env.realm_prefix.len ; i< uri->host.len ; i++ ) *(p++) = (0x20)|(uri->host.s[i]); *(p++) = 0; /* sanity check */ if (p-uh->s!=uh->len+1) { LOG(L_CRIT,"BUG:cpl-c:build_userhost: buffer overflow l=%d,w=%ld\n", uh->len,(long)(p-uh->s)); return -1; } return 0; } static inline int get_dest_user(struct sip_msg *msg, str *uh, int flg) { struct sip_uri uri; /* get the user_name from new_uri/RURI/To */ DBG("DEBUG:cpl-c:get_dest_user: trying to get user from new_uri\n"); if ( !msg->new_uri.s || parse_uri( msg->new_uri.s,msg->new_uri.len,&uri)==-1 || !uri.user.len ) { DBG("DEBUG:cpl-c:get_dest_user: trying to get user from R_uri\n"); if ( parse_uri( msg->first_line.u.request.uri.s, msg->first_line.u.request.uri.len ,&uri)==-1 || !uri.user.len ) { DBG("DEBUG:cpl-c:get_dest_user: trying to get user from To\n"); if ( (!msg->to&&( (parse_headers(msg,HDR_TO_F,0)==-1) || !msg->to)) || parse_uri( get_to(msg)->uri.s, get_to(msg)->uri.len, &uri)==-1 || !uri.user.len) { LOG(L_ERR,"ERROR:cpl-c:get_dest_user: unable to extract user" " name from RURI or To header!\n"); return -1; } } } return build_userhost( &uri, uh, flg); } static inline int get_orig_user(struct sip_msg *msg, str *uh, int flg) { struct to_body *from; struct sip_uri uri; /* if it's outgoing -> get the user_name from From */ /* parsing from header */ DBG("DEBUG:cpl-c:get_orig_user: trying to get user from From\n"); if ( parse_from_header( msg )==-1 ) { LOG(L_ERR,"ERROR:cpl-c:get_orig_user: unable to extract URI " "from FROM header\n"); return -1; } from = (struct to_body*)msg->from->parsed; /* parse the extracted uri from From */ if (parse_uri( from->uri.s, from->uri.len, &uri)||!uri.user.len) { LOG(L_ERR,"ERROR:cpl-c:get_orig_user: unable to extract user name " "from URI (From header)\n"); return -1; } return build_userhost( &uri, uh, flg); } /* Params: * str1 - as unsigned int - can be CPL_RUN_INCOMING or CPL_RUN_OUTGOING * str2 - as unsigned int - flags regarding state(less)|(ful) */ static int cpl_invoke_script(struct sip_msg* msg, char* str1, char* str2) { struct cpl_interpreter *cpl_intr; str user; str loc; str script; /* get the user_name */ if ( ((unsigned long)str1)&CPL_RUN_INCOMING ) { /* if it's incoming -> get the destination user name */ if (get_dest_user( msg, &user, BUILD_UH_SHM)==-1) goto error0; } else { /* if it's outgoing -> get the origin user name */ if (get_orig_user( msg, &user, BUILD_UH_SHM)==-1) goto error0; } /* get the script for this user */ if (get_user_script(&user, &script, 1)==-1) goto error1; /* has the user a non-empty script? if not, return normally, allowing ser to * continue its script */ if ( !script.s || !script.len ) { shm_free(user.s); return 1; } /* build a new script interpreter */ if ( (cpl_intr=new_cpl_interpreter(msg,&script))==0 ) goto error2; /* set the flags */ cpl_intr->flags =(unsigned int)((unsigned long)str1)|((unsigned long)str2); /* attache the user */ cpl_intr->user = user; /* for OUTGOING we need also the destination user for init. with him * the location set */ if ( ((unsigned long)str1)&CPL_RUN_OUTGOING ) { if (get_dest_user( msg, &loc,BUILD_UH_ADDSIP)==-1) goto error3; if (add_location( &(cpl_intr->loc_set), &loc,10,CPL_LOC_DUPL)==-1) goto error3; } /* since the script interpretation can take some time, it will be better to * send a 100 back to prevent the UAC to retransmit if ( cpl_tmb.t_reply( msg, (int)100, "Running cpl script" )!=1 ) { LOG(L_ERR,"ERROR:cpl_invoke_script: unable to send 100 reply!\n"); goto error3; } * this should be done from script - it's much sooner ;-) */ /* run the script */ switch (cpl_run_script( cpl_intr )) { case SCRIPT_DEFAULT: free_cpl_interpreter( cpl_intr ); return 1; /* execution of ser's script will continue */ case SCRIPT_END: free_cpl_interpreter( cpl_intr ); case SCRIPT_TO_BE_CONTINUED: return 0; /* break the SER script */ case SCRIPT_RUN_ERROR: case SCRIPT_FORMAT_ERROR: goto error3; } return 1; error3: free_cpl_interpreter( cpl_intr ); return -1; error2: shm_free(script.s); error1: shm_free(user.s); error0: return -1; } #define CPL_SCRIPT "script" #define CPL_SCRIPT_LEN (sizeof(CPL_SCRIPT)-1) #define ACTION_PARAM "action" #define ACTION_PARAM_LEN (sizeof(ACTION_PARAM)-1) #define STORE_ACTION "store" #define STORE_ACTION_LEN (sizeof(STORE_ACTION)-1) #define REMOVE_ACTION "remove" #define REMOVE_ACTION_LEN (sizeof(REMOVE_ACTION)-1) #define REMOVE_SCRIPT 0xcaca #define STORE_SCRIPT 0xbebe #define CONTENT_TYPE_HDR ("Content-Type: application/cpl-xml"CRLF) #define CONTENT_TYPE_HDR_LEN (sizeof(CONTENT_TYPE_HDR)-1) struct cpl_error { int err_code; char *err_msg; }; static struct cpl_error bad_req = {400,"Bad request"}; static struct cpl_error intern_err = {500,"Internal server error"}; static struct cpl_error bad_cpl = {400,"Bad CPL script"}; static struct cpl_error *cpl_err = &bad_req; static inline int do_script_action(struct sip_msg *msg, int action) { str body = STR_NULL; str user = STR_NULL; str bin = STR_NULL; str log = STR_NULL; /* content-length (if present) */ if ( !msg->content_length && ((parse_headers(msg, HDR_CONTENTLENGTH_F, 0)==-1) || !msg->content_length) ) { LOG(L_ERR,"ERROR:cpl-c:do_script_action: no Content-Length " "hdr found!\n"); goto error; } body.len = get_content_length( msg ); /* get the user name */ if (get_dest_user( msg, &user, 0)==-1) goto error; /* we have the script and the user */ switch (action) { case STORE_SCRIPT : /* check the len -> it must not be 0 */ if (body.len==0) { LOG(L_ERR,"ERROR:cpl-c:do_script_action: 0 content-len found " "for store\n"); goto error_1; } /* get the message's body */ body.s = get_body( msg ); if (body.s==0) { LOG(L_ERR,"ERROR:cpl-c:do_script_action: cannot extract " "body from msg!\n"); goto error_1; } /* now compile the script and place it into database */ /* get the binary coding for the XML file */ if ( encodeCPL( &body, &bin, &log)!=1) { cpl_err = &bad_cpl; goto error_1; } /* write both the XML and binary formats into database */ if (write_to_db(user.s, &body, &bin)!=1) { cpl_err = &intern_err; goto error_1; } break; case REMOVE_SCRIPT: /* check the len -> it must be 0 */ if (body.len!=0) { LOG(L_ERR,"ERROR:cpl-c:do_script_action: non-0 content-len " "found for remove\n"); goto error_1; } /* remove the script for the user */ if (rmv_from_db(user.s)!=1) { cpl_err = &intern_err; goto error_1; } break; } if (log.s) pkg_free( log.s ); return 0; error_1: if (log.s) pkg_free( log.s ); error: return -1; } static inline int do_script_download(struct sip_msg *msg) { str user = STR_NULL; str script = STR_NULL; /* get the destination user name */ if (get_dest_user( msg, &user, 0)==-1) goto error; /* get the user's xml script from the database */ if (get_user_script(&user, &script, 0)==-1) goto error; /* add a lump with content-type hdr */ if (add_lump_rpl( msg, CONTENT_TYPE_HDR, CONTENT_TYPE_HDR_LEN, LUMP_RPL_HDR)==0) { LOG(L_ERR,"ERROR:cpl-c:do_script_download: cannot build hdr lump\n"); cpl_err = &intern_err; goto error; } if (script.s!=0) { /*DBG("script len=%d\n--------\n%.*s\n--------\n", script.len, script.len, script.s);*/ /* user has a script -> add a body lump */ if ( add_lump_rpl( msg, script.s, script.len, LUMP_RPL_BODY)==0) { LOG(L_ERR,"ERROR:cpl-c:do_script_download: cannot build " "body lump\n"); cpl_err = &intern_err; goto error; } /* build_lump_rpl duplicates the added text, so free the original */ shm_free( script.s ); } return 0; error: if (script.s) shm_free(script.s); return -1; } static int w_process_register(struct sip_msg* msg, char* str, char* str2) { return cpl_process_register( msg, 0); } static int w_process_register_norpl(struct sip_msg* msg, char* str,char* str2) { return cpl_process_register( msg, 1); } static int cpl_process_register(struct sip_msg* msg, int no_rpl) { struct disposition *disp; struct disposition_param *param; int ret; int mime; int *mimes; /* make sure that is a REGISTER ??? */ /* here should be the CONTACT- hack */ /* is there a CONTENT-TYPE hdr ? */ mime = parse_content_type_hdr( msg ); if (mime==-1) goto error; /* check the mime type */ DBG("DEBUG:cpl_process_register: Content-Type mime found %u, %u\n", mime>>16,mime&0x00ff); if ( mime && mime==(TYPE_APPLICATION<<16)+SUBTYPE_CPLXML ) { /* can be an upload or remove -> check for the content-purpose and * content-action headers */ DBG("DEBUG:cpl_process_register: carrying CPL -> look at " "Content-Disposition\n"); if (parse_content_disposition( msg )!=0) { LOG(L_ERR,"ERROR:cpl_process_register: Content-Disposition missing " "or corrupted\n"); goto error; } disp = get_content_disposition(msg); print_disposition( disp ); /* just for DEBUG */ /* check if the type of disposition is SCRIPT */ if (disp->type.len!=CPL_SCRIPT_LEN || strncasecmp(disp->type.s,CPL_SCRIPT,CPL_SCRIPT_LEN) ) { LOG(L_ERR,"ERROR:cpl_process_register: bogus message - Content-Type" "says CPL_SCRIPT, but Content-Disposition something else\n"); goto error; } /* disposition type is OK -> look for action parameter */ for(param=disp->params;param;param=param->next) { if (param->name.len==ACTION_PARAM_LEN && !strncasecmp(param->name.s,ACTION_PARAM,ACTION_PARAM_LEN)) break; } if (param==0) { LOG(L_ERR,"ERROR:cpl_process_register: bogus message - " "Content-Disposition has no action param\n"); goto error; } /* action param found -> check its value: store or remove */ if (param->body.len==STORE_ACTION_LEN && !strncasecmp( param->body.s, STORE_ACTION, STORE_ACTION_LEN)) { /* it's a store action -> get the script from body message and store * it into database (CPL and BINARY format) */ if (do_script_action( msg, STORE_SCRIPT)==-1) goto error; } else if (param->body.len==REMOVE_ACTION_LEN && !strncasecmp( param->body.s, REMOVE_ACTION, REMOVE_ACTION_LEN)) { /* it's a remove action -> remove the script from database */ if (do_script_action( msg, REMOVE_SCRIPT)==-1) goto error; } else { LOG(L_ERR,"ERROR:cpl_process_register: unknown action <%.*s>\n", param->body.len,param->body.s); goto error; } /* do I have to send to reply? */ if (no_rpl) goto resume_script; /* send a 200 OK reply back */ cpl_fct.slb.zreply( msg, 200, "OK"); /* I send the reply and I don't want to return to script execution, so * I return 0 to do break */ goto stop_script; } /* is there an ACCEPT hdr ? */ if ( (ret=parse_accept_hdr(msg))==-1) goto error; if (ret==0 || (mimes=get_accept(msg))==0 ) /* accept header not present or no mimes found */ goto resume_script; /* looks if the REGISTER accepts cpl-xml or * */ while (*mimes) { DBG("DEBUG: accept mime found %u, %u\n", (*mimes)>>16,(*mimes)&0x00ff); if (*mimes==(TYPE_ALL<<16)+SUBTYPE_ALL || *mimes==(TYPE_APPLICATION<<16)+SUBTYPE_CPLXML ) break; mimes++; } if (*mimes==0) /* no accept mime that matched cpl */ goto resume_script; /* get the user name from msg, retrieve the script from db * and appended to reply */ if (do_script_download( msg )==-1) goto error; /* do I have to send to reply? */ if (no_rpl) goto resume_script; /* send a 200 OK reply back */ cpl_fct.slb.zreply( msg, 200, "OK"); stop_script: return 0; resume_script: return 1; error: /* send a error reply back */ cpl_fct.slb.zreply( msg, cpl_err->err_code, cpl_err->err_msg); /* I don't want to return to script execution, so I return 0 to do break */ return 0; } kamailio-4.0.4/obsolete/cpl-c/sub_list.c0000644000000000000000000000342512223032460016603 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* History: * -------- * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei) */ #include #include #include "../../mem/mem.h" #include "sub_list.h" struct node* append_to_list(struct node *head, char *offset, char *name) { struct node *new_node; new_node = pkg_malloc(sizeof(struct node)); if (!new_node) return 0; new_node->offset = offset; new_node->name = name; new_node->next = head; return new_node; } char* search_the_list(struct node *head, char *name) { struct node *n; n = head; while (n) { if (strcasecmp(n->name,name)==0) return n->offset; n = n->next; } return 0; } void delete_list(struct node* head) { struct node *n; ; while (head) { n=head->next; pkg_free(head); head = n; } } kamailio-4.0.4/obsolete/cpl-c/cpl_log.h0000644000000000000000000000303212223032460016375 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2003-09-22: created (bogdan) * */ #ifndef _CPL_LOG_H_ #define _CPL_LOG_H_ #include #include "../../str.h" #define MAX_LOG_NR 64 #define MSG_ERR "Error: " #define MSG_ERR_LEN (sizeof(MSG_ERR)-1) #define MSG_WARN "Warning: " #define MSG_WARN_LEN (sizeof(MSG_WARN)-1) #define MSG_NOTE "Notice: " #define MSG_NOTE_LEN (sizeof(MSG_NOTE)-1) #define LF "\n" #define LF_LEN (1) void reset_logs(); void append_log( int nr, ...); void compile_logs( str *log); #endif kamailio-4.0.4/obsolete/cpl-c/cpl_time.h0000644000000000000000000000737412223032460016567 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2003-06-24: file imported from tmrec (bogdan) * 2003-xx-xx: file Created (daniel) */ #ifndef _CPL_TIME_H_ #define _CPL_TIME_H_ /************************ imported from "ac_tm.h" ***************************/ #include /* USE_YWEEK_U -- Sunday system - see strftime %U * USE_YWEEK_V -- ISO 8601 - see strftime %V * USE_YWEEK_W -- Monday system - see strftime %W */ #ifndef USE_YWEEK_U # ifndef USE_YWEEK_V # ifndef USE_YWEEK_W # define USE_YWEEK_W # endif # endif #endif #define is_leap_year(yyyy) ((((yyyy)%400))?(((yyyy)%100)?(((yyyy)%4)?0:1):0):1) typedef struct _ac_maxval { int yweek; int yday; int ywday; int mweek; int mday; int mwday; } ac_maxval_t, *ac_maxval_p; typedef struct _ac_tm { time_t time; struct tm t; int mweek; int yweek; int ywday; int mwday; ac_maxval_p mv; } ac_tm_t, *ac_tm_p; ac_tm_p ac_tm_new(); int ac_tm_set(ac_tm_p, struct tm*); int ac_tm_set_time(ac_tm_p, time_t); int ac_tm_reset(ac_tm_p); int ac_tm_free(ac_tm_p); int ac_get_mweek(struct tm*); int ac_get_yweek(struct tm*); ac_maxval_p ac_get_maxval(ac_tm_p); int ac_get_wkst(); int ac_print(ac_tm_p); /************************ imported from "tmrec.h" ***************************/ #define FREQ_NOFREQ 0 #define FREQ_YEARLY 1 #define FREQ_MONTHLY 2 #define FREQ_WEEKLY 3 #define FREQ_DAILY 4 #define WDAY_SU 0 #define WDAY_MO 1 #define WDAY_TU 2 #define WDAY_WE 3 #define WDAY_TH 4 #define WDAY_FR 5 #define WDAY_SA 6 #define WDAY_NU 7 #define TSW_TSET 1 #define TSW_RSET 2 typedef struct _tr_byxxx { int nr; int *xxx; int *req; } tr_byxxx_t, *tr_byxxx_p; typedef struct _tmrec { time_t dtstart; struct tm ts; time_t dtend; time_t duration; time_t until; int freq; int interval; tr_byxxx_p byday; tr_byxxx_p bymday; tr_byxxx_p byyday; tr_byxxx_p bymonth; tr_byxxx_p byweekno; int wkst; } tmrec_t, *tmrec_p; typedef struct _tr_res { int flag; time_t rest; } tr_res_t, *tr_res_p; tr_byxxx_p tr_byxxx_new(); int tr_byxxx_init(tr_byxxx_p, int); int tr_byxxx_free(tr_byxxx_p); tmrec_p tmrec_new(); int tmrec_free(tmrec_p); int tr_parse_dtstart(tmrec_p, char*); int tr_parse_dtend(tmrec_p, char*); int tr_parse_duration(tmrec_p, char*); int tr_parse_until(tmrec_p, char*); int tr_parse_freq(tmrec_p, char*); int tr_parse_interval(tmrec_p, char*); int tr_parse_byday(tmrec_p, char*); int tr_parse_bymday(tmrec_p, char*); int tr_parse_byyday(tmrec_p, char*); int tr_parse_bymonth(tmrec_p, char*); int tr_parse_byweekno(tmrec_p, char*); int tr_parse_wkst(tmrec_p, char*); int tr_print(tmrec_p); time_t ic_parse_datetime(char*,struct tm*); time_t ic_parse_duration(char*); tr_byxxx_p ic_parse_byday(char*); tr_byxxx_p ic_parse_byxxx(char*); int ic_parse_wkst(char*); int check_tmrec(tmrec_p, ac_tm_p, tr_res_p); #endif kamailio-4.0.4/obsolete/cpl-c/loc_set.h0000644000000000000000000000770212223032460016416 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _CPL_LOC_SET_H_ #define _CPL_LOC_SET_H_ #include #include #include #include #include "../../mem/shm_mem.h" #include "../../str.h" #include "../../dprint.h" #define CPL_LOC_DUPL (1<<0) #define CPL_LOC_NATED (1<<1) struct location { struct address { str uri; unsigned int priority; }addr; int flags; struct location *next; }; static inline void free_location( struct location *loc) { shm_free( loc ); } /* insert a new location into the set maintaining order by the prio val - the * list starts with the smallest prio! * For locations having the same prio val, the adding order will be kept */ static inline int add_location(struct location **loc_set, str *uri, unsigned int prio, int flags) { struct location *loc; struct location *foo, *bar; loc = (struct location*)shm_malloc( sizeof(struct location)+((flags&CPL_LOC_DUPL)?uri->len+1:0) ); if (!loc) { LOG(L_ERR,"ERROR:add_location: no more free shm memory!\n"); return -1; } if (flags&CPL_LOC_DUPL) { loc->addr.uri.s = ((char*)loc)+sizeof(struct location); memcpy(loc->addr.uri.s,uri->s,uri->len); loc->addr.uri.s[uri->len] = 0; } else { loc->addr.uri.s = uri->s; } loc->addr.uri.len = uri->len; loc->addr.priority = prio; loc->flags = flags; /* find the proper place for the new location */ foo = *loc_set; bar = 0; while(foo && foo->addr.priority<=prio) { bar = foo; foo = foo->next; } if (!bar) { /* insert at the beginning */ loc->next = *loc_set; *loc_set = loc; } else { /* insert after bar, before foo */ loc->next = foo; bar->next = loc; } return 0; } static inline void remove_location(struct location **loc_set, char *uri_s, int uri_len) { struct location *loc = *loc_set; struct location *prev_loc = 0; for( ; loc ; prev_loc=loc,loc=loc->next ) { if (loc->addr.uri.len==uri_len && !strncasecmp(loc->addr.uri.s,uri_s,uri_len) ) break; } if (loc) { DBG("DEBUG:remove_location: removing from loc_set <%.*s>\n", uri_len,uri_s); if (prev_loc) prev_loc->next=loc->next; else (*loc_set)=loc->next; shm_free( loc ); } else { DBG("DEBUG:remove_location: no matching in loc_set for <%.*s>\n", uri_len,uri_s); } } static inline struct location *remove_first_location(struct location **loc_set) { struct location *loc; if (!*loc_set) return 0; loc = *loc_set; *loc_set = (*loc_set)->next; loc->next = 0; DBG("DEBUG:remove_first_location: removing <%.*s>\n", loc->addr.uri.len,loc->addr.uri.s); return loc; } static inline void empty_location_set(struct location **loc_set) { struct location *loc; while (*loc_set) { loc = (*loc_set)->next; shm_free(*loc_set); *loc_set = loc; } *loc_set = 0; } static inline void print_location_set(struct location *loc_set) { while (loc_set) { DBG("DEBUG:cpl_c:print_loc_set: uri=<%s> q=%d\n",loc_set->addr.uri.s, loc_set->addr.priority); loc_set=loc_set->next; } } #endif kamailio-4.0.4/obsolete/cpl-c/cpl_proxy.h0000644000000000000000000004133112223032460017001 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-07-29: file created (bogdan) * 2004-06-14: flag CPL_IS_STATEFUL is set now immediately after the * transaction is created (bogdan) */ #include "../../modules/tm/h_table.h" #include "../../parser/contact/parse_contact.h" #define duplicate_str( _orig_ , _new_ ) \ do {\ (_new_) = (str*)shm_malloc(sizeof(str)+(_orig_)->len);\ if (!(_new_)) goto mem_error;\ (_new_)->len = (_orig_)->len;\ (_new_)->s = (char*)((_new_))+sizeof(str);\ memcpy((_new_)->s,(_orig_)->s,(_orig_)->len);\ } while(0) #define search_and_duplicate_hdr( _intr_ , _field_ , _name_ , _sfoo_ ) \ do {\ if (!(_intr_)->_field_) {\ if (!(_intr_)->msg->_field_) { \ if (parse_headers((_intr_)->msg,_name_,0)==-1) {\ LOG(L_ERR,"ERROR:run_proxy: bad %llx hdr\n",_name_);\ goto runtime_error;\ } else if ( !(_intr_)->msg->_field_) {\ (_intr_)->_field_ = STR_NOT_FOUND;\ } else {\ (_sfoo_) = &((_intr_)->msg->_field_->body);\ duplicate_str( (_sfoo_) , (_intr_)->_field_ );\ }\ } else {\ (_sfoo_) = &((_intr_)->msg->_field_->body);\ duplicate_str( (_sfoo_) , (_intr_)->_field_ );\ }\ } else {\ (_sfoo_) = (_intr_)->_field_;\ duplicate_str( (_sfoo_) , (_intr_)->_field_ );\ }\ }while(0) static inline int parse_q(str *q, unsigned int *prio) { if (q->s[0]=='0') *prio=0; else if (q->s[0]=='1') *prio=10; else goto error; if (q->s[1]!='.') goto error; if (q->s[2]<'0' || q->s[2]>'9') goto error; *prio += q->s[2] - '0'; if (*prio>10) goto error; return 0; error: LOG(L_ERR,"ERROR:cpl-c:parse_q:bad q param <%.*s>\n",q->len,q->s); return -1; } static inline int add_contacts_to_loc_set(struct sip_msg* msg, struct location **loc_set) { struct sip_uri uri; struct contact *contacts; unsigned int prio; /* we need to have the contact header */ if (msg->contact==0) { /* find and parse the Contact header */ if ((parse_headers(msg, HDR_CONTACT_F, 0)==-1) || (msg->contact==0) ) { LOG(L_ERR,"ERROR:cpl-c:add_contacts_to_loc_set: error parsing or " "no Contact hdr found!\n"); goto error; } } /* extract from contact header the all the addresses */ if (parse_contact( msg->contact )!=0) { LOG(L_ERR,"ERROR:cpl-c:add_contacts_to_loc_set: unable to parse " "Contact hdr!\n"); goto error; } /* in contact hdr, in parsed attr, we should have a list of contacts */ if ( msg->contact->parsed ) { contacts = ((struct contact_body*)msg->contact->parsed)->contacts; for( ; contacts ; contacts=contacts->next) { /* check if the contact is a valid sip uri */ if (parse_uri( contacts->uri.s, contacts->uri.len , &uri)!=0) { continue; } /* convert the q param to int value (if any) */ if (contacts->q) { if (parse_q( &(contacts->q->body), &prio )!=0) continue; } else { prio = 10; /* set default to minimum */ } /* add the uri to location set */ if (add_location( loc_set, &contacts->uri,prio, CPL_LOC_DUPL)!=0) { LOG(L_ERR,"ERROR:cpl-c:add_contacts_to_loc_set: unable to add " "<%.*s>\n",contacts->uri.len,contacts->uri.s); } } } return 0; error: return -1; } static void reply_callback( struct cell* t, int type, struct tmcb_params* ps) { struct cpl_interpreter *intr = (struct cpl_interpreter*)(*(ps->param)); struct location *loc = 0; int rez; if (intr==0) { LOG(L_WARN,"WARNING:cpl-c:reply_callback: param=0 for callback %d," " transaction=%p \n",type,t); return; } if (type&TMCB_RESPONSE_OUT) { /* the purpose of the final reply is to trash down the interpreter * structure! it's the safest place to do that, since this callback * it's called only once per transaction for final codes (>=200) ;-) */ if (ps->code>=200) { DBG("DEBUG:cpl-c:final_reply: code=%d -------------->\n" " --------------------------> final reply received\n", ps->code); /* CPL interpretation done, call established -> destroy */ free_cpl_interpreter( intr ); /* set to zero the param callback*/ *(ps->param) = 0; } return; } else if (!(type&TMCB_ON_FAILURE)) { LOG(L_ERR,"BUG:cpl-c:reply_callback: unknown type %d\n",type); goto exit; } DBG("DEBUG:cpl-c:negativ_reply: ------------------------------>\n" " ---------------------------------> negativ reply received\n"); intr->flags |= CPL_PROXY_DONE; intr->msg = ps->req; /* if it's a redirect-> do I have to added to the location set ? */ if (intr->proxy.recurse && (ps->code)/100==3) { DBG("DEBUG:cpl-c:negativ_reply: recurse level %d processing..\n", intr->proxy.recurse); intr->proxy.recurse--; /* get the locations from the Contact */ add_contacts_to_loc_set( ps->rpl, &(intr->loc_set)); switch (intr->proxy.ordering) { case SEQUENTIAL_VAL: /* update the last_to_proxy to last location from set */ if (intr->proxy.last_to_proxy==0) { /* the pointer went through entire old set -> set it to the * updated set, from the beginning */ if (intr->loc_set==0) /* the updated set is also empty -> proxy ended */ break; intr->proxy.last_to_proxy = intr->loc_set; } while(intr->proxy.last_to_proxy->next) intr->proxy.last_to_proxy=intr->proxy.last_to_proxy->next; break; case PARALLEL_VAL: /* push the whole new location set to be proxy */ intr->proxy.last_to_proxy = intr->loc_set; break; case FIRSTONLY_VAL: intr->proxy.last_to_proxy = 0; break; } } /* the current proxying failed -> do I have another location to try ? * This applies only for SERIAL forking or if RECURSE is set */ if (intr->proxy.last_to_proxy) { /* continue proxying */ DBG("DEBUG:cpl-c:failed_reply: resuming proxying....\n"); switch (intr->proxy.ordering) { case PARALLEL_VAL: /* I get here only if I got a 3xx and RECURSE in on -> * forward to all location from location set */ intr->proxy.last_to_proxy = 0; cpl_proxy_to_loc_set(intr->msg,&(intr->loc_set),intr->flags ); break; case SEQUENTIAL_VAL: /* place a new branch to the next location from loc. set*/ loc = remove_first_location( &(intr->loc_set) ); /*print_location_set(intr->loc_set);*/ /* update (if necessary) the last_to_proxy location */ if (intr->proxy.last_to_proxy==loc) intr->proxy.last_to_proxy = 0; cpl_proxy_to_loc_set(intr->msg,&loc,intr->flags ); break; default: LOG(L_CRIT,"BUG:cpl_c:failed_reply: unexpected ordering found " "when continuing proxying (%d)\n",intr->proxy.ordering); goto exit; } /* nothing more to be done */ return; } else { /* done with proxying.... -> process the final response */ DBG("DEBUG:cpl-c:failed_reply:final_reply: got a final %d\n",ps->code); intr->ip = 0; if (ps->code==486 || ps->code==600) { /* busy response */ intr->ip = intr->proxy.busy; } else if (ps->code==408) { /* request timeout -> no response */ intr->ip = intr->proxy.noanswer; } else if (((ps->code)/100)==3) { /* redirection */ /* add to the location list all the addresses from Contact */ add_contacts_to_loc_set( ps->rpl, &(intr->loc_set)); print_location_set( intr->loc_set ); intr->ip = intr->proxy.redirect; } else { /* generic failure */ intr->ip = intr->proxy.failure; } if (intr->ip==0) intr->ip = (intr->proxy.default_)? intr->proxy.default_:DEFAULT_ACTION; if (intr->ip!=DEFAULT_ACTION) intr->ip = get_first_child( intr->ip ); if( intr->ip==DEFAULT_ACTION) rez = run_default(intr); else rez = cpl_run_script(intr); switch ( rez ) { case SCRIPT_END: /* we don't need to free the interpreter here since it will * be freed in the final_reply callback */ case SCRIPT_TO_BE_CONTINUED: return; case SCRIPT_RUN_ERROR: case SCRIPT_FORMAT_ERROR: goto exit; default: LOG(L_CRIT,"BUG:cpl-c:failed_reply: improper result %d\n", rez); goto exit; } } exit: /* in case of error the default response chosen by ser at the last * proxying will be forwarded to the UAC */ free_cpl_interpreter( intr ); /* set to zero the param callback*/ *(ps->param) = 0; return; } static inline char *run_proxy( struct cpl_interpreter *intr ) { unsigned short attr_name; unsigned short n; char *kid; char *p; int i; str *s; struct location *loc; int_str tmp; intr->proxy.ordering = PARALLEL_VAL; intr->proxy.recurse = (unsigned short)cpl_env.proxy_recurse; /* identify the attributes */ for( i=NR_OF_ATTR(intr->ip),p=ATTR_PTR(intr->ip) ; i>0 ; i-- ) { get_basic_attr( p, attr_name, n, intr, script_error); switch (attr_name) { case TIMEOUT_ATTR: if (cpl_env.timer_avp.n || cpl_env.timer_avp.s.s) { tmp.n=(int)n; if ( add_avp( AVP_TRACK_TO | cpl_env.timer_avp_type, cpl_env.timer_avp, tmp)<0) { LOG(L_ERR,"ERROR:run_proxy: unable to set " "timer AVP\n"); /* continue */ } } break; case RECURSE_ATTR: switch (n) { case NO_VAL: intr->proxy.recurse = 0; break; case YES_VAL: /* already set as default */ break; default: LOG(L_ERR,"ERROR:run_proxy: invalid value (%u) found" " for attr. RECURSE in PROXY node!\n",n); goto script_error; } break; case ORDERING_ATTR: if (n!=PARALLEL_VAL && n!=SEQUENTIAL_VAL && n!=FIRSTONLY_VAL){ LOG(L_ERR,"ERROR:run_proxy: invalid value (%u) found" " for attr. ORDERING in PROXY node!\n",n); goto script_error; } intr->proxy.ordering = n; break; default: LOG(L_ERR,"ERROR:run_proxy: unknown attribute (%d) in" "PROXY node\n",attr_name); goto script_error; } } intr->proxy.busy = intr->proxy.noanswer = 0; intr->proxy.redirect = intr->proxy.failure = intr->proxy.default_ = 0; /* this is quite an "expensive" node to run, so let's make some checking * before getting deeply into it */ for( i=0 ; iip) ; i++ ) { kid = intr->ip + KID_OFFSET(intr->ip,i); check_overflow_by_ptr( kid+SIMPLE_NODE_SIZE(kid), intr, script_error); switch ( NODE_TYPE(kid) ) { case BUSY_NODE : intr->proxy.busy = kid; break; case NOANSWER_NODE: intr->proxy.noanswer = kid; break; case REDIRECTION_NODE: intr->proxy.redirect = kid; break; case FAILURE_NODE: intr->proxy.failure = kid; break; case DEFAULT_NODE: intr->proxy.default_ = kid; break; default: LOG(L_ERR,"ERROR:run_proxy: unknown output node type" " (%d) for PROXY node\n",NODE_TYPE(kid)); goto script_error; } } /* if the location set if empty, I will go directly on failure/default */ if (intr->loc_set==0) { DBG("DEBUG:run_proxy: location set found empty -> going on " "failure/default branch\n"); if (intr->proxy.failure) return get_first_child(intr->proxy.failure); else if (intr->proxy.default_) return get_first_child(intr->proxy.default_); else return DEFAULT_ACTION; } /* if it's the first execution of a proxy node, force parsing of the needed * headers and duplicate them in shared memory */ if (!(intr->flags&CPL_PROXY_DONE)) { /* user name is already in shared memory */ /* requested URI - mandatory in SIP msg (cannot be STR_NOT_FOUND) */ s = GET_RURI( intr->msg ); duplicate_str( s , intr->ruri ); intr->flags |= CPL_RURI_DUPLICATED; /* TO header - mandatory in SIP msg (cannot be STR_NOT_FOUND) */ if (!intr->to) { if (!intr->msg->to && (parse_headers(intr->msg,HDR_TO_F,0)==-1 || !intr->msg->to)) { LOG(L_ERR,"ERROR:run_proxy: bad msg or missing TO header\n"); goto runtime_error; } s = &(get_to(intr->msg)->uri); } else { s = intr->to; } duplicate_str( s , intr->to ); intr->flags |= CPL_TO_DUPLICATED; /* FROM header - mandatory in SIP msg (cannot be STR_NOT_FOUND) */ if (!intr->from) { if (parse_from_header( intr->msg )==-1) goto runtime_error; s = &(get_from(intr->msg)->uri); } else { s = intr->from; } duplicate_str( s , intr->from ); intr->flags |= CPL_FROM_DUPLICATED; /* SUBJECT header - optional in SIP msg (can be STR_NOT_FOUND) */ if (intr->subject!=STR_NOT_FOUND) { search_and_duplicate_hdr(intr,subject,HDR_SUBJECT_F,s); if (intr->subject!=STR_NOT_FOUND) intr->flags |= CPL_SUBJECT_DUPLICATED; } /* ORGANIZATION header - optional in SIP msg (can be STR_NOT_FOUND) */ if ( intr->organization!=STR_NOT_FOUND) { search_and_duplicate_hdr(intr,organization,HDR_ORGANIZATION_F,s); if ( intr->organization!=STR_NOT_FOUND) intr->flags |= CPL_ORGANIZATION_DUPLICATED; } /* USER_AGENT header - optional in SIP msg (can be STR_NOT_FOUND) */ if (intr->user_agent!=STR_NOT_FOUND) { search_and_duplicate_hdr(intr,user_agent,HDR_USERAGENT_F,s); if (intr->user_agent!=STR_NOT_FOUND) intr->flags |= CPL_USERAGENT_DUPLICATED; } /* ACCEPT_LANGUAGE header - optional in SIP msg * (can be STR_NOT_FOUND) */ if (intr->accept_language!=STR_NOT_FOUND) { search_and_duplicate_hdr(intr,accept_language, HDR_ACCEPTLANGUAGE_F,s); if (intr->accept_language!=STR_NOT_FOUND) intr->flags |= CPL_ACCEPTLANG_DUPLICATED; } /* PRIORITY header - optional in SIP msg (can be STR_NOT_FOUND) */ if (intr->priority!=STR_NOT_FOUND) { search_and_duplicate_hdr(intr,priority,HDR_PRIORITY_F,s); if (intr->priority!=STR_NOT_FOUND) intr->flags |= CPL_PRIORITY_DUPLICATED; } /* now is the first time doing proxy, so I can still be stateless; * as proxy is done all the time stateful, I have to switch from * stateless to stateful if necessary. */ if ( !(intr->flags&CPL_IS_STATEFUL) ) { i = cpl_fct.tmb.t_newtran( intr->msg ); if (i<0) { LOG(L_ERR,"ERROR:cpl-c:run_proxy: failed to build new " "transaction!\n"); goto runtime_error; } else if (i==0) { LOG(L_ERR,"ERROR:cpl-c:run_proxy: processed INVITE is a " "retransmission!\n"); /* instead of generating an error is better just to break the * script by returning EO_SCRIPT */ return EO_SCRIPT; } intr->flags |= CPL_IS_STATEFUL; } /* as I am interested in getting the responses back - I need to install * some callback functions for replies */ if (cpl_fct.tmb.register_tmcb(intr->msg,0, TMCB_ON_FAILURE|TMCB_RESPONSE_OUT,reply_callback,(void*)intr, 0) <= 0){ LOG(L_ERR, "ERROR:cpl_c:run_proxy: failed to register " "TMCB_RESPONSE_OUT callback\n"); goto runtime_error; } } switch (intr->proxy.ordering) { case FIRSTONLY_VAL: /* forward the request only to the first address from loc. set */ /* location set cannot be empty -> was checked before */ loc = remove_first_location( &(intr->loc_set) ); intr->proxy.last_to_proxy = 0; /* set the new ip before proxy -> otherwise race cond with rpls */ intr->ip = CPL_TO_CONTINUE; if (cpl_proxy_to_loc_set(intr->msg,&loc,intr->flags )==-1) goto runtime_error; break; case PARALLEL_VAL: /* forward to all location from location set */ intr->proxy.last_to_proxy = 0; /* set the new ip before proxy -> otherwise race cond with rpls */ intr->ip = CPL_TO_CONTINUE; if (cpl_proxy_to_loc_set(intr->msg,&(intr->loc_set),intr->flags) ==-1) goto runtime_error; break; case SEQUENTIAL_VAL: /* forward the request one at the time to all addresses from * loc. set; location set cannot be empty -> was checked before */ /* use the first location from set */ loc = remove_first_location( &(intr->loc_set) ); /* set as the last_to_proxy the last location from set */ intr->proxy.last_to_proxy = intr->loc_set; while (intr->proxy.last_to_proxy&&intr->proxy.last_to_proxy->next) intr->proxy.last_to_proxy = intr->proxy.last_to_proxy->next; /* set the new ip before proxy -> otherwise race cond with rpls */ intr->ip = CPL_TO_CONTINUE; if (cpl_proxy_to_loc_set(intr->msg,&loc,intr->flags)==-1) goto runtime_error; break; } return CPL_TO_CONTINUE; script_error: return CPL_SCRIPT_ERROR; mem_error: LOG(L_ERR,"ERROR:run_proxy: no more free shm memory\n"); runtime_error: return CPL_RUNTIME_ERROR; } kamailio-4.0.4/obsolete/cpl-c/cpl_nonsig.h0000644000000000000000000000332512223032460017116 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-06-27: file created (bogdan) */ #ifndef _CPL_NONSIG_H_ #define _CPL_NONSIG_H_ #include #include "../../str.h" #include "cpl_env.h" struct cpl_cmd { unsigned int code; str s1; str s2; str s3; }; #define CPL_LOG_CMD 1 #define CPL_MAIL_CMD 2 #define MAX_LOG_DIR_SIZE 256 void cpl_aux_process( int cmd_out, char *log_dir); static inline void write_cpl_cmd(unsigned int code, str *s1, str *s2, str *s3) { static struct cpl_cmd cmd; cmd.code = code; cmd.s1 = *s1; cmd.s2 = *s2; cmd.s3 = *s3; if (write( cpl_env.cmd_pipe[1], &cmd, sizeof(struct cpl_cmd) )==-1) LOG(L_ERR,"ERROR:cpl_c:write_cpl_cmd: write ret: %s\n", strerror(errno)); } #endif kamailio-4.0.4/obsolete/cpl-c/cpl_sig.c0000644000000000000000000000765612223032460016411 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../../action.h" #include "../../dset.h" #include "../../route.h" #include "../../modules/tm/tm_load.h" #include "loc_set.h" #include "cpl_sig.h" #include "cpl_env.h" /* forwards the msg to the given location set; if flags has set the * CPL_PROXY_DONE, all locations will be added as branches, otherwise, the first * one will set as RURI (this is ha case when this is the first proxy of the * message) * The given list of location will be freed, returning 0 instead. * Returns: 0 - OK * -1 - error */ int cpl_proxy_to_loc_set( struct sip_msg *msg, struct location **locs, unsigned char flag) { struct location *foo; struct action act; struct run_act_ctx ra_ctx; if (!*locs) { LOG(L_ERR,"ERROR:cpl_c:cpl_proxy_to_loc_set: empty loc set!!\n"); goto error; } init_run_actions_ctx(&ra_ctx); /* if it's the first time when this sip_msg is proxied, use the first addr * in loc_set to rewrite the RURI */ if (!(flag&CPL_PROXY_DONE)) { DBG("DEBUG:cpl_c:cpl_proxy_to_loc_set: rewriting Request-URI with " "<%s>\n",(*locs)->addr.uri.s); /* build a new action for setting the URI */ memset(&act, 0, sizeof(act)); act.type = SET_URI_T; act.val[0].type = STRING_ST; act.val[0].u.string = (*locs)->addr.uri.s; act.next = 0; /* push the action */ if (do_action(&ra_ctx, &act, msg) < 0) { LOG(L_ERR,"ERROR:cpl_c:cpl_proxy_to_loc_set: do_action failed\n"); goto error; } /* is the location NATED? */ if ((*locs)->flags&CPL_LOC_NATED) setflag(msg,cpl_env.nat_flag); /* free the location and point to the next one */ foo = (*locs)->next; free_location( *locs ); *locs = foo; } /* add the rest of the locations as branches */ while(*locs) { DBG("DEBUG:cpl_c:cpl_proxy_to_loc_set: appending branch " "<%.*s>\n",(*locs)->addr.uri.len,(*locs)->addr.uri.s); if(append_branch(msg, &(*locs)->addr.uri, 0, 0, Q_UNSPECIFIED, 0, 0, 0, 0)==-1){ LOG(L_ERR,"ERROR:cpl_c:cpl_proxy_to_loc_set: failed when " "appending branch <%s>\n",(*locs)->addr.uri.s); goto error; } /* is the location NATED? */ if ((*locs)->flags&CPL_LOC_NATED) setflag(msg,cpl_env.nat_flag); /* free the location and point to the next one */ foo = (*locs)->next; free_location( *locs ); *locs = foo; } /* run what proxy route is set */ if (cpl_env.proxy_route) { if (run_actions(&ra_ctx, main_rt.rlist[cpl_env.proxy_route], msg)<0) { LOG(L_ERR,"ERROR:cpl_c:cpl_proxy_to_loc_set: " "Error in do_action for proxy_route\n"); } } /* do t_forward */ if ( flag&CPL_IS_STATEFUL ) { /* transaction exists */ if (cpl_fct.tmb.t_forward_nonack(msg, 0/*no proxy*/)==-1) { LOG(L_ERR,"ERROR:cpl_c:cpl_proxy_to_loc_set: t_forward_nonack " "failed !\n"); goto error; } } else { /* no transaction -> build & fwd */ if (cpl_fct.tmb.t_relay(msg, 0, 0)==-1) { LOG(L_ERR,"ERROR:cpl_c:cpl_proxy_to_loc_set: t_relay failed !\n"); goto error; } } return 0; error: return -1; } kamailio-4.0.4/obsolete/cpl-c/cpl_rpc.c0000644000000000000000000001327212223032460016402 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include "../../str.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "cpl_db.h" #include "cpl_parser.h" #include "cpl_loader.h" #include "cpl_rpc.h" static inline int check_userhost( char *p, char *end) { char *p1; int dot; /* parse user name */ p1 = p; while (pscan(c, "s", &user.s) < 1) { rpc->fault(c, 400, "Username parameter not found"); return; } user.len = strlen(user.s); DBG("DEBUG:cpl_load: user=%.*s\n", user.len, user.s); if (rpc->scan(c, "s", &cpl_file) < 1) { rpc->fault(c, 400, "CPL file name expected"); return; } cpl_file_len = strlen(cpl_file); DBG("DEBUG:cpl-c:cpl_load: cpl file=%s\n", cpl_file); /* check user+host */ if (check_userhost( user.s, user.s+user.len)!=0) { LOG(L_ERR,"ERROR:cpl-c:cpl_load: invalid user@host [%.*s]\n", user.len,user.s); rpc->fault(c, 400, "Bad user@host: %.*s", user.len, user.s); return; } /* load the xml file - this function will allocated a buff for the loading * the cpl file and attach it to xml.s -> don't forget to free it! */ if (load_file( cpl_file, &xml)!=1) { rpc->fault(c, 400, "Cannot read CPL file\n"); goto error; } /* get the binary coding for the XML file */ if (encodeCPL( &xml, &bin, &enc_log)!=1) { rpc->fault(c, 400, "%.*s", enc_log.len, enc_log.s); goto error; } /* write both the XML and binary formats into database */ if (write_to_db(user.s, &xml, &bin)!=1) { rpc->fault(c, 400, "Cannot save CPL to database"); goto error; } /* free the memory used for storing the cpl script in XML format */ pkg_free( xml.s ); /* everything was OK -> dump the logs into response file */ rpc->add(c, "S", &enc_log); if (enc_log.s) pkg_free ( enc_log.s ); return; error: if (enc_log.s) pkg_free ( enc_log.s ); if (xml.s) pkg_free ( xml.s ); } static const char* cpl_remove_doc[] = { "Remove a CPL script from server.", /* Documentation string */ 0 /* Method signature(s) */ }; static void cpl_remove(rpc_t* rpc, void* c) { char* user; int user_len; DBG("DEBUG:cpl-c:cpl_remove: \"REMOVE_CPL\" FIFO command received!\n"); if (rpc->scan(c, "s", &user) < 1) { rpc->fault(c, 400, "Username parameter not found"); return; } user_len = strlen(user); /* check user+host */ if (check_userhost( user, user+user_len)!=0) { LOG(L_ERR,"ERROR:cpl-c:cpl_remove: invalid user@host [%.*s]\n", user_len,user); rpc->fault(c, 400, "Bad user@host: '%s'", user); return; } if (rmv_from_db(user)!=1) { rpc->fault(c, 400, "Error while removing CPL script of %s from database", user); return; } } static const char* cpl_get_doc[] = { "Return a CPL script.", /* Documentation string */ 0 /* Method signature(s) */ }; static void cpl_get(rpc_t* rpc, void* c) { str user; str script = STR_NULL; if (rpc->scan(c, "S", &user) < 1) { rpc->fault(c, 400, "Username parameter expected"); return; } DBG("DEBUG:cpl-c:cpl_get: user=%.*s\n", user.len, user.s); /* check user+host */ if (check_userhost( user.s, user.s+user.len)!=0) { LOG(L_ERR,"ERROR:cpl-c:cpl_load: invalid user@host [%.*s]\n", user.len,user.s); rpc->fault(c, 400, "Bad user@host '%.*s'", user.len, user.s); return; } /* get the script for this user */ if (get_user_script(&user, &script, 0)==-1) { rpc->fault(c, 500, "Database query failed"); return; } rpc->add(c, "S", &script); if (script.s) shm_free( script.s ); } /* * RPC Methods exported by this module */ rpc_export_t cpl_rpc_methods[] = { {"cpl.load", cpl_load, cpl_load_doc, 0}, {"cpl.remove", cpl_remove, cpl_remove_doc, 0}, {"cpl.get", cpl_get, cpl_get_doc, 0}, {0, 0, 0, 0} }; kamailio-4.0.4/obsolete/cpl-c/sub_list.h0000644000000000000000000000251312223032460016605 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _CPL_SUB_LIST_H #define _CPL_SUB_LIST_H struct node { char *offset; char *name; struct node *next; }; struct node* append_to_list(struct node *head, char *offdet, char *name); char* search_the_list(struct node *head, char *name); void delete_list(struct node *head ); #endif kamailio-4.0.4/obsolete/cpl-c/cpl_loader.c0000644000000000000000000001177312223032460017070 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2003-08-21: cpl_remove() added (bogdan) * 2003-06-24: file created (bogdan) */ #include #include #include #include #include #include #include #include #include #include #include #include "../../str.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "cpl_db.h" #include "cpl_parser.h" #include "cpl_loader.h" #define MAX_STATIC_BUF 256 extern db_con_t* db_hdl; #if 0 /* debug function -> write into a file the content of a str struct. */ int write_to_file(char *filename, str *buf) { int fd; int ret; fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0644); if (!fd) { LOG(L_ERR,"ERROR:cpl-c:write_to_file: cannot open file : %s\n", strerror(errno)); goto error; } while ( (ret=write( fd, buf->s, buf->len))!=buf->len) { if ((ret==-1 && errno!=EINTR)|| ret!=-1) { LOG(L_ERR,"ERROR:cpl-c:write_to_file:cannot write to file:" "%s write_ret=%d\n",strerror(errno), ret ); goto error; } } close(fd); return 0; error: return -1; } #endif /* Loads a file into a buffer; first the file length will be determined for * allocated an exact buffer len for storing the file content into. * Returns: 1 - success * -1 - error */ int load_file( char *filename, str *xml) { int n; int offset; int fd; xml->s = 0; xml->len = 0; /* open the file for reading */ fd = open(filename,O_RDONLY); if (fd==-1) { LOG(L_ERR,"ERROR:cpl-c:load_file: cannot open file for reading:" " %s\n",strerror(errno)); goto error; } /* get the file length */ if ( (xml->len=lseek(fd,0,SEEK_END))==-1) { LOG(L_ERR,"ERROR:cpl-c:load_file: cannot get file length (lseek):" " %s\n", strerror(errno)); goto error; } DBG("DEBUG:cpl-c:load_file: file size = %d\n",xml->len); if ( lseek(fd,0,SEEK_SET)==-1 ) { LOG(L_ERR,"ERROR:cpl-c:load_file: cannot go to beginning (lseek):" " %s\n",strerror(errno)); goto error; } /* get some memory */ xml->s = (char*)pkg_malloc( xml->len+1/*null terminated*/ ); if (!xml->s) { LOG(L_ERR,"ERROR:cpl-c:load_file: no more free pkg memory\n"); goto error; } /*start reading */ offset = 0; while ( offsetlen ) { n=read( fd, xml->s+offset, xml->len-offset); if (n==-1) { if (errno!=EINTR) { LOG(L_ERR,"ERROR:cpl-c:load_file: read failed:" " %s\n", strerror(errno)); goto error; } } else { if (n==0) break; offset += n; } } if (xml->len!=offset) { LOG(L_ERR,"ERROR:cpl-c:load_file: couldn't read all file!\n"); goto error; } xml->s[xml->len] = 0; close(fd); return 1; error: if (fd!=-1) close(fd); if (xml->s) pkg_free( xml->s); return -1; } /* Writes an array of texts into the given response file. * Accepts also empty texts, case in which it will be created an empty * response file. */ void write_to_file( char *file, str *txt, int n ) { int fd; /* open file for write */ fd = open( file, O_WRONLY|O_CREAT|O_TRUNC/*|O_NOFOLLOW*/, 0600 ); if (fd==-1) { LOG(L_ERR,"ERROR:cpl-c:write_to_file: cannot open response file " "<%s>: %s\n", file, strerror(errno)); return; } /* write the txt, if any */ if (n>0) { again: if ( writev( fd, (struct iovec*)txt, n)==-1) { if (errno==EINTR) { goto again; } else { LOG(L_ERR,"ERROR:cpl-c:write_logs_to_file: writev failed: " "%s\n", strerror(errno) ); } } } /* close the file*/ close( fd ); return; } static inline int check_userhost( char *p, char *end) { char *p1; int dot; /* parse user name */ p1 = p; while (p #include #include #include #include #include #include #include #include #include #include "../../parser/parse_uri.h" #include "../../dprint.h" #include "../../str.h" #include "../../ut.h" #include "CPL_tree.h" #include "sub_list.h" #include "cpl_log.h" static struct node *list = 0; static xmlDtdPtr dtd; /* DTD file */ static xmlValidCtxt cvp; /* validating context */ typedef unsigned short length_type ; typedef length_type* length_type_ptr; enum {EMAIL_TO,EMAIL_HDR_NAME,EMAIL_KNOWN_HDR_BODY,EMAIL_UNKNOWN_HDR_BODY}; #define ENCONDING_BUFFER_SIZE 65536 #define FOR_ALL_ATTR(_node,_attr) \ for( (_attr)=(_node)->properties ; (_attr) ; (_attr)=(_attr)->next) /* right and left space trimming */ #define trimlr(_s_) \ do{\ for(;(_s_).s[(_s_).len-1]==' ';(_s_).s[--(_s_).len]=0);\ for(;(_s_).s[0]==' ';(_s_).s=(_s_).s+1,(_s_).len--);\ }while(0); #define check_overflow(_p_,_offset_,_end_,_error_) \ do{\ if ((_p_)+(_offset_)>=(_end_)) { \ LOG(L_ERR,"ERROR:cpl-c:%s:%d: overflow -> buffer to small\n",\ __FILE__,__LINE__);\ goto _error_;\ }\ }while(0)\ #define set_attr_type(_p_,_type_,_end_,_error_) \ do{\ check_overflow(_p_,sizeof(length_type),_end_,_error_);\ *((length_type_ptr)(_p_)) = htons((length_type)(_type_));\ (_p_) += sizeof(length_type);\ }while(0)\ #define append_short_attr(_p_,_n_,_end_,_error_) \ do{\ check_overflow(_p_,sizeof(length_type),_end_,_error_);\ *((length_type_ptr)(_p_)) = htons((length_type)(_n_));\ (_p_) += sizeof(length_type);\ }while(0) #define append_str_attr(_p_,_s_,_end_,_error_) \ do{\ check_overflow(_p_,(_s_).len + 1*((((_s_).len)&0x0001)==1),\ _end_,_error_);\ *((length_type_ptr)(_p_)) = htons((length_type)(_s_).len);\ (_p_) += sizeof(length_type);\ memcpy( (_p_), (_s_).s, (_s_).len);\ (_p_) += (_s_).len + 1*((((_s_).len)&0x0001)==1);\ }while(0) #define append_double_str_attr(_p_,_s1_,_s2_,_end_,_error_) \ do{\ check_overflow(_p_,(_s1_).len + (_s2_).len +\ 1*((((_s2_).len+(_s2_).len)&0x0001)==1), _end_, _error_);\ *((length_type_ptr)(_p_))=htons((length_type)((_s1_).len)+(_s2_).len);\ (_p_) += sizeof(length_type);\ memcpy( (_p_), (_s1_).s, (_s1_).len);\ (_p_) += (_s1_).len;\ memcpy( (_p_), (_s2_).s, (_s2_).len);\ (_p_) += (_s2_).len + 1*((((_s1_).len+(_s2_).len)&0x0001)==1);\ }while(0) #define get_attr_val(_attr_name_,_val_,_error_) \ do { \ (_val_).s = (char*)xmlGetProp(node,(_attr_name_));\ (_val_).len = strlen((_val_).s);\ /* remove all spaces from begin and end */\ trimlr( (_val_) );\ if ((_val_).len==0) {\ LOG(L_ERR,"ERROR:cpl_c:%s:%d: attribute <%s> has an "\ "empty value\n",__FILE__,__LINE__,(_attr_name_));\ goto _error_;\ }\ }while(0)\ #define MAX_EMAIL_HDR_SIZE 7 /*we are looking only for SUBJECT and BODY ;-)*/ #define MAX_EMAIL_BODY_SIZE 512 #define MAX_EMAIL_SUBJECT_SIZE 32 static inline char *decode_mail_url(char *p, char *p_end, char *url, unsigned char *nr_attr) { static char buf[ MAX_EMAIL_HDR_SIZE ]; char c; char foo; unsigned short hdr_len; unsigned short *len; int max_len; int status; /* init */ hdr_len = 0; max_len = 0; status = EMAIL_TO; (*nr_attr) ++; set_attr_type(p, TO_ATTR, p_end, error); /* attr type */ len = ((unsigned short*)(p)); /* attr val's len */ *len = 0; /* init the len */ p += 2; /* parse the whole url */ do { /* extract a char from the encoded url */ if (*url=='+') { /* substitute a blank for a plus */ c=' '; url++; /* Look for a hex encoded character */ } else if ( (*url=='%') && *(url+1) && *(url+2) ) { /* hex encoded - convert to a char */ c = hex2int(url[1]); foo = hex2int(url[2]); if (c==-1 || foo==-1) { LOG(L_ERR, "ERROR:cpl_c:decode_mail_url: non-ASCII escaped " "character in mail url [%.*s]\n", 3, url); goto error; } c = c<<4 | foo; url += 3; } else { /* normal character - just copy it without changing */ c = *url; url++; } /* finally we got a character !! */ switch (c) { case '?': switch (status) { case EMAIL_TO: if (*len==0) { LOG(L_ERR,"ERROR:cpl_c:decode_mail_url: empty TO " "address found in MAIL node!\n"); goto error; } if (((*len)&0x0001)==1) p++; *len = htons(*len); hdr_len = 0; status = EMAIL_HDR_NAME; break; default: goto parse_error; } break; case '=': switch (status) { case EMAIL_HDR_NAME: DBG("DEBUG:cpl_c:decode_mail_url: hdr [%.*s] found\n", hdr_len,buf); if ( hdr_len==BODY_EMAILHDR_LEN && strncasecmp(buf,BODY_EMAILHDR_STR,hdr_len)==0 ) { /* BODY hdr found */ set_attr_type( p, BODY_ATTR, p_end, error); max_len = MAX_EMAIL_BODY_SIZE; } else if ( hdr_len==SUBJECT_EMAILHDR_LEN && strncasecmp(buf,SUBJECT_EMAILHDR_STR,hdr_len)==0 ) { /* SUBJECT hdr found */ set_attr_type( p, SUBJECT_ATTR, p_end, error); max_len = MAX_EMAIL_SUBJECT_SIZE; } else { DBG("DEBUG:cpl_c:decode_mail_url: unknown hdr ->" " ignoring\n"); status = EMAIL_UNKNOWN_HDR_BODY; break; } (*nr_attr) ++; len = ((unsigned short*)(p)); /* attr val's len */ *len = 0; /* init the len */ p += 2; status = EMAIL_KNOWN_HDR_BODY; break; default: goto parse_error; } break; case '&': switch (status) { case EMAIL_KNOWN_HDR_BODY: if (((*len)&0x0001)==1) p++; *len = htons(*len); case EMAIL_UNKNOWN_HDR_BODY: hdr_len = 0; status = EMAIL_HDR_NAME; break; default: goto parse_error; } break; case 0: switch (status) { case EMAIL_TO: if (*len==0) { LOG(L_ERR,"ERROR:cpl_c:decode_mail_url: empty TO " "address found in MAIL node!\n"); goto error; } case EMAIL_KNOWN_HDR_BODY: if (((*len)&0x0001)==1) p++; *len = htons(*len); case EMAIL_UNKNOWN_HDR_BODY: break; default: goto parse_error; } break; default: switch (status) { case EMAIL_TO: (*len)++; *(p++) = c; if (*len==URL_MAILTO_LEN && !strncasecmp(p-(*len),URL_MAILTO_STR,(*len))) { DBG("DEBUG:cpl_c:decode_mail_url: MAILTO: found at" " the beginning of TO -> removed\n"); p -= (*len); *len = 0; } break; case EMAIL_KNOWN_HDR_BODY: if ((*len)name[0]) { case 'i': case 'I': set_attr_type(p, IS_ATTR, buf_end, error); break; case 'c': case 'C': set_attr_type(p, CONTAINS_ATTR, buf_end, error); break; case 's': case 'S': set_attr_type(p, SUBDOMAIN_OF_ATTR, buf_end, error); break; default: LOG(L_ERR,"ERROR:cpl_c:encode_address_attr: unknown attribute " "<%s>\n",attr->name); goto error; } /* get the value of the attribute */ get_attr_val( attr->name , val, error); /* copy also the \0 from the end of string */ val.len++; append_str_attr(p, val, buf_end, error); } return p-p_orig; error: return -1; } /* Attr. encoding for ADDRESS_SWITCH node: * | attr1_t(2) attr1_val(2) | FIELD attr * [| attr2_t(2) attr2_val(2) |]? SUBFILED attr */ static inline int encode_address_switch_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get the value of the attribute */ get_attr_val( attr->name , val, error); switch(attr->name[0]) { case 'F': case 'f': set_attr_type(p, FIELD_ATTR, buf_end, error); if (val.s[0]=='D' || val.s[0]=='d') append_short_attr(p, DESTINATION_VAL, buf_end, error); else if (val.s[6]=='A' || val.s[6]=='a') append_short_attr(p,ORIGINAL_DESTINATION_VAL,buf_end,error); else if (!val.s[6]) append_short_attr(p, ORIGIN_VAL, buf_end, error); else { LOG(L_ERR,"ERROR:cpl_c:encode_address_switch_attr: unknown" " value <%s> for FIELD attr\n",val.s); goto error; }; break; case 'S': case 's': set_attr_type(p, SUBFIELD_ATTR, buf_end, error); switch (val.s[0]) { case 'u': case 'U': append_short_attr(p, USER_VAL, buf_end, error); break; case 'h': case 'H': append_short_attr(p, HOST_VAL, buf_end, error); break; case 'p': case 'P': append_short_attr(p, PORT_VAL, buf_end, error); break; case 't': case 'T': append_short_attr(p, TEL_VAL, buf_end, error); break; case 'd': case 'D': /*append_short_attr(p, DISPLAY_VAL, buf_end, error); break;*/ /* NOT YET SUPPORTED BY INTERPRETER */ case 'a': case 'A': /*append_short_attr(p, ADDRESS_TYPE_VAL, buf_end,error); break;*/ /* NOT YET SUPPORTED BY INTERPRETER */ default: LOG(L_ERR,"ERROR:cpl_c:encode_address_switch_attr: " "unknown value <%s> for SUBFIELD attr\n",val.s); goto error; } break; default: LOG(L_ERR,"ERROR:cpl_c:encode_address_switch_attr: unknown" " attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for LANGUAGE node: * | attr_t(2) attr_len(2) attr_val(2*x) | MATCHES attr (NNT) */ static inline int encode_lang_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; char *end; char *val_bk; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { /* there is only one attribute -> MATCHES */ if (attr->name[0]!='M' && attr->name[0]!='m') { LOG(L_ERR,"ERROR:cpl_c:encode_lang_attr: unknown attribute " "<%s>\n",attr->name); goto error; } val.s = val_bk = (char*)xmlGetProp(node,attr->name); /* parse the language-tag */ for(end=val.s,val.len=0;;end++) { /* trim all spaces from the beginning of the tag */ if (!val.len && (*end==' ' || *end=='\t')) continue; /* we cannot have more than 2 attrs - LANG_TAG and LANG_SUBTAG */ if ((*nr_attr)>=2) goto lang_error; if (((*end)|0x20)>='a' && ((*end)|0x20)<='z') { val.len++; continue; } else if (*end=='*' && val.len==0 && (*nr_attr)==0 && (*end==' '|| *end=='\t' || *end==0)) { val.len++; set_attr_type(p, MATCHES_TAG_ATTR, buf_end, error); } else if (val.len && (*nr_attr)==0 && *end=='-' ) { set_attr_type(p, MATCHES_TAG_ATTR, buf_end, error); } else if (val.len && ((*nr_attr)==0 || (*nr_attr)==1) && (*end==' '|| *end=='\t' || *end==0)) { set_attr_type(p, (!(*nr_attr))?MATCHES_TAG_ATTR:MATCHES_SUBTAG_ATTR, buf_end, error ); } else goto lang_error; (*nr_attr)++; /*DBG("----> language tag=%d; %d [%.*s]\n",*(p-1), val.len,val.len,end-val.len);*/ val.s = end-val.len; append_str_attr(p, val, buf_end, error); val.len = 0; if (*end==0) break; } } return p-p_orig; lang_error: LOG(L_ERR,"ERROR:cpl-c:encode_lang_attr: bad value for language_tag <%s>\n", val_bk); error: return -1; } /* Attr. encoding for PRIORITY node: * | attr1_t(2) attr1_val(2) | LESS/GREATER/EQUAL attr * [| attr2_t(2) attr2_len(2) attr_val(2*x) |]? PRIOSTR attr (NT) */ static inline int encode_priority_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* attribute's name */ switch(attr->name[0]) { case 'L': case 'l': set_attr_type(p, LESS_ATTR, buf_end, error); break; case 'G': case 'g': set_attr_type(p, GREATER_ATTR, buf_end, error); break; case 'E': case 'e': set_attr_type(p, EQUAL_ATTR, buf_end, error); break; default: LOG(L_ERR,"ERROR:cpl_c:encode_priority_attr: unknown attribute " "<%s>\n",attr->name); goto error; } /* attribute's encoded value */ get_attr_val( attr->name , val, error); if ( val.len==EMERGENCY_STR_LEN && !strncasecmp(val.s,EMERGENCY_STR,val.len) ) { append_short_attr(p, EMERGENCY_VAL, buf_end, error); } else if ( val.len==URGENT_STR_LEN && !strncasecmp(val.s,URGENT_STR,val.len) ) { append_short_attr(p, URGENT_VAL, buf_end, error); } else if ( val.len==NORMAL_STR_LEN && !strncasecmp(val.s,NORMAL_STR,val.len) ) { append_short_attr(p, NORMAL_VAL, buf_end, error); } else if ( val.len==NON_URGENT_STR_LEN && !strncasecmp(val.s,NON_URGENT_STR,val.len) ) { append_short_attr(p, NON_URGENT_VAL, buf_end, error); } else { append_short_attr(p, UNKNOWN_PRIO_VAL, buf_end, error); set_attr_type(p, PRIOSTR_ATTR, buf_end, error); val.len++; /* append \0 also */ append_str_attr(p, val, buf_end, error); } } return p-p_orig; error: return -1; } /* Attr. encoding for STRING_SWITCH node: * [| attr1_t(2) attr1_len(2) attr_val(2*x) |]? IS attr (NT) * [| attr2_t(2) attr2_len(2) attr_val(2*x) |]? CONTAINS attr (NT) */ static inline int encode_string_switch_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* there is only one attribute -> MATCHES */ if (attr->name[0]!='F' && attr->name[0]!='f') { LOG(L_ERR,"ERROR:cpl_c:encode_string_switch_attr: unknown " "attribute <%s>\n",attr->name); goto error; } set_attr_type(p, FIELD_ATTR, buf_end, error); /* attribute's encoded value */ get_attr_val( attr->name , val, error); switch (val.s[0]) { case 'S': case 's': append_short_attr(p, SUBJECT_VAL, buf_end, error); break; case 'O': case 'o': append_short_attr(p, ORGANIZATION_VAL, buf_end, error); break; case 'U': case 'u': append_short_attr(p, USER_AGENT_VAL, buf_end, error); break; case 'D': case 'd': append_short_attr(p, DISPLAY_VAL, buf_end, error); break; default: LOG(L_ERR,"ERROR:cpl_c:encode_string_switch_attr: unknown " "value <%s> for FIELD\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for STRING node: * [| attr1_t(2) attr1_len(2) attr_val(2*x) |]? IS attr (NT) * [| attr2_t(2) attr2_len(2) attr_val(2*x) |]? CONTAINS attr (NT) */ static inline int encode_string_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; switch(attr->name[0]) { case 'I': case 'i': set_attr_type(p, IS_ATTR, buf_end, error); break; case 'C': case 'c': set_attr_type(p, CONTAINS_ATTR, buf_end, error); break; default: LOG(L_ERR,"ERROR:cpl_c:encode_string_attr: unknown " "attribute <%s>\n",attr->name); goto error; } /* attribute's encoded value */ get_attr_val( attr->name , val, error); val.len++; /* grab also the \0 */ append_str_attr(p,val, buf_end, error); } return p-p_orig; error: return -1; } /* Attr. encoding for TIME_SWITCH node: * [| attr1_t(2) attr1_len(2) attr_val(2*x) |]? TZID attr (NT) * [| attr2_t(2) attr2_len(2) attr_val(2*x) |]? TZURL attr (NT) */ static inline int encode_time_switch_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { static str tz_str = STR_STATIC_INIT("TZ="); xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; switch(attr->name[2]) { case 'I': case 'i': set_attr_type(p, TZID_ATTR, buf_end, error); /* attribute's encoded value */ get_attr_val( attr->name , val, error); val.len++; /* grab also the \0 */ append_double_str_attr(p,tz_str,val, buf_end, error); break; case 'U': case 'u': /* set_attr_type(p, TZURL_ATTR, buf_end, error); * is a waste of space to copy the url - the interpreter doesn't * use it at all ;-) */ break; default: LOG(L_ERR,"ERROR:cpl_c:encode_time_switch_attr: unknown " "attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for TIME node: * | attr1_t(2) attr1_len(2) attr1_val(2*x) | DSTART attr (NT) * [| attr2_t(2) attr2_len(2) attr2_val(2*x) |]? DTEND attr (NT) * [| attr3_t(2) attr3_len(2) attr3_val(2*x) |]? DURATION attr (NT) * [| attr4_t(2) attr4_len(2) attr4_val(2*x) |]? FREQ attr (NT) * [| attr5_t(2) attr5_len(2) attr5_val(2*x) |]? WKST attr (NT) * [| attr6_t(2) attr6_len(2) attr6_val(2*x) |]? BYYEARDAY attr (NT) * [| attr7_t(2) attr7_len(2) attr7_val(2*x) |]? COUNT attr (NT) * [| attr8_t(2) attr8_len(2) attr8_val(2*x) |]? BYSETPOS attr (NT) * [| attr9_t(2) attr9_len(2) attr9_val(2*x) |]? BYMONTH attr (NT) * [| attr10_t(2) attr10_len(2) attr_val10(2*x) |]? BYMONTHDAY attr (NT) * [| attr11_t(2) attr11_len(2) attr_val11(2*x) |]? BYMINUTE attr (NT) * [| attr12_t(2) attr12_len(2) attr_val12(2*x) |]? INTERVAL attr (NT) * [| attr13_t(2) attr13_len(2) attr_val13(2*x) |]? UNTIL attr (NT) * [| attr14_t(2) attr14_len(2) attr_val14(2*x) |]? BYSECOND attr (NT) * [| attr15_t(2) attr15_len(2) attr_val15(2*x) |]? BYHOUR attr (NT) * [| attr16_t(2) attr16_len(2) attr_val16(2*x) |]? BYDAY attr (NT) * [| attr17_t(2) attr17_len(2) attr_val17(2*x) |]? BYWEEKNO attr (NT) */ static inline int encode_time_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; switch (attr->name[4]) { case 0: if (attr->name[0]=='F' || attr->name[0]=='f') set_attr_type(p, FREQ_ATTR, buf_end, error); else if (attr->name[0]=='W' || attr->name[0]=='w') set_attr_type(p, WKST_ATTR, buf_end, error); break; case 'a': case 'A': if (attr->name[0]=='D' || attr->name[0]=='d') set_attr_type(p, DTSTART_ATTR, buf_end, error); else if (attr->name[0]=='B' || attr->name[0]=='b') set_attr_type(p, BYYEARDAY_ATTR, buf_end, error); break; case 't': case 'T': if (attr->name[0]=='D' || attr->name[0]=='d') set_attr_type(p, DURATION_ATTR, buf_end, error); else if (attr->name[0]=='C' || attr->name[0]=='c') set_attr_type(p, COUNT_ATTR, buf_end, error); else if (attr->name[0]=='B' || attr->name[0]=='b') set_attr_type(p, BYSETPOS_ATTR, buf_end, error); break; case 'n': case 'N': if (!attr->name[0]) set_attr_type(p, BYMONTH_ATTR, buf_end, error); else if (attr->name[0]=='D' || attr->name[0]=='d') set_attr_type(p, BYMONTHDAY_ATTR, buf_end, error); else if (attr->name[0]=='e' || attr->name[0]=='E') set_attr_type(p, BYMINUTE_ATTR, buf_end, error); break; case 'd': case 'D': set_attr_type(p, DTEND_ATTR, buf_end, error); break; case 'r': case 'R': set_attr_type(p, INTERVAL_ATTR, buf_end, error); break; case 'l': case 'L': set_attr_type(p, UNTIL_ATTR, buf_end, error); break; case 'c': case 'C': set_attr_type(p, BYSECOND_ATTR, buf_end, error); break; case 'u': case 'U': set_attr_type(p, BYHOUR_ATTR, buf_end, error); break; case 'y': case 'Y': set_attr_type(p, BYDAY_ATTR, buf_end, error); break; case 'e': case 'E': set_attr_type(p, BYWEEKNO_ATTR, buf_end, error); break; default: LOG(L_ERR,"ERROR:cpl_c:encode_time_attr: unknown " "attribute <%s>\n",attr->name); goto error; } /* attribute's encoded value */ get_attr_val( attr->name , val, error); val.len++; /* grab also the \0 */ append_str_attr(p,val, buf_end, error); } return p-p_orig; error: return -1; } /* Attr. encoding for LOOKUP node: * | attr1_t(2) attr1_len(2) attr1_val(2*x) | SOURCE attr (NT) * [| attr2_t(2) attr2_val(2) |]? CLEAR attr */ static inline int encode_lookup_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { /* get attribute's value */ get_attr_val( attr->name , val, error); if ( !strcasecmp((const char*)attr->name,"source") ) { /* this param will not be copied, since it has only one value ;-)*/ if ( val.len!=SOURCE_REG_STR_LEN || strncasecmp( val.s, SOURCE_REG_STR, val.len) ) { LOG(L_ERR,"ERROR:cpl_c:encode_lookup_attr: unsupported value" " <%.*s> in SOURCE param\n",val.len,val.s); goto error; } } else if ( !strcasecmp((const char*)attr->name,"clear") ) { (*nr_attr)++; set_attr_type(p, CLEAR_ATTR, buf_end, error); if ( val.len==3 && !strncasecmp(val.s,"yes",3) ) append_short_attr(p, YES_VAL, buf_end, error); else if ( val.len==2 && !strncasecmp(val.s,"no",2) ) append_short_attr(p, NO_VAL, buf_end, error); else { LOG(L_ERR,"ERROR:cpl_c:encode_lookup_attr: unknown value " "<%.*s> for attribute CLEAR\n",val.len,val.s); goto error; } } else if ( !strcasecmp((const char*)attr->name,"timeout") ) { LOG(L_WARN,"WARNING:cpl_c:encode_lookup_attr: unsupported param " "TIMEOUT; skipping\n"); } else { LOG(L_ERR,"ERROR:cpl_c:encode_lookup_attr: unknown attribute " "<%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for LOCATION node: * | attr1_t(2) attr1_len(2) attr1_val(2*x) | URL attr (NT) * [| attr2_t(2) attr2_val(2) |]? PRIORITY attr * [| attr3_t(2) attr3_val(2) |]? CLEAR attr */ static inline int encode_location_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { struct sip_uri uri; xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; unsigned short nr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get attribute's value */ get_attr_val( attr->name , val, error); switch(attr->name[0]) { case 'U': case 'u': set_attr_type(p, URL_ATTR, buf_end, error); /* check if it's a valid SIP URL -> just call * parse uri function and see if returns error ;-) */ if (parse_uri( val.s, val.len, &uri)!=0) { LOG(L_ERR,"ERROR:cpl-c:encrypt_location_attr: <%s> is " "not a valid SIP URL\n",val.s); goto error; } val.len++; /*copy also the \0 */ append_str_attr(p,val, buf_end, error); break; case 'P': case 'p': set_attr_type(p, PRIORITY_ATTR, buf_end, error); if (val.s[0]=='0') nr=0; else if (val.s[0]=='1') nr=10; else goto prio_error; if (val.s[1]!='.') goto prio_error; if (val.s[2]<'0' || val.s[2]>'9') goto prio_error; nr += val.s[2] - '0'; if (nr>10) goto prio_error; append_short_attr(p, nr, buf_end, error); break; case 'C': case 'c': set_attr_type(p, CLEAR_ATTR, buf_end, error); if (val.s[0]=='y' || val.s[0]=='Y') append_short_attr(p, YES_VAL, buf_end, error); else append_short_attr(p, NO_VAL, buf_end, error); break; default: LOG(L_ERR,"ERROR:cpl_c:encode_location_attr: unknown attribute " "<%s>\n",attr->name); goto error; } } return p-p_orig; prio_error: LOG(L_ERR,"ERROR:cpl_c:encode_location_attr: invalid priority <%s>\n", val.s); error: return -1; } /* Attr. encoding for REMOVE_LOCATION node: * [| attr1_t(2) attr1_len(2) attr1_val(2*x) |]? LOCATION attr (NT) */ static inline int encode_rmvloc_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { struct sip_uri uri; xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; switch(attr->name[0]) { case 'L': case 'l': set_attr_type(p, LOCATION_ATTR, buf_end, error); /* get the value of the attribute */ get_attr_val( attr->name , val, error); /* check if it's a valid SIP URL -> just call * parse uri function and see if returns error ;-) */ if (parse_uri( val.s, val.len, &uri)!=0) { LOG(L_ERR,"ERROR:cpl-c:encrypt_rmvloc_attr: <%s> is " "not a valid SIP URL\n",val.s); goto error; } val.len++; /*copy also the \0 */ append_str_attr(p,val, buf_end, error); break; case 'P': case 'p': case 'V': case 'v': /* as the interpreter ignores PARAM and VALUE attributes, we will * do the same ;-) */ break; default: LOG(L_ERR,"ERROR:cpl_c:encode_rmvloc_attr: unknown attribute " "<%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for PROXY node: * [| attr1_t(2) attr1_val(2) |]? RECURSE attr * [| attr2_t(2) attr2_val(2) |]? TIMEOUT attr * [| attr3_t(2) attr3_val(2) |]? ORDERING attr */ static inline int encode_proxy_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; unsigned int nr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get the value of the attribute */ get_attr_val( attr->name , val, error); switch(attr->name[0]) { case 'R': case 'r': set_attr_type(p, RECURSE_ATTR, buf_end, error); if (val.s[0]=='y' || val.s[0]=='Y') append_short_attr(p, YES_VAL, buf_end, error); else if (val.s[0]=='n' || val.s[0]=='N') append_short_attr(p, NO_VAL, buf_end, error); else { LOG(L_ERR,"ERROR:cpl_c:encode_proxy_attr: unknown value " "<%s> for attribute RECURSE\n",val.s); goto error; } break; case 'T': case 't': set_attr_type(p, TIMEOUT_ATTR, buf_end, error); if (str2int(&val,&nr)==-1) { LOG(L_ERR,"ERROR:cpl_c:encode_proxy_attr: bad value <%.*s>" " for attribute TIMEOUT\n",val.len,val.s); goto error; } append_short_attr(p, (unsigned short)nr, buf_end, error); break; case 'O': case 'o': set_attr_type(p, ORDERING_ATTR, buf_end, error); switch (val.s[0]) { case 'p': case'P': append_short_attr(p, PARALLEL_VAL, buf_end, error); break; case 'S': case 's': append_short_attr(p, SEQUENTIAL_VAL, buf_end, error); break; case 'F': case 'f': append_short_attr(p, FIRSTONLY_VAL, buf_end, error); break; default: LOG(L_ERR,"ERROR:cpl_c:encode_proxy_attr: unknown " "value <%s> for attribute ORDERING\n",val.s); goto error; } break; default: LOG(L_ERR,"ERROR:cpl_c:encode_proxy_attr: unknown attribute " "<%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for REJECT node: * | attr1_t(2) attr1_val(2) | STATUS attr * [| attr2_t(2) attr2_len(2) attr2_val(2*x)|]? REASON attr (NT) */ static inline int encode_reject_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; unsigned int nr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get the value of the attribute */ get_attr_val( attr->name , val, error); switch(attr->name[0]) { case 'R': case 'r': set_attr_type(p, REASON_ATTR, buf_end, error); val.len++; /* grab also the /0 */ append_str_attr(p, val, buf_end, error); break; case 'S': case 's': set_attr_type(p, STATUS_ATTR, buf_end, error); if (str2int(&val,&nr)==-1) { /*it was a non numeric value */ if (val.len==BUSY_STR_LEN && !strncasecmp(val.s,BUSY_STR,val.len)) { append_short_attr(p, BUSY_VAL, buf_end, error); } else if (val.len==NOTFOUND_STR_LEN && !strncasecmp(val.s,NOTFOUND_STR,val.len)) { append_short_attr(p, NOTFOUND_VAL, buf_end, error); } else if (val.len==ERROR_STR_LEN && !strncasecmp(val.s,ERROR_STR,val.len)) { append_short_attr(p, ERROR_VAL, buf_end, error); } else if (val.len==REJECT_STR_LEN && !strncasecmp(val.s,REJECT_STR,val.len)) { append_short_attr(p, REJECT_VAL, buf_end, error); } else { LOG(L_ERR,"ERROR:cpl_c:encode_priority_attr: bad " "val. <%s> for STATUS\n",val.s); goto error; } } else if (nr<400 || nr>700) { LOG(L_ERR,"ERROR:cpl_c:encode_priority_attr: bad " "code <%d> for STATUS\n",nr); goto error; } else { append_short_attr(p, nr, buf_end, error); } break; default: LOG(L_ERR,"ERROR:cpl_c:encode_priority_attr: unknown attribute " "<%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for REDIRECT node: * | attr1_t(2) attr1_val(2) | STATUS attr * [| attr2_t(2) attr2_len(2) attr2_val(2*x)|]? REASON attr (NT) */ static inline int encode_redirect_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; if (attr->name[0]=='p' || attr->name[0]=='P') { set_attr_type(p, PERMANENT_ATTR, buf_end, error); /* get the value */ get_attr_val( attr->name , val, error); if (val.s[0]=='y' || val.s[0]=='Y') append_short_attr( p, YES_VAL, buf_end, error); else if (val.s[0]=='n' || val.s[0]=='N') append_short_attr( p, NO_VAL, buf_end, error); else { LOG(L_ERR,"ERROR:cpl_c:encode_redirect_attr: bad " "val. <%s> for PERMANENT\n",val.s); goto error; } } else { LOG(L_ERR,"ERROR:cpl_c:encode_redirect_attr: unknown attribute " "<%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for LOG node: * [| attr1_t(2) attr1_len(2) attr1_val(2*x) |]? NAME attr (NT) * [| attr2_t(2) attr2_len(2) attr2_val(2*x) |]? COMMENT attr (NT) */ static inline int encode_log_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get the value of the attribute */ get_attr_val( attr->name , val, error); switch (attr->name[0] ) { case 'n': case 'N': if (val.len>MAX_NAME_SIZE) val.len=MAX_NAME_SIZE; set_attr_type(p, NAME_ATTR, buf_end, error); break; case 'c': case 'C': if (val.len>MAX_COMMENT_SIZE) val.len=MAX_COMMENT_SIZE; set_attr_type(p, COMMENT_ATTR, buf_end, error); break; default: LOG(L_ERR,"ERROR:cpl_c:encode_log_attr: unknown attribute " "<%s>\n",attr->name); goto error; } /* be sure there is a \0 at the end of string */ val.s[val.len++]=0; append_str_attr(p,val, buf_end, error); } return p-p_orig; error: return -1; } /* Attr. encoding for MAIL node: * | attr1_t(2) attr1_len(2) attr1_val(2*x) | TO_ATTR attr (NNT) * [| attr2_t(2) attr2_len(2) attr2_val(2*x) |]? SUBJECT_ATTR attr (NNT) * [| attr3_t(2) attr3_len(2) attr3_val(2*x) |]? BODY_ATTR attr (NNT) */ static inline int encode_mail_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { /* there is only one attribute -> URL */ if (attr->name[0]!='u' && attr->name[0]!='U') { LOG(L_ERR,"ERROR:cpl_c:encode_node_attr: unknown attribute " "<%s>\n",attr->name); goto error; } p = decode_mail_url( p, buf_end, (char*)xmlGetProp(node,attr->name), nr_attr); if (p==0) goto error; } return p-p_orig; error: return -1; } /* Attr. encoding for SUBACTION node: */ static inline int encode_subaction_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; str val; FOR_ALL_ATTR(node,attr) { /* there is only one attribute -> ID */ if ((attr->name[0]|0x20)=='i' && ((attr->name[1]|0x20)=='d') && attr->name[2]==0 ) { /* get the value of the attribute */ get_attr_val( attr->name , val, error); if ((list = append_to_list(list, node_ptr,val.s))==0) { LOG(L_ERR,"ERROR:cpl_c:encode_subaction_attr: failed to add " "subaction into list -> pkg_malloc failed?\n"); goto error; } } else { LOG(L_ERR,"ERROR:cpl_c:encode_subaction_attr: unknown attribute " "<%s>\n",attr->name); goto error; } } return 0; error: return -1; } /* Attr. encoding for SUB node: * | attr1_t(2) attr1_val(2) | REF_ATTR attr */ static inline int encode_sub_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; char *sub_ptr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* there is only one attribute -> REF */ if ( strcasecmp("ref",(char*)attr->name)!=0 ) { LOG(L_ERR,"ERROR:cpl_c:encode_sub_attr: unknown attribute " "<%s>\n",attr->name); goto error; } set_attr_type(p, REF_ATTR, buf_end, error); /* get the value of the attribute */ get_attr_val( attr->name , val, error); if ( (sub_ptr=search_the_list(list, val.s))==0 ) { LOG(L_ERR,"ERROR:cpl_c:encode_sub_attr: unable to find declaration " "of subaction <%s>\n",val.s); goto error; } append_short_attr(p,(unsigned short)(node_ptr-sub_ptr),buf_end,error); } return p-p_orig; error: return -1; } /* Returns : -1 - error * >0 - subtree size of the given node */ int encode_node( xmlNodePtr node, char *p, char *p_end) { xmlNodePtr kid; unsigned short sub_tree_size; int attr_size; int kid_size; int foo; /* counting the kids */ for(kid=node->children,foo=0;kid;kid=kid->next) if (kid->type==XML_ELEMENT_NODE) foo++; check_overflow(p,GET_NODE_SIZE(foo),p_end,error); NR_OF_KIDS(p) = foo; /* size of the encoded attributes */ attr_size = 0; /* init the number of attributes */ NR_OF_ATTR(p) = 0; /* encode node name */ switch (node->name[0]) { case 'a':case 'A': switch (node->name[7]) { case 0: NODE_TYPE(p) = ADDRESS_NODE; attr_size = encode_address_attr( node, p, p_end); break; case '-': NODE_TYPE(p) = ADDRESS_SWITCH_NODE; attr_size = encode_address_switch_attr( node, p, p_end); break; default: NODE_TYPE(p) = ANCILLARY_NODE; break; } break; case 'B':case 'b': NODE_TYPE(p) = BUSY_NODE; break; case 'c':case 'C': NODE_TYPE(p) = CPL_NODE; break; case 'd':case 'D': NODE_TYPE(p) = DEFAULT_NODE; break; case 'f':case 'F': NODE_TYPE(p) = FAILURE_NODE; break; case 'i':case 'I': NODE_TYPE(p) = INCOMING_NODE; break; case 'l':case 'L': switch (node->name[2]) { case 'g':case 'G': NODE_TYPE(p) = LOG_NODE; attr_size = encode_log_attr( node, p, p_end); break; case 'o':case 'O': NODE_TYPE(p) = LOOKUP_NODE; attr_size = encode_lookup_attr( node, p, p_end); break; case 'c':case 'C': NODE_TYPE(p) = LOCATION_NODE; attr_size = encode_location_attr( node, p, p_end); break; default: if (node->name[8]) { NODE_TYPE(p) = LANGUAGE_SWITCH_NODE; } else { NODE_TYPE(p) = LANGUAGE_NODE; attr_size = encode_lang_attr( node, p, p_end); } break; } break; case 'm':case 'M': NODE_TYPE(p) = MAIL_NODE; attr_size = encode_mail_attr( node, p, p_end); break; case 'n':case 'N': switch (node->name[3]) { case 'F':case 'f': NODE_TYPE(p) = NOTFOUND_NODE; break; case 'N':case 'n': NODE_TYPE(p) = NOANSWER_NODE; break; default: NODE_TYPE(p) = NOT_PRESENT_NODE; break; } break; case 'o':case 'O': if (node->name[1]=='t' || node->name[1]=='T') { NODE_TYPE(p) = OTHERWISE_NODE; } else { NODE_TYPE(p) = OUTGOING_NODE; } break; case 'p':case 'P': if (node->name[2]=='o' || node->name[2]=='O') { NODE_TYPE(p) = PROXY_NODE; attr_size = encode_proxy_attr( node, p, p_end); } else if (node->name[8]) { NODE_TYPE(p) = PRIORITY_SWITCH_NODE; } else { NODE_TYPE(p) = PRIORITY_NODE; attr_size = encode_priority_attr( node, p, p_end); } break; case 'r':case 'R': switch (node->name[2]) { case 'j':case 'J': NODE_TYPE(p) = REJECT_NODE; attr_size = encode_reject_attr( node, p, p_end); break; case 'm':case 'M': NODE_TYPE(p) = REMOVE_LOCATION_NODE; attr_size = encode_rmvloc_attr( node, p, p_end); break; default: if (node->name[8]) { NODE_TYPE(p) = REDIRECTION_NODE; } else { NODE_TYPE(p) = REDIRECT_NODE; attr_size = encode_redirect_attr( node, p, p_end); } break; } break; case 's':case 'S': switch (node->name[3]) { case 0: NODE_TYPE(p) = SUB_NODE; attr_size = encode_sub_attr( node, p, p_end); break; case 'c':case 'C': NODE_TYPE(p) = SUCCESS_NODE; break; case 'a':case 'A': NODE_TYPE(p) = SUBACTION_NODE; attr_size = encode_subaction_attr( node, p, p_end); break; default: if (node->name[6]) { NODE_TYPE(p) = STRING_SWITCH_NODE; attr_size = encode_string_switch_attr( node, p, p_end); } else { NODE_TYPE(p) = STRING_NODE; attr_size = encode_string_attr( node, p, p_end); } break; } break; case 't':case 'T': if (node->name[4]) { NODE_TYPE(p) = TIME_SWITCH_NODE; attr_size = encode_time_switch_attr( node, p, p_end); } else { NODE_TYPE(p) = TIME_NODE; attr_size = encode_time_attr( node, p, p_end); } break; default: LOG(L_ERR,"ERROR:cpl-c:encode_node: unknown node <%s>\n", node->name); goto error; } /* compute the total length of the node (including attributes) */ if (attr_size<0) goto error; sub_tree_size = SIMPLE_NODE_SIZE(p) + (unsigned short)attr_size; /* encrypt all the kids */ for(kid = node->children,foo=0;kid;kid=kid->next) { if (kid->type!=XML_ELEMENT_NODE) continue; SET_KID_OFFSET( p, foo, sub_tree_size); kid_size = encode_node( kid, p+sub_tree_size, p_end); if (kid_size<=0) goto error; sub_tree_size += (unsigned short)kid_size; foo++; } return sub_tree_size; error: return -1; } #define BAD_XML "CPL script is not a valid XML document" #define BAD_XML_LEN (sizeof(BAD_XML)-1) #define BAD_CPL "CPL script doesn't respect CPL grammar" #define BAD_CPL_LEN (sizeof(BAD_CPL)-1) #define NULL_CPL "Empty CPL script" #define NULL_CPL_LEN (sizeof(NULL_CPL)-1) #define ENC_ERR "Encoding of the CPL script failed" #define ENC_ERR_LEN (sizeof(ENC_ERR)-1) int encodeCPL( str *xml, str *bin, str *log) { static char buf[ENCONDING_BUFFER_SIZE]; xmlDocPtr doc; xmlNodePtr cur; doc = 0; list = 0; /* reset all the logs (if any) to catch some possible err/warn/notice * from the parser/validater/encoder */ reset_logs(); /* parse the xml */ doc = xmlParseDoc( (unsigned char*)xml->s ); if (!doc) { append_log( 1, MSG_ERR BAD_XML LF, MSG_ERR_LEN+BAD_XML_LEN+LF_LEN); LOG(L_ERR,"ERROR:cpl:encodeCPL:" BAD_XML "\n"); goto error; } /* check the xml against dtd */ if (xmlValidateDtd(&cvp, doc, dtd)!=1) { append_log( 1, MSG_ERR BAD_CPL LF, MSG_ERR_LEN+BAD_CPL_LEN+LF_LEN); LOG(L_ERR,"ERROR:cpl-c:encodeCPL: " BAD_CPL "\n"); goto error; } cur = xmlDocGetRootElement(doc); if (!cur) { append_log( 1, MSG_ERR NULL_CPL LF, MSG_ERR_LEN+NULL_CPL_LEN+LF_LEN); LOG(L_ERR,"ERROR:cpl-c:encodeCPL: " NULL_CPL "\n"); goto error; } bin->len = encode_node( cur, buf, buf+ENCONDING_BUFFER_SIZE); if (bin->len<0) { append_log( 1, MSG_ERR ENC_ERR LF, MSG_ERR_LEN+ENC_ERR_LEN+LF_LEN); LOG(L_ERR,"ERROR:cpl-c:encodeCPL: " ENC_ERR "\n"); goto error; } xmlFreeDoc(doc); if (list) delete_list(list); /* compile the log buffer */ compile_logs( log ); bin->s = buf; return 1; error: if (doc) xmlFreeDoc(doc); if (list) delete_list(list); /* compile the log buffer */ compile_logs( log ); return 0; } #if 0 static void err_print(void *ctx, const char *msg, ...) { va_list ap; //char *t; va_start( ap, msg); LOG(L_ERR,"->>>> my errr <%s>\n",msg); //while ( (t=va_arg(ap, char *))!=0) { // LOG(L_ERR," -> <%s>\n",t); //} vfprintf(stderr,msg,ap); //append_log( 2, ERR, ERR_LEN, msg, strlen(msg) ); va_end(ap); } #endif /* loads and parse the dtd file; a validating context is created */ int init_CPL_parser( char* DTD_filename ) { dtd = xmlParseDTD( NULL, (unsigned char*)DTD_filename); if (!dtd) { LOG(L_ERR,"ERROR:cpl-c:init_CPL_parser: DTD not parsed successfully\n"); return -1; } cvp.userData = (void *) stderr; cvp.error = (xmlValidityErrorFunc) /*err_print*/ fprintf; cvp.warning = (xmlValidityWarningFunc) /*err_print*/ fprintf; return 1; } kamailio-4.0.4/obsolete/cpl-c/cpl_time.c0000644000000000000000000005736012223032460016562 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2003-06-24: file imported from tmrec (bogdan) * 2003-xx-xx: file Created (daniel) */ #include #include #include #include "../../mem/mem.h" #include "cpl_time.h" /************************ imported from "utils.h" ***************************/ static inline int strz2int(char *_bp) { int _v; char *_p; if(!_bp) return 0; _v = 0; _p = _bp; while(*_p && *_p>='0' && *_p<='9') { _v += *_p - '0'; _p++; } return _v; } static inline char* trim(char* _s) { int len; char* end; /* Null pointer, there is nothing to do */ if (!_s) return _s; /* Remove spaces and tabs from the beginning of string */ while ((*_s == ' ') || (*_s == '\t')) _s++; len = strlen(_s); end = _s + len - 1; /* Remove trailing spaces and tabs */ while ((*end == ' ') || (*end == '\t')) end--; if (end != (_s + len - 1)) { *(end+1) = '\0'; } return _s; } /************************ imported from "ac_tm.c" ***************************/ /* #define USE_YWEEK_U // Sunday system * #define USE_YWEEK_V // ISO 8601 */ #ifndef USE_YWEEK_U #ifndef USE_YWEEK_V #ifndef USE_YWEEK_W #define USE_YWEEK_W // Monday system #endif #endif #endif #ifdef USE_YWEEK_U #define SUN_WEEK(t) (int)(((t)->tm_yday + 7 - \ ((t)->tm_wday)) / 7) #else #define MON_WEEK(t) (int)(((t)->tm_yday + 7 - \ ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7) #endif #define ac_get_wday_yr(t) (int)((t)->tm_yday/7) #define ac_get_wday_mr(t) (int)(((t)->tm_mday-1)/7) ac_tm_p ac_tm_new() { ac_tm_p _atp = NULL; _atp = (ac_tm_p)pkg_malloc(sizeof(ac_tm_t)); if(!_atp) return NULL; memset(_atp, 0, sizeof(ac_tm_t)); return _atp; } int ac_tm_fill(ac_tm_p _atp, struct tm* _tm) { if(!_atp || !_tm) return -1; _atp->t.tm_sec = _tm->tm_sec; /* seconds */ _atp->t.tm_min = _tm->tm_min; /* minutes */ _atp->t.tm_hour = _tm->tm_hour; /* hours */ _atp->t.tm_mday = _tm->tm_mday; /* day of the month */ _atp->t.tm_mon = _tm->tm_mon; /* month */ _atp->t.tm_year = _tm->tm_year; /* year */ _atp->t.tm_wday = _tm->tm_wday; /* day of the week */ _atp->t.tm_yday = _tm->tm_yday; /* day in the year */ _atp->t.tm_isdst = _tm->tm_isdst; /* daylight saving time */ _atp->mweek = ac_get_mweek(_tm); _atp->yweek = ac_get_yweek(_tm); _atp->ywday = ac_get_wday_yr(_tm); _atp->mwday = ac_get_wday_mr(_tm); DBG("---> fill = %s\n",asctime(&(_atp->t)) ); return 0; } int ac_tm_set(ac_tm_p _atp, struct tm* _tm) { if(!_atp || !_tm) return -1; _atp->time = mktime(_tm); return ac_tm_fill(_atp, _tm); } int ac_tm_set_time(ac_tm_p _atp, time_t _t) { if(!_atp) return -1; _atp->time = _t; return ac_tm_fill(_atp, localtime(&_t)); } int ac_get_mweek(struct tm* _tm) { if(!_tm) return -1; #ifdef USE_YWEEK_U return ((_tm->tm_mday-1)/7 + (7-_tm->tm_wday+(_tm->tm_mday-1)%7)/7); #else return ((_tm->tm_mday-1)/7 + (7-(6+_tm->tm_wday)%7+(_tm->tm_mday-1)%7)/7); #endif } int ac_get_yweek(struct tm* _tm) { int week = -1; #ifdef USE_YWEEK_V int days; #endif if(!_tm) return -1; #ifdef USE_YWEEK_U week = SUN_WEEK(_tm); #else week = MON_WEEK(_tm); #endif #ifdef USE_YWEEK_V days = ((_tm->tm_yday + 7 - (_tm->tm_wday ? _tm->tm_wday-1 : 6)) % 7); if(days >= 4) week++; else if(week == 0) week = 53; #endif return week; } int ac_get_wkst() { #ifdef USE_YWEEK_U return 0; #else return 1; #endif } int ac_tm_reset(ac_tm_p _atp) { if(!_atp) return -1; memset(_atp, 0, sizeof(ac_tm_t)); return 0; } int ac_tm_free(ac_tm_p _atp) { if(!_atp) return -1; if(_atp->mv) pkg_free(_atp->mv); /*pkg_free(_atp);*/ return 0; } ac_maxval_p ac_get_maxval(ac_tm_p _atp) { struct tm _tm; int _v; ac_maxval_p _amp = NULL; if(!_atp) return NULL; _amp = (ac_maxval_p)pkg_malloc(sizeof(ac_maxval_t)); if(!_amp) return NULL; // the number of the days in the year _amp->yday = 365 + is_leap_year(_atp->t.tm_year+1900); // the number of the days in the month switch(_atp->t.tm_mon) { case 1: if(_amp->yday == 366) _amp->mday = 29; else _amp->mday = 28; break; case 3: case 5: case 8: case 10: _amp->mday = 30; break; default: _amp->mday = 31; } // maximum occurrences of a week day in the year memset(&_tm, 0, sizeof(struct tm)); _tm.tm_year = _atp->t.tm_year; _tm.tm_mon = 11; _tm.tm_mday = 31; mktime(&_tm); _v = 0; if(_atp->t.tm_wday > _tm.tm_wday) _v = _atp->t.tm_wday - _tm.tm_wday + 1; else _v = _tm.tm_wday - _atp->t.tm_wday; _amp->ywday = (int)((_tm.tm_yday-_v)/7) + 1; // maximum number of weeks in the year _amp->yweek = ac_get_yweek(&_tm) + 1; // maximum number of the week day in the month _amp->mwday=(int)((_amp->mday-1-(_amp->mday-_atp->t.tm_mday)%7)/7)+1; // maximum number of weeks in the month _v = (_atp->t.tm_wday + (_amp->mday - _atp->t.tm_mday)%7)%7; #ifdef USE_YWEEK_U _amp->mweek = (int)((_amp->mday-1)/7+(7-_v+(_amp->mday-1)%7)/7)+1; #else _amp->mweek = (int)((_amp->mday-1)/7+(7-(6+_v)%7+(_amp->mday-1)%7)/7)+1; #endif _atp->mv = _amp; return _amp; } int ac_print(ac_tm_p _atp) { static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"}; if(!_atp) { printf("\n(null)\n"); return -1; } printf("\nSys time: %d\nTime: %02d:%02d:%02d\n", (int)_atp->time, _atp->t.tm_hour, _atp->t.tm_min, _atp->t.tm_sec); printf("Date: %s, %04d-%02d-%02d\n", _wdays[_atp->t.tm_wday], _atp->t.tm_year+1900, _atp->t.tm_mon+1, _atp->t.tm_mday); printf("Year day: %d\nYear week-day: %d\nYear week: %d\n", _atp->t.tm_yday, _atp->ywday, _atp->yweek); printf("Month week: %d\nMonth week-day: %d\n", _atp->mweek, _atp->mwday); if(_atp->mv) { printf("Max ydays: %d\nMax yweeks: %d\nMax yweekday: %d\n", _atp->mv->yday, _atp->mv->yweek, _atp->mv->ywday);; printf("Max mdays: %d\nMax mweeks: %d\nMax mweekday: %d\n", _atp->mv->mday, _atp->mv->mweek, _atp->mv->mwday);; } return 0; } /************************ imported from "tmrec.c" ***************************/ #define _D(c) ((c) -'0') tr_byxxx_p tr_byxxx_new() { tr_byxxx_p _bxp = NULL; _bxp = (tr_byxxx_p)pkg_malloc(sizeof(tr_byxxx_t)); if(!_bxp) return NULL; memset(_bxp, 0, sizeof(tr_byxxx_t)); return _bxp; } int tr_byxxx_init(tr_byxxx_p _bxp, int _nr) { if(!_bxp) return -1; _bxp->nr = _nr; _bxp->xxx = (int*)pkg_malloc(_nr*sizeof(int)); if(!_bxp->xxx) return -1; _bxp->req = (int*)pkg_malloc(_nr*sizeof(int)); if(!_bxp->req) { pkg_free(_bxp->xxx); return -1; } memset(_bxp->xxx, 0, _nr*sizeof(int)); memset(_bxp->req, 0, _nr*sizeof(int)); return 0; } int tr_byxxx_free(tr_byxxx_p _bxp) { if(!_bxp) return -1; if(_bxp->xxx) pkg_free(_bxp->xxx); if(_bxp->req) pkg_free(_bxp->req); pkg_free(_bxp); return 0; } tmrec_p tmrec_new() { tmrec_p _trp = NULL; _trp = (tmrec_p)pkg_malloc(sizeof(tmrec_t)); if(!_trp) return NULL; memset(_trp, 0, sizeof(tmrec_t)); localtime_r(&_trp->dtstart,&(_trp->ts)); return _trp; } int tmrec_free(tmrec_p _trp) { if(!_trp) return -1; tr_byxxx_free(_trp->byday); tr_byxxx_free(_trp->bymday); tr_byxxx_free(_trp->byyday); tr_byxxx_free(_trp->bymonth); tr_byxxx_free(_trp->byweekno); /*pkg_free(_trp);*/ return 0; } int tr_parse_dtstart(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->dtstart = ic_parse_datetime(_in, &(_trp->ts)); DBG("----->dtstart = %ld | %s\n", (long)_trp->dtstart, ctime(&(_trp->dtstart))); return (_trp->dtstart==0)?-1:0; } int tr_parse_dtend(tmrec_p _trp, char *_in) { struct tm _tm; if(!_trp || !_in) return -1; _trp->dtend = ic_parse_datetime(_in,&_tm); DBG("----->dtend = %ld | %s\n", (long)_trp->dtend, ctime(&(_trp->dtend))); return (_trp->dtend==0)?-1:0; } int tr_parse_duration(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->duration = ic_parse_duration(_in); return (_trp->duration==0)?-1:0; } int tr_parse_until(tmrec_p _trp, char *_in) { struct tm _tm; if(!_trp || !_in) return -1; _trp->until = ic_parse_datetime(_in, &_tm); return (_trp->until==0)?-1:0; } int tr_parse_freq(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; if(!strcasecmp(_in, "daily")) { _trp->freq = FREQ_DAILY; return 0; } if(!strcasecmp(_in, "weekly")) { _trp->freq = FREQ_WEEKLY; return 0; } if(!strcasecmp(_in, "monthly")) { _trp->freq = FREQ_MONTHLY; return 0; } if(!strcasecmp(_in, "yearly")) { _trp->freq = FREQ_YEARLY; return 0; } _trp->freq = FREQ_NOFREQ; return 0; } int tr_parse_interval(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->interval = strz2int(_in); return 0; } int tr_parse_byday(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->byday = ic_parse_byday(_in); return 0; } int tr_parse_bymday(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->bymday = ic_parse_byxxx(_in); return 0; } int tr_parse_byyday(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->byyday = ic_parse_byxxx(_in); return 0; } int tr_parse_bymonth(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->bymonth = ic_parse_byxxx(_in); return 0; } int tr_parse_byweekno(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->byweekno = ic_parse_byxxx(_in); return 0; } int tr_parse_wkst(tmrec_p _trp, char *_in) { if(!_trp || !_in) return -1; _trp->wkst = ic_parse_wkst(_in); return 0; } int tr_print(tmrec_p _trp) { static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"}; int i; if(!_trp) { printf("\n(null)\n"); return -1; } printf("Recurrence definition\n-- start time ---\n"); printf("Sys time: %d\n", (int)_trp->dtstart); printf("Time: %02d:%02d:%02d\n", _trp->ts.tm_hour, _trp->ts.tm_min, _trp->ts.tm_sec); printf("Date: %s, %04d-%02d-%02d\n", _wdays[_trp->ts.tm_wday], _trp->ts.tm_year+1900, _trp->ts.tm_mon+1, _trp->ts.tm_mday); printf("---\n"); printf("End time: %d\n", (int)_trp->dtend); printf("Duration: %d\n", (int)_trp->duration); printf("Until: %d\n", (int)_trp->until); printf("Freq: %d\n", (int)_trp->freq); printf("Interval: %d\n", (int)_trp->interval); if(_trp->byday) { printf("Byday: "); for(i=0; i<_trp->byday->nr; i++) printf(" %d%s", _trp->byday->req[i], _wdays[_trp->byday->xxx[i]]); printf("\n"); } if(_trp->bymday) { printf("Bymday: %d:", _trp->bymday->nr); for(i=0; i<_trp->bymday->nr; i++) printf(" %d", _trp->bymday->xxx[i]*_trp->bymday->req[i]); printf("\n"); } if(_trp->byyday) { printf("Byyday:"); for(i=0; i<_trp->byyday->nr; i++) printf(" %d", _trp->byyday->xxx[i]*_trp->byyday->req[i]); printf("\n"); } if(_trp->bymonth) { printf("Bymonth: %d:", _trp->bymonth->nr); for(i=0; i< _trp->bymonth->nr; i++) printf(" %d", _trp->bymonth->xxx[i]*_trp->bymonth->req[i]); printf("\n"); } if(_trp->byweekno) { printf("Byweekno: "); for(i=0; i<_trp->byweekno->nr; i++) printf(" %d", _trp->byweekno->xxx[i]*_trp->byweekno->req[i]); printf("\n"); } printf("Weekstart: %d\n", _trp->wkst); return 0; } time_t ic_parse_datetime(char *_in, struct tm *_tm) { if(!_in || !_tm) return 0; memset(_tm, 0, sizeof(struct tm)); _tm->tm_year = _D(_in[0])*1000 + _D(_in[1])*100 + _D(_in[2])*10 + _D(_in[3]) - 1900; _tm->tm_mon = _D(_in[4])*10 + _D(_in[5]) - 1; _tm->tm_mday = _D(_in[6])*10 + _D(_in[7]); _tm->tm_hour = _D(_in[9])*10 + _D(_in[10]); _tm->tm_min = _D(_in[11])*10 + _D(_in[12]); _tm->tm_sec = _D(_in[13])*10 + _D(_in[14]); _tm->tm_isdst = -1 /*daylight*/; return mktime(_tm); } time_t ic_parse_duration(char *_in) { time_t _t, _ft; char *_p; int _fl; if(!_in || (*_in!='+' && *_in!='-' && *_in!='P' && *_in!='p')) return 0; if(*_in == 'P' || *_in=='p') _p = _in+1; else { if(strlen(_in)<2 || (_in[1]!='P' && _in[1]!='p')) return 0; _p = _in+2; } _t = _ft = 0; _fl = 1; while(*_p) { switch(*_p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': _t = _t*10 + *_p - '0'; break; case 'w': case 'W': if(!_fl) return 0; _ft += _t*7*24*3600; _t = 0; break; case 'd': case 'D': if(!_fl) return 0; _ft += _t*24*3600; _t = 0; break; case 'h': case 'H': if(_fl) return 0; _ft += _t*3600; _t = 0; break; case 'm': case 'M': if(_fl) return 0; _ft += _t*60; _t = 0; break; case 's': case 'S': if(_fl) return 0; _ft += _t; _t = 0; break; case 't': case 'T': if(!_fl) return 0; _fl = 0; break; default: return 0; } _p++; } return _ft; } tr_byxxx_p ic_parse_byday(char *_in) { tr_byxxx_p _bxp = NULL; int _nr, _s, _v; char *_p; if(!_in) return NULL; _bxp = tr_byxxx_new(); if(!_bxp) return NULL; _p = _in; _nr = 1; while(*_p) { if(*_p == ',') _nr++; _p++; } if(tr_byxxx_init(_bxp, _nr) < 0) { tr_byxxx_free(_bxp); return NULL; } _p = _in; _nr = _v = 0; _s = 1; while(*_p && _nr < _bxp->nr) { switch(*_p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': _v = _v*10 + *_p - '0'; break; case 's': case 'S': _p++; switch(*_p) { case 'a': case 'A': _bxp->xxx[_nr] = WDAY_SA; _bxp->req[_nr] = _s*_v; break; case 'u': case 'U': _bxp->xxx[_nr] = WDAY_SU; _bxp->req[_nr] = _s*_v; break; default: goto error; } _s = 1; _v = 0; break; case 'm': case 'M': _p++; if(*_p!='o' && *_p!='O') goto error; _bxp->xxx[_nr] = WDAY_MO; _bxp->req[_nr] = _s*_v; _s = 1; _v = 0; break; case 't': case 'T': _p++; switch(*_p) { case 'h': case 'H': _bxp->xxx[_nr] = WDAY_TH; _bxp->req[_nr] = _s*_v; break; case 'u': case 'U': _bxp->xxx[_nr] = WDAY_TU; _bxp->req[_nr] = _s*_v; break; default: goto error; } _s = 1; _v = 0; break; case 'w': case 'W': _p++; if(*_p!='e' && *_p!='E') goto error; _bxp->xxx[_nr] = WDAY_WE; _bxp->req[_nr] = _s*_v; _s = 1; _v = 0; break; case 'f': case 'F': _p++; if(*_p!='r' && *_p!='R') goto error; _bxp->xxx[_nr] = WDAY_FR; _bxp->req[_nr] = _s*_v; _s = 1; _v = 0; break; case '-': _s = -1; break; case '+': case ' ': case '\t': break; case ',': _nr++; break; default: goto error; } _p++; } return _bxp; error: tr_byxxx_free(_bxp); return NULL; } tr_byxxx_p ic_parse_byxxx(char *_in) { tr_byxxx_p _bxp = NULL; int _nr, _s, _v; char *_p; if(!_in) return NULL; _bxp = tr_byxxx_new(); if(!_bxp) return NULL; _p = _in; _nr = 1; while(*_p) { if(*_p == ',') _nr++; _p++; } if(tr_byxxx_init(_bxp, _nr) < 0) { tr_byxxx_free(_bxp); return NULL; } _p = _in; _nr = _v = 0; _s = 1; while(*_p && _nr < _bxp->nr) { switch(*_p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': _v = _v*10 + *_p - '0'; break; case '-': _s = -1; break; case '+': case ' ': case '\t': break; case ',': _bxp->xxx[_nr] = _v; _bxp->req[_nr] = _s; _s = 1; _v = 0; _nr++; break; default: goto error; } _p++; } if(_nr < _bxp->nr) { _bxp->xxx[_nr] = _v; _bxp->req[_nr] = _s; } return _bxp; error: tr_byxxx_free(_bxp); return NULL; } int ic_parse_wkst(char *_in) { if(!_in || strlen(_in)!=2) goto error; switch(_in[0]) { case 's': case 'S': switch(_in[1]) { case 'a': case 'A': return WDAY_SA; case 'u': case 'U': return WDAY_SU; default: goto error; } case 'm': case 'M': if(_in[1]!='o' && _in[1]!='O') goto error; return WDAY_MO; case 't': case 'T': switch(_in[1]) { case 'h': case 'H': return WDAY_TH; case 'u': case 'U': return WDAY_TU; default: goto error; } case 'w': case 'W': if(_in[1]!='e' && _in[1]!='E') goto error; return WDAY_WE; case 'f': case 'F': if(_in[1]!='r' && _in[1]!='R') goto error; return WDAY_FR; break; default: goto error; } error: #ifdef USE_YWEEK_U return WDAY_SU; #else return WDAY_MO; #endif } /*********************** imported from "checktr.c" **************************/ #define REC_ERR -1 #define REC_MATCH 0 #define REC_NOMATCH 1 #define _IS_SET(x) (((x)>0)?1:0) /*** local headers ***/ int get_min_interval(tmrec_p); int check_min_unit(tmrec_p, ac_tm_p, tr_res_p); int check_freq_interval(tmrec_p _trp, ac_tm_p _atp); int check_byxxx(tmrec_p, ac_tm_p); /** * * return 0/REC_MATCH - the time falls in * -1/REC_ERR - error * 1/REC_NOMATCH - the time falls out */ int check_tmrec(tmrec_p _trp, ac_tm_p _atp, tr_res_p _tsw) { if(!_trp || !_atp || (!_IS_SET(_trp->duration) && !_IS_SET(_trp->dtend))) return REC_ERR; // it is before start date if(_atp->time < _trp->dtstart) return REC_NOMATCH; // compute the duration of the recurrence interval if(!_IS_SET(_trp->duration)) _trp->duration = _trp->dtend - _trp->dtstart; if(_atp->time <= _trp->dtstart+_trp->duration) { if(_tsw) { if(_tsw->flag & TSW_RSET) { if(_tsw->rest>_trp->dtstart+_trp->duration-_atp->time) _tsw->rest = _trp->dtstart+_trp->duration - _atp->time; } else { _tsw->flag |= TSW_RSET; _tsw->rest = _trp->dtstart+_trp->duration - _atp->time; } } return REC_MATCH; } // after the bound of recurrence if(_IS_SET(_trp->until) && _atp->time >= _trp->until + _trp->duration) return REC_NOMATCH; // check if the instance of recurrence matches the 'interval' if(check_freq_interval(_trp, _atp)!=REC_MATCH) return REC_NOMATCH; if(check_min_unit(_trp, _atp, _tsw)!=REC_MATCH) return REC_NOMATCH; if(check_byxxx(_trp, _atp)!=REC_MATCH) return REC_NOMATCH; return REC_MATCH; } int check_freq_interval(tmrec_p _trp, ac_tm_p _atp) { int _t0, _t1; struct tm _tm; if(!_trp || !_atp) return REC_ERR; if(!_IS_SET(_trp->freq)) return REC_NOMATCH; if(!_IS_SET(_trp->interval) || _trp->interval==1) return REC_MATCH; switch(_trp->freq) { case FREQ_DAILY: case FREQ_WEEKLY: memset(&_tm, 0, sizeof(struct tm)); _tm.tm_year = _trp->ts.tm_year; _tm.tm_mon = _trp->ts.tm_mon; _tm.tm_mday = _trp->ts.tm_mday; _t0 = (int)mktime(&_tm); memset(&_tm, 0, sizeof(struct tm)); _tm.tm_year = _atp->t.tm_year; _tm.tm_mon = _atp->t.tm_mon; _tm.tm_mday = _atp->t.tm_mday; _t1 = (int)mktime(&_tm); if(_trp->freq == FREQ_DAILY) return (((_t1-_t0)/(24*3600))%_trp->interval==0)? REC_MATCH:REC_NOMATCH; #ifdef USE_YWEEK_U _t0 -= _trp->ts.tm_wday*24*3600; _t1 -= _atp->t.tm_wday*24*3600; #else _t0 -= ((_trp->ts.tm_wday+6)%7)*24*3600; _t1 -= ((_atp->t.tm_wday+6)%7)*24*3600; #endif return (((_t1-_t0)/(7*24*3600))%_trp->interval==0)? REC_MATCH:REC_NOMATCH; case FREQ_MONTHLY: _t0 = (_atp->t.tm_year-_trp->ts.tm_year)*12 + _atp->t.tm_mon-_trp->ts.tm_mon; return (_t0%_trp->interval==0)?REC_MATCH:REC_NOMATCH; case FREQ_YEARLY: return ((_atp->t.tm_year-_trp->ts.tm_year)%_trp->interval==0)? REC_MATCH:REC_NOMATCH; } return REC_NOMATCH; } int get_min_interval(tmrec_p _trp) { if(!_trp) return FREQ_NOFREQ; if(_trp->freq == FREQ_DAILY || _trp->byday || _trp->bymday || _trp->byyday) return FREQ_DAILY; if(_trp->freq == FREQ_WEEKLY || _trp->byweekno) return FREQ_WEEKLY; if(_trp->freq == FREQ_MONTHLY || _trp->bymonth) return FREQ_MONTHLY; if(_trp->freq == FREQ_YEARLY) return FREQ_YEARLY; return FREQ_NOFREQ; } int check_min_unit(tmrec_p _trp, ac_tm_p _atp, tr_res_p _tsw) { int _v0, _v1; if(!_trp || !_atp) return REC_ERR; switch(get_min_interval(_trp)) { case FREQ_DAILY: break; case FREQ_WEEKLY: if(_trp->ts.tm_wday != _atp->t.tm_wday) return REC_NOMATCH; break; case FREQ_MONTHLY: if(_trp->ts.tm_mday != _atp->t.tm_mday) return REC_NOMATCH; break; case FREQ_YEARLY: if(_trp->ts.tm_mon != _atp->t.tm_mon || _trp->ts.tm_mday != _atp->t.tm_mday) return REC_NOMATCH; break; default: return REC_NOMATCH; } _v0 = _trp->ts.tm_hour*3600 + _trp->ts.tm_min*60 + _trp->ts.tm_sec; _v1 = _atp->t.tm_hour*3600 + _atp->t.tm_min*60 + _atp->t.tm_sec; if(_v1 >= _v0 && _v1 < _v0 + _trp->duration) { if(_tsw) { if(_tsw->flag & TSW_RSET) { if(_tsw->rest>_v0+_trp->duration-_v1) _tsw->rest = _v0 + _trp->duration - _v1; } else { _tsw->flag |= TSW_RSET; _tsw->rest = _v0 + _trp->duration - _v1; } } return REC_MATCH; } return REC_NOMATCH; } int check_byxxx(tmrec_p _trp, ac_tm_p _atp) { int i; ac_maxval_p _amp = NULL; if(!_trp || !_atp) return REC_ERR; if(!_trp->byday && !_trp->bymday && !_trp->byyday && !_trp->bymonth && !_trp->byweekno) return REC_MATCH; _amp = ac_get_maxval(_atp); if(!_amp) return REC_NOMATCH; if(_trp->bymonth) { for(i=0; i<_trp->bymonth->nr; i++) { if(_atp->t.tm_mon == (_trp->bymonth->xxx[i]*_trp->bymonth->req[i]+12)%12) break; } if(i>=_trp->bymonth->nr) return REC_NOMATCH; } if(_trp->freq==FREQ_YEARLY && _trp->byweekno) { for(i=0; i<_trp->byweekno->nr; i++) { if(_atp->yweek == (_trp->byweekno->xxx[i]*_trp->byweekno->req[i]+ _amp->yweek)%_amp->yweek) break; } if(i>=_trp->byweekno->nr) return REC_NOMATCH; } if(_trp->byyday) { for(i=0; i<_trp->byyday->nr; i++) { if(_atp->t.tm_yday == (_trp->byyday->xxx[i]*_trp->byyday->req[i]+ _amp->yday)%_amp->yday) break; } if(i>=_trp->byyday->nr) return REC_NOMATCH; } if(_trp->bymday) { for(i=0; i<_trp->bymday->nr; i++) { #ifdef EXTRA_DEBUG DBG("Req:bymday: %d == %d\n", _atp->t.tm_mday, (_trp->bymday->xxx[i]*_trp->bymday->req[i]+ _amp->mday)%_amp->mday + ((_trp->bymday->req[i]<0)?1:0)); #endif if(_atp->t.tm_mday == (_trp->bymday->xxx[i]*_trp->bymday->req[i]+ _amp->mday)%_amp->mday + (_trp->bymday->req[i]<0)?1:0) break; } if(i>=_trp->bymday->nr) return REC_NOMATCH; } if(_trp->byday) { for(i=0; i<_trp->byday->nr; i++) { if(_trp->freq==FREQ_YEARLY) { #ifdef EXTRA_DEBUG DBG("Req:byday:y: %d==%d && %d==%d\n", _atp->t.tm_wday, _trp->byday->xxx[i], _atp->ywday+1, (_trp->byday->req[i]+_amp->ywday)%_amp->ywday); #endif if(_atp->t.tm_wday == _trp->byday->xxx[i] && _atp->ywday+1 == (_trp->byday->req[i]+_amp->ywday)% _amp->ywday) break; } else { if(_trp->freq==FREQ_MONTHLY) { #ifdef EXTRA_DEBUG DBG("Req:byday:m: %d==%d && %d==%d\n", _atp->t.tm_wday, _trp->byday->xxx[i], _atp->mwday+1, (_trp->byday->req[i]+_amp->mwday)%_amp->mwday); #endif if(_atp->t.tm_wday == _trp->byday->xxx[i] && _atp->mwday+1==(_trp->byday->req[i]+ _amp->mwday)%_amp->mwday) break; } else { if(_atp->t.tm_wday == _trp->byday->xxx[i]) break; } } } if(i>=_trp->byday->nr) return REC_NOMATCH; } return REC_MATCH; } kamailio-4.0.4/obsolete/cpl-c/cpl_log.c0000644000000000000000000000417512223032460016401 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2003-09-22: created (bogdan) * */ #include #include #include "cpl_log.h" #include "../../mem/mem.h" #include "../../dprint.h" static str cpl_logs[MAX_LOG_NR]; static int nr_logs; void reset_logs() { nr_logs = 0; } void append_log( int nr, ...) { va_list ap; int i; if ( nr_logs+nr>MAX_LOG_NR ) { LOG(L_ERR,"ERROR:cpl-c:append_log: no more space fr logging\n"); return; } va_start(ap, nr); for(i=0;is = 0; log->len = 0; if (nr_logs==0) /* no logs */ return; /* compile the total len */ for(i=0;ilen += cpl_logs[i].len; /* get a buffer */ log->s = (char*)pkg_malloc(log->len); if (log->s==0) { LOG(L_ERR,"ERROR:cpl-c:compile_logs: no more pkg mem\n"); log->len = 0; return; } /*copy all logs into buffer */ p = log->s; for(i=0;i #include "../../str.h" /* looks for s2 into s1 */ static inline char *strcasestr_str(str *s1, str *s2) { int i,j; for(i=0;ilen-s2->len;i++) { for(j=0;jlen;j++) { if ( !((s1->s[i+j]==s2->s[j]) || ( isalpha((int)s1->s[i+j]) && ((s1->s[i+j])^(s2->s[j]))==0x20 )) ) break; } if (j==s2->len) return s1->s+i; } return 0; } #endif kamailio-4.0.4/obsolete/cpl-c/Makefile0000644000000000000000000000064212223032460016251 0ustar rootroot# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=cpl-c.so DEFS +=-I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include # iconv.h LIBS= -L$(LOCALBASE)/lib -lxml2 DEFS+=-DSER_MOD_INTERFACE SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/cpl-c/cpl_db.c0000644000000000000000000001156412223032460016205 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2004-06-06 updated to the new DB api (andrei) */ #include "../../mem/shm_mem.h" #include "../../lib/srdb2/db.h" #include "../../dprint.h" #include "cpl_db.h" static db_ctx_t* ctx = NULL; static db_cmd_t* get_script; static db_cmd_t* write_script; static db_cmd_t* delete_user; void cpl_db_close() { if (delete_user) db_cmd_free(delete_user); delete_user = NULL; if (write_script) db_cmd_free(write_script); write_script = NULL; if (get_script) db_cmd_free(get_script); get_script = NULL; if (ctx) { db_disconnect(ctx); db_ctx_free(ctx); ctx = NULL; } } int cpl_db_init(char* db_url, char* db_table) { db_fld_t cols[] = { {.name = "cpl_bin", .type = DB_BLOB}, {.name = "cpl_xml", .type = DB_STR}, {.name = 0} }; db_fld_t match[] = { {.name = "uid", .type = DB_CSTR}, {.name = 0} }; db_fld_t vals[] = { {.name = "uid", .type = DB_CSTR}, {.name = "cpl_bin", .type = DB_BLOB}, {.name = "cpl_xml", .type = DB_STR }, {.name = 0} }; ctx = db_ctx("cpl-c"); if (ctx == NULL) goto error; if (db_add_db(ctx, db_url) < 0) goto error; if (db_connect(ctx) < 0) goto error; get_script = db_cmd(DB_GET, ctx, db_table, cols, match, NULL); if (!get_script) goto error; write_script = db_cmd(DB_PUT, ctx, db_table, NULL, NULL, vals); if (!write_script) goto error; delete_user = db_cmd(DB_DEL, ctx, db_table, NULL, match, NULL); if (!delete_user) goto error; return 0; error: ERR("cpl-c: Error while initializing db layer\n"); cpl_db_close(); return -1; } /* gets from database the cpl script in binary format; the returned script is * allocated in shared memory * Returns: 1 - success * -1 - error */ int get_user_script(str *user, str *script, int bin) { db_res_t* res = 0; db_rec_t* rec; int i; if (bin) i = 0; else i = 1; get_script->match[0].v.cstr = user->s; DBG("DEBUG:get_user_script: fetching script for user <%s>\n",user->s); if (db_exec(&res, get_script) < 0) { LOG(L_ERR,"ERROR:cpl-c:get_user_script: db_query failed\n"); goto error; } if (!res || !(rec = db_first(res))) { DBG("DEBUG:get_user_script: user <%.*s> not found in db -> probably " "he has no script\n",user->len, user->s); script->s = 0; script->len = 0; } else { if (rec->fld[i].flags & DB_NULL) { DBG("DEBUG:get_user_script: user <%.*s> has a NULL script\n", user->len, user->s); script->s = 0; script->len = 0; } else { DBG("DEBUG:get_user_script: we got the script len=%d\n", rec->fld[i].v.blob.len); script->len = rec->fld[i].v.blob.len; script->s = shm_malloc( script->len ); if (!script->s) { LOG(L_ERR,"ERROR:cpl-c:get_user_script: no free sh_mem\n"); goto error; } memcpy( script->s, rec->fld[i].v.blob.s, script->len); } } if (res) db_res_free(res); return 1; error: if (res) db_res_free(res); script->s = 0; script->len = 0; return -1; } /* inserts into database a cpl script in XML format(xml) along with its binary * format (bin) * Returns: 1 - success * -1 - error */ int write_to_db(char *usr, str *xml, str *bin) { write_script->vals[0].v.cstr = usr; write_script->vals[1].v.blob = *bin; write_script->vals[2].v.lstr = *xml; /* No need to do update/insert here, the db layer does that * automatically without the need to query the db */ if (db_exec(NULL, write_script) < 0) { ERR("cpl-c: Error while writing script into database\n"); return -1; } return 0; } /* delete from database the entity record for a given user - if a user has no * script, he will be removed completely from db; users without script are not * allowed into db ;-) * Returns: 1 - success * -1 - error */ int rmv_from_db(char *usr) { delete_user->match[0].v.cstr = usr; if (db_exec(NULL, delete_user) < 0) { LOG(L_ERR,"ERROR:cpl-c:rmv_from_db: error when deleting script for " "user \"%s\"\n",usr); return -1; } return 1; } kamailio-4.0.4/obsolete/cpl-c/ser-cpl.cfg0000644000000000000000000000056412223032460016642 0ustar rootroot# # Minimalistic configuration file for SER that can be used to # test the CPL module. # debug = 4 fork = no children = 1 log_stderror = yes listen=127.0.0.1 loadpath "./modules" loadmodule "mysql" loadmodule "sl" loadmodule "tm" loadmodule "cpl-c" modparam("cpl-c", "cpl_db", "mysql://ser:heslo@localhost/ser") modparam("cpl-c", "cpl_table", "cpl") route { break; } kamailio-4.0.4/obsolete/uri/0000755000000000000000000000000012223032460014410 5ustar rootrootkamailio-4.0.4/obsolete/uri/checks.c0000644000000000000000000002126512223032460016022 0ustar rootroot/* * $Id$ * * Various URI checks and Request URI manipulation * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2003-02-26: Created by janakj * 2004-03-20: has_totag introduced (jiri) * 2004-04-14: uri_param and add_uri_param introduced (jih) */ #include #include "../../str.h" #include "../../dprint.h" /* Debugging */ #include "../../mem/mem.h" #include "../../sr_module.h" #include "../../parser/digest/digest.h" /* get_authorized_cred */ #include "../../parser/parse_from.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_param.h" #include "../../ut.h" /* Handy utilities */ #include "../../dset.h" #include "uri_mod.h" #include "checks.h" /* * Checks if From includes a To-tag -- good to identify * if a request creates a new dialog */ int has_totag(struct sip_msg* _m, char* _foo, char* _bar) { str tag; if (!_m->to && parse_headers(_m, HDR_TO_F,0)==-1) { LOG(L_ERR, "ERROR: has_totag: To parsing failed\n"); return -1; } if (!_m->to) { LOG(L_ERR, "ERROR: has_totag: no To\n"); return -1; } tag=get_to(_m)->tag_value; if (tag.s==0 || tag.len==0) { DBG("DEBUG: has_totag: no totag\n"); return -1; } DBG("DEBUG: has_totag: totag found\n"); return 1; } /* * Check if the username matches the username in credentials */ int is_user(struct sip_msg* _m, char* _user, char* _str2) { str s; struct hdr_field* h; auth_body_t* c; if (get_str_fparam(&s, _m, (fparam_t *)_user) < 0) { ERR("is_user: failed to recover parameter.\n"); return -1; } get_authorized_cred(_m->authorization, &h); if (!h) { get_authorized_cred(_m->proxy_auth, &h); if (!h) { LOG(L_ERR, "is_user(): No authorized credentials found (error in scripts)\n"); LOG(L_ERR, "is_user(): Call {www,proxy}_authorize before calling is_user function !\n"); return -1; } } c = (auth_body_t*)(h->parsed); if (!c->digest.username.user.len) { DBG("is_user(): Username not found in credentials\n"); return -1; } if (s.len != c->digest.username.user.len) { DBG("is_user(): Username length does not match\n"); return -1; } if (!memcmp(s.s, c->digest.username.user.s, s.len)) { DBG("is_user(): Username matches\n"); return 1; } else { DBG("is_user(): Username differs\n"); return -1; } } /* * Find if Request URI has a given parameter with no value */ int uri_param_1(struct sip_msg* _msg, char* _param, char* _str2) { return uri_param_2(_msg, _param, (char*)0); } /* * Find if Request URI has a given parameter with matching value */ int uri_param_2(struct sip_msg* _msg, char* _param, char* _value) { str param, value, t; param_hooks_t hooks; param_t* params; if (get_str_fparam(¶m, _msg, (fparam_t *)_param) < 0) { ERR("is_user: failed to recover 1st parameter.\n"); return -1; } if (_value) { if (get_str_fparam(&value, _msg, (fparam_t *)_value) < 0) { ERR("is_user: failed to recover 1st parameter.\n"); return -1; } } else { value.s = 0; } if (parse_sip_msg_uri(_msg) < 0) { LOG(L_ERR, "uri_param(): ruri parsing failed\n"); return -1; } t = _msg->parsed_uri.params; if (parse_params(&t, CLASS_ANY, &hooks, ¶ms) < 0) { LOG(L_ERR, "uri_param(): ruri parameter parsing failed\n"); return -1; } while (params) { if ((params->name.len == param.len) && (strncmp(params->name.s, param.s, param.len) == 0)) { if (value.s) { if ((value.len == params->body.len) && strncmp(value.s, params->body.s, value.len) == 0) { goto ok; } else { goto nok; } } else { if (params->body.len > 0) { goto nok; } else { goto ok; } } } else { params = params->next; } } nok: free_params(params); return -1; ok: free_params(params); return 1; } /* * Adds a new parameter to Request URI */ int add_uri_param(struct sip_msg* _msg, char* _param, char* _s2) { str param, *cur_uri, new_uri; struct sip_uri *parsed_uri; char *at; if (get_str_fparam(¶m, _msg, (fparam_t *)_param) < 0) { ERR("add_uri_param: failed to recover parameter.\n"); return -1; } if (param.len == 0) { return 1; } if (parse_sip_msg_uri(_msg) < 0) { LOG(L_ERR, "add_uri_param(): ruri parsing failed\n"); return -1; } parsed_uri = &(_msg->parsed_uri); /* if current ruri has no headers, pad param at the end */ if (parsed_uri->headers.len == 0) { cur_uri = GET_RURI(_msg); new_uri.len = cur_uri->len + param.len + 1; if (new_uri.len > MAX_URI_SIZE) { LOG(L_ERR, "add_uri_param(): new ruri too long\n"); return -1; } new_uri.s = pkg_malloc(new_uri.len); if (new_uri.s == 0) { LOG(L_ERR, "add_uri_param(): Memory allocation failure\n"); return -1; } memcpy(new_uri.s, cur_uri->s, cur_uri->len); *(new_uri.s + cur_uri->len) = ';'; memcpy(new_uri.s + cur_uri->len + 1, param.s, param.len); if (rewrite_uri(_msg, &new_uri ) == 1) { goto ok; } else { goto nok; } } /* otherwise take the long path */ new_uri.len = 4 + (parsed_uri->user.len ? parsed_uri->user.len + 1 : 0) + (parsed_uri->passwd.len ? parsed_uri->passwd.len + 1 : 0) + parsed_uri->host.len + (parsed_uri->port.len ? parsed_uri->port.len + 1 : 0) + parsed_uri->params.len + param.len + 1 + parsed_uri->headers.len + 1; if (new_uri.len > MAX_URI_SIZE) { LOG(L_ERR, "add_uri_param(): new ruri too long\n"); return -1; } new_uri.s = pkg_malloc(new_uri.len); if (new_uri.s == 0) { LOG(L_ERR, "add_uri_param(): Memory allocation failure\n"); return -1; } at = new_uri.s; memcpy(at, "sip:", 4); at = at + 4; if (parsed_uri->user.len) { memcpy(at, parsed_uri->user.s, parsed_uri->user.len); if (parsed_uri->passwd.len) { *at = ':'; at = at + 1; memcpy(at, parsed_uri->passwd.s, parsed_uri->passwd.len); at = at + parsed_uri->passwd.len; }; *at = '@'; at = at + 1; } memcpy(at, parsed_uri->host.s, parsed_uri->host.len); at = at + parsed_uri->host.len; if (parsed_uri->port.len) { *at = ':'; at = at + 1; memcpy(at, parsed_uri->port.s, parsed_uri->port.len); at = at + parsed_uri->port.len; } memcpy(at, parsed_uri->params.s, parsed_uri->params.len); at = at + parsed_uri->params.len; *at = ';'; at = at + 1; memcpy(at, param.s, param.len); at = at + param.len; *at = '?'; at = at + 1; memcpy(at, parsed_uri->headers.s, parsed_uri->headers.len); if (rewrite_uri(_msg, &new_uri) == 1) { goto ok; } nok: pkg_free(new_uri.s); return -1; ok: pkg_free(new_uri.s); return 1; } /* * Converts Request-URI, if it is tel URI, to SIP URI. Returns 1, if * conversion succeeded or if no conversion was needed, i.e., Request-URI * was not tel URI. Returns -1, if conversion failed. */ int tel2sip(struct sip_msg* _msg, char* _s1, char* _s2) { str *ruri, furi; struct sip_uri pfuri; str suri; char* at; ruri = GET_RURI(_msg); if (ruri->len < 4) return 1; if (strncmp(ruri->s, "tel:", 4) != 0) return 1; if (parse_from_header(_msg) < 0) { LOG(L_ERR, "tel2sip(): Error while parsing From header\n"); return -1; } furi = get_from(_msg)->uri; if (parse_uri(furi.s, furi.len, &pfuri) < 0) { LOG(L_ERR, "tel2sip(): Error while parsing From URI\n"); return -1; } suri.len = 4 + ruri->len - 4 + 1 + pfuri.host.len + 1 + 10; suri.s = pkg_malloc(suri.len); if (suri.s == 0) { LOG(L_ERR, "tel2sip(): Memory allocation failure\n"); return -1; } at = suri.s; memcpy(at, "sip:", 4); at = at + 4; memcpy(at, ruri->s + 4, ruri->len - 4); at = at + ruri->len - 4; *at = '@'; at = at + 1; memcpy(at, pfuri.host.s, pfuri.host.len); at = at + pfuri.host.len; *at = ';'; at = at + 1; memcpy(at, "user=phone", 10); LOG(L_ERR, "tel2sip(): SIP URI is <%.*s>\n", suri.len, suri.s); if (rewrite_uri(_msg, &suri) == 1) { pkg_free(suri.s); return 1; } else { pkg_free(suri.s); return -1; } } kamailio-4.0.4/obsolete/uri/doc/0000755000000000000000000000000012223032460015155 5ustar rootrootkamailio-4.0.4/obsolete/uri/doc/functions.xml0000644000000000000000000000226612223032460017715 0ustar rootroot
Functions
<function>is_user(username)</function> Check if the username in credentials matches the given username. Meaning of the parameters is as follows: username - Username string or AVP. <function>is_user</function> usage ... if (is_user("jan")) { ... }; ...
<function>has_totag()</function> Check if To header field uri contains tag parameter. <function>has_totag</function> usage ... if (has_totag()) { ... }; ...
kamailio-4.0.4/obsolete/uri/doc/uri.xml0000644000000000000000000000142312223032460016476 0ustar rootroot
Jan Janak FhG FOKUS
jan@iptel.org
2003 FhG FOKUS
Uri Module
Overview Various checks related to SIP URI.
kamailio-4.0.4/obsolete/uri/doc/Makefile0000644000000000000000000000012412223032460016612 0ustar rootrootdocs = uri.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/uri/doc/params.xml0000644000000000000000000000047512223032460017170 0ustar rootroot
Parameters
kamailio-4.0.4/obsolete/uri/README0000644000000000000000000000130612223032460015270 0ustar rootroot1. Uri Module Jan Janak FhG FOKUS Copyright © 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. Functions 1.2.1. is_user(username) 1.2.2. has_totag() 1.1. Overview Various checks related to SIP URI. 1.2. Functions 1.2.1. is_user(username) Check if the username in credentials matches the given username. Meaning of the parameters is as follows: * username - Username string or AVP. Example 1. is_user usage ... if (is_user("jan")) { ... }; ... 1.2.2. has_totag() Check if To header field uri contains tag parameter. Example 2. has_totag usage ... if (has_totag()) { ... }; ... kamailio-4.0.4/obsolete/uri/uri_mod.h0000644000000000000000000000233512223032460016222 0ustar rootroot/* * $Id$ * * Various URI related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2003-02-26: created by janakj */ #ifndef URI_MOD_H #define URI_MOD_H #include "../../lib/srdb2/db.h" #include "../../str.h" #endif /* URI_MOD_H */ kamailio-4.0.4/obsolete/uri/uri_mod.c0000644000000000000000000000517312223032460016220 0ustar rootroot/* * $Id$ * * Various URI related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-03-11: New module interface (janakj) * 2003-03-16: flags export parameter added (janakj) * 2003-03-19 replaces all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-04-05: default_uri #define used (jiri) * 2004-03-20: has_totag introduced (jiri) * 2004-04-14: uri_param and add_uri_param introduced (jih) * 2004-05-03: tel2sip introduced (jih) */ #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../ut.h" #include "../../error.h" #include "../../mem/mem.h" #include "uri_mod.h" #include "checks.h" MODULE_VERSION /* * Exported functions */ static cmd_export_t cmds[] = { {"is_user", is_user, 1, fixup_var_str_1, REQUEST_ROUTE}, {"has_totag", has_totag, 0, 0, REQUEST_ROUTE}, {"uri_param", uri_param_1, 1, fixup_str_1, REQUEST_ROUTE}, {"uri_param", uri_param_2, 2, fixup_str_12, REQUEST_ROUTE}, {"add_uri_param", add_uri_param, 1, fixup_str_1, REQUEST_ROUTE}, {"tel2sip", tel2sip, 0, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {0, 0, 0} }; /* * Module interface */ struct module_exports exports = { "uri", cmds, /* Exported functions */ 0, /* RPC methods */ params, /* Exported parameters */ 0, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0, /* oncancel function */ 0 /* child initialization function */ }; kamailio-4.0.4/obsolete/uri/checks.h0000644000000000000000000000416412223032460016026 0ustar rootroot/* * $Id$ * * Various URI checks * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2003-03-26 created by janakj * 2004-04-14 uri_param and add_uri_param introduced (jih) */ #ifndef CHECKS_H #define CHECKS_H #include "../../parser/msg_parser.h" /* * Check if given username matches those in digest credentials */ int is_user(struct sip_msg* _msg, char* _user, char* _str2); /* * Check if message includes a to-tag */ int has_totag(struct sip_msg* _m, char* _foo, char* _bar); /* * Find if Request URI has a given parameter with no value */ int uri_param_1(struct sip_msg* _msg, char* _param, char* _str2); /* * Find if Request URI has a given parameter with matching value */ int uri_param_2(struct sip_msg* _msg, char* _param, char* _value); /* * Adds a new parameter to Request URI */ int add_uri_param(struct sip_msg* _msg, char* _param, char* _s2); /* * Converts Request-URI, if it is tel URI, to SIP URI. Returns 1, if * conversion succeeded or if no conversion was needed, i.e., Request-URI * was not tel URI. Returns -1, if conversion failed. */ int tel2sip(struct sip_msg* _msg, char* _s1, char* _s2); #endif /* CHECKS_H */ kamailio-4.0.4/obsolete/uri/Makefile0000644000000000000000000000037212223032460016052 0ustar rootroot# $Id$ # # Various URI checks # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=uri.so LIBS= DEFS+=-DSER_MOD_INTERFACE SERLIBPATH=../../lib include ../../Makefile.modules kamailio-4.0.4/obsolete/speeddial/0000755000000000000000000000000012223032460015543 5ustar rootrootkamailio-4.0.4/obsolete/speeddial/speeddial.c0000644000000000000000000001133712223032460017646 0ustar rootroot/* * $Id$ * * Copyright (C) 2004 Voice Sistem SRL * * This file is part of SIP Express Router. * * SPEEDDIAL SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * SPEEDDIAL SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * */ #include #include #include "../../sr_module.h" #include "../../lib/srdb2/db.h" #include "../../dprint.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../modules/sl/sl.h" #include "sdlookup.h" #include "speeddial.h" MODULE_VERSION /* Module destroy function prototype */ static void destroy(void); /* Module child-init function prototype */ static int child_init(int rank); /* Module initialization function prototype */ static int mod_init(void); static int sd_lookup_fixup(void** param, int param_no); /* Module parameter variables */ char* db_url = DEFAULT_RODB_URL; char* uid_column = "uid"; char* dial_username_column = "dial_username"; char* dial_did_column = "dial_did"; char* new_uri_column = "new_uri"; db_ctx_t* db=NULL; struct db_table_name* tables = NULL; unsigned int tables_no = 0; sl_api_t slb; /* Exported functions */ static cmd_export_t cmds[] = { {"sd_lookup", sd_lookup, 1, sd_lookup_fixup, REQUEST_ROUTE}, {0, 0, 0, 0, 0} }; /* Exported parameters */ static param_export_t params[] = { {"db_url", PARAM_STRING, &db_url }, {"uid_column", PARAM_STRING, &uid_column }, {"dial_username_column", PARAM_STRING, &dial_username_column }, {"dial_did_column", PARAM_STRING, &dial_did_column }, {"new_uri_column", PARAM_STRING, &new_uri_column }, {0, 0, 0} }; /* Module interface */ struct module_exports exports = { "speeddial", cmds, /* Exported functions */ 0, /* RPC params */ params, /* Exported parameters */ mod_init, /* module initialization function */ 0, /* response function */ destroy, /* destroy function */ 0, /* oncancel function */ child_init /* child initialization function */ }; static int build_db_cmds(void) { int i; db_fld_t match[] = { {.name = uid_column, .type = DB_STR}, {.name = dial_did_column, .type = DB_STR}, {.name = dial_username_column, .type = DB_STR}, {.name = NULL} }; db_fld_t cols[] = { {.name = new_uri_column, .type = DB_STR}, {.name = NULL} }; for(i = 0; i < tables_no; i++) { tables[i].lookup_num = db_cmd(DB_GET, db, tables[i].table, cols, match, NULL); if (tables[i].lookup_num == NULL) { ERR("speeddial: Error while preparing database commands\n"); goto error; } } return 0; error: i--; while(i >= 0) { db_cmd_free(tables[i].lookup_num); tables[i].lookup_num = NULL; i--; } return -1; } /** * */ static int child_init(int rank) { if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN) return 0; /* do nothing for the main or tcp_main processes */ db = db_ctx("speeddial"); if (db == NULL) { ERR("Error while initializing database layer\n"); return -1; } if (db_add_db(db, db_url) < 0) return -1; if (db_connect(db) < 0) return -1; if (build_db_cmds() < 0) { pkg_free(tables); return -1; } return 0; } /** * */ static int mod_init(void) { DBG("speeddial module - initializing\n"); /* bind the SL API */ if (sl_load_api(&slb)!=0) { LM_ERR("cannot bind to SL API\n"); return -1; } return 0; } /** * */ static void destroy(void) { int i; if (tables) { for(i = 0; i < tables_no; i++) { if (tables[i].lookup_num) db_cmd_free(tables[i].lookup_num); } pkg_free(tables); } } static int sd_lookup_fixup(void** param, int param_no) { struct db_table_name* ptr; if (param_no == 1) { ptr = pkg_realloc(tables, sizeof(struct db_table_name) * (tables_no + 1)); if (ptr == NULL) { ERR("No memory left\n"); return -1; } ptr[tables_no].table = (char*)*param; ptr[tables_no].lookup_num = NULL; *param = (void*)(long)tables_no; tables_no++; tables = ptr; } return 0; } kamailio-4.0.4/obsolete/speeddial/sdlookup.h0000644000000000000000000000226412223032460017560 0ustar rootroot/* * $Id$ * * Copyright (C) 2004 Voice Sistem SRL * * This file is part of SIP Express Router. * * SPEEDDIAL SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * SPEEDDIAL SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * */ #ifndef _SDLOOKUP_H_ #define _SDLOOKUP_H_ #include "../../parser/msg_parser.h" int sd_lookup(struct sip_msg* _msg, char* _index, char* _str2); #endif /* _SDLOOKUP_H_ */ kamailio-4.0.4/obsolete/speeddial/doc/0000755000000000000000000000000012223032460016310 5ustar rootrootkamailio-4.0.4/obsolete/speeddial/doc/functions.xml0000644000000000000000000000163412223032460021046 0ustar rootroot
Functions
<function>sd_lookup(table)</function> Print a formated message using LOG function. Meaning of the parameters is as follows: table - The name of the table storing the speed dial records. <function>sd_lookup</function> usage ... if (uri =~ "sip:[0-9]{2}@.*") { sd_lookup("speeddial"); } ...
kamailio-4.0.4/obsolete/speeddial/doc/speeddial.sql0000644000000000000000000000054512223032460020767 0ustar rootrootCREATE TABLE speed_dial ( username varchar(64) NOT NULL default '', domain varchar(128) NOT NULL default '', sd_username varchar(64) NOT NULL default '', sd_domain varchar(128) NOT NULL default '', new_uri varchar(192) NOT NULL default '', description varchar(64) NOT NULL default '', PRIMARY KEY (username, domain, sd_domain, sd_username) ); kamailio-4.0.4/obsolete/speeddial/doc/speeddial.xml0000644000000000000000000000514112223032460020765 0ustar rootroot
Elena-Ramona Modroiu Asipto
ramona@asipto.com http://www.asipto.com
2004 Voice Sistem
Speedial Module
Overview This module provides on-server speed dial facilities. An user can store records consisting of pairs short numbers (2 digits) and SIP addresses into a table of SER. Then it can dial the two digits whenever he wants to call the SIP address associated with those digits.
Dependencies The following modules must be loaded before this module: database module (mysql, dbtext, ...). sl - stateless module.
Installation And Running
Database setup Before using speeddial module, you have to create the database table where the short dial addresses are stored. If the table was not created at the installation time, you can use the following SQL script (tested with MySQL) as template. Database, the table name, and column names can be set with module parameters so they can be changed. speeddial sql script
Example SER Configuration FIle SER Configuration File
kamailio-4.0.4/obsolete/speeddial/doc/faq.xml0000644000000000000000000000275112223032460017606 0ustar rootroot
Frequently Asked Questions Where can I post a question about this module? Sent an email to sr-users@lists.sip-router.org or, Remember: first at all, check if your question was already answered on one of SER mailing lists: serusers mailing list serdev mailing list How can I report a bug? Accumulate as much as possible information (SER version, ser -V output, your OS (uname -a), SER logs, network dumps, core dump files, configuration file) and send a mail to support@voice-system.ro
kamailio-4.0.4/obsolete/speeddial/doc/speeddial.cfg0000644000000000000000000000360712223032460020731 0ustar rootroot# # $Id$ # # sample config script to use speeddial module # # ----------- global configuration parameters ------------------------ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) fifo="/tmp/ser_fifo" # ------------------ module loading ---------------------------------- loadmodule "/usr/local/lib/ser/modules/sl.so" loadmodule "/usr/local/lib/ser/modules/tm.so" loadmodule "/usr/local/lib/ser/modules/rr.so" loadmodule "/usr/local/lib/ser/modules/maxfwd.so" loadmodule "/usr/local/lib/ser/modules/usrloc.so" loadmodule "/usr/local/lib/ser/modules/registrar.so" loadmodule "/usr/local/lib/ser/modules/textops.so" loadmodule "/usr/local/lib/ser/modules/mysql.so" loadmodule "/usr/local/lib/ser/modules/speeddial.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- modparam("usrloc", "db_mode", 0) # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); break; }; if (!method=="REGISTER") record_route(); if (loose_route()) { if (!t_relay()) { sl_reply_error(); }; break; }; if (!uri==myself) { if (!t_relay()) { sl_reply_error(); }; break; }; if (uri==myself) { if (method=="REGISTER") { save("location"); break; }; if(uri=~"sip:[0-9]{2}@.*") sd_lookup("speeddial"); lookup("aliases"); if (!uri==myself) { if (!t_relay()) { sl_reply_error(); }; break; }; if (!lookup("location")) { sl_send_reply("404", "Not Found"); break; }; }; if (!t_relay()) { sl_reply_error(); }; } kamailio-4.0.4/obsolete/speeddial/doc/Makefile0000644000000000000000000000013212223032460017744 0ustar rootrootdocs = speeddial.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/speeddial/doc/params.xml0000644000000000000000000001011112223032460020307 0ustar rootroot
Parameters
<varname>db_url</varname> (string) The URL of database where the table containing speed dial records. Default value is DEFAULT_RODB_URL. Set <varname>db_url</varname> parameter ... modparam("speeddial", "db_url", "mysql://ser:xxx@localhost/ser") ...
<varname>user_column</varname> (string) The name of column storing the user name of the owner of the speed dial record. Default value is "username". Set <varname>user_column</varname> parameter ... modparam("speeddial", "user_column", "userid") ...
<varname>domain_column</varname> (string) The name of column storing the domain of the owner of the speed dial record. Default value is "domain". Set <varname>domain_column</varname> parameter ... modparam("speeddial", "domain_column", "userdomain") ...
<varname>sd_user_column</varname> (string) The name of the column storing the user part of the short dial address. Default value is "sd_username". Set <varname>sd_user_column</varname> parameter ... modparam("speeddial", "sd_user_column", "short_user") ...
<varname>sd_domain_column</varname> (string) The name of the column storing the domain of the short dial address. Default value is "sd_domain". Set <varname>sd_domain_column</varname> parameter ... modparam("speeddial", "sd_domain_column", "short_domain") ...
<varname>new_uri_column</varname> (string) The name of the column containing the URI that will be use to replace the short dial URI. Default value is "new_uri". Set <varname>new_uri_column</varname> parameter ... modparam("speeddial", "new_uri_column", "real_uri") ...
<varname>domain_prefix</varname> (string) If the domain of the owner (From URI) starts with the value of this parameter, then it is stripped before performing the lookup of the short number. Default value is NULL. Set <varname>domain_prefix</varname> parameter ... modparam("speeddial", "domain_prefix", "tel.") ...
<varname>use_domain</varname> (int) The parameter specifies wheter or not to use the domain when searching a speed dial record (0 - no domain, 1 - use domain from From URI, 2 - use both domains, from From URI and from request URI). Default value is 0. Set <varname>use_domain</varname> parameter ... modparam("speeddial", "use_domain", 1) ...
kamailio-4.0.4/obsolete/speeddial/README0000644000000000000000000001733712223032460016436 0ustar rootroot1. Speedial Module Elena-Ramona Modroiu Asipto Copyright © 2004 Voice Sistem __________________________________________________________________ 1.1. Overview 1.2. Dependencies 1.3. Installation And Running 1.3.1. Database setup 1.3.2. Example SER Configuration FIle 1.4. Parameters 1.4.1. db_url (string) 1.4.2. user_column (string) 1.4.3. domain_column (string) 1.4.4. sd_user_column (string) 1.4.5. sd_domain_column (string) 1.4.6. new_uri_column (string) 1.4.7. domain_prefix (string) 1.4.8. use_domain (int) 1.5. Functions 1.5.1. sd_lookup(table) 1.6. Frequently Asked Questions 1.1. Overview This module provides on-server speed dial facilities. An user can store records consisting of pairs short numbers (2 digits) and SIP addresses into a table of SER. Then it can dial the two digits whenever he wants to call the SIP address associated with those digits. 1.2. Dependencies The following modules must be loaded before this module: * database module (mysql, dbtext, ...). * sl - stateless module. 1.3. Installation And Running 1.3.1. Database setup Before using speeddial module, you have to create the database table where the short dial addresses are stored. If the table was not created at the installation time, you can use the following SQL script (tested with MySQL) as template. Database, the table name, and column names can be set with module parameters so they can be changed. Example 1. speeddial sql script CREATE TABLE speed_dial ( username varchar(64) NOT NULL default '', domain varchar(128) NOT NULL default '', sd_username varchar(64) NOT NULL default '', sd_domain varchar(128) NOT NULL default '', new_uri varchar(192) NOT NULL default '', description varchar(64) NOT NULL default '', PRIMARY KEY (username, domain, sd_domain, sd_username) ); 1.3.2. Example SER Configuration FIle Example 2. SER Configuration File # # $Id$ # # sample config script to use speeddial module # # ----------- global configuration parameters ------------------------ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) fifo="/tmp/ser_fifo" # ------------------ module loading ---------------------------------- loadmodule "/usr/local/lib/ser/modules/sl.so" loadmodule "/usr/local/lib/ser/modules/tm.so" loadmodule "/usr/local/lib/ser/modules/rr.so" loadmodule "/usr/local/lib/ser/modules/maxfwd.so" loadmodule "/usr/local/lib/ser/modules/usrloc.so" loadmodule "/usr/local/lib/ser/modules/registrar.so" loadmodule "/usr/local/lib/ser/modules/textops.so" loadmodule "/usr/local/lib/ser/modules/mysql.so" loadmodule "/usr/local/lib/ser/modules/speeddial.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- modparam("usrloc", "db_mode", 0) # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); break; }; if (!method=="REGISTER") record_route(); if (loose_route()) { if (!t_relay()) { sl_reply_error(); }; break; }; if (!uri==myself) { if (!t_relay()) { sl_reply_error(); }; break; }; if (uri==myself) { if (method=="REGISTER") { save("location"); break; }; if(uri=~"sip:[0-9]{2}@.*") sd_lookup("speeddial"); lookup("aliases"); if (!uri==myself) { if (!t_relay()) { sl_reply_error(); }; break; }; if (!lookup("location")) { sl_send_reply("404", "Not Found"); break; }; }; if (!t_relay()) { sl_reply_error(); }; } 1.4. Parameters 1.4.1. db_url (string) The URL of database where the table containing speed dial records. Default value is DEFAULT_RODB_URL. Example 3. Set db_url parameter ... modparam("speeddial", "db_url", "mysql://ser:xxx@localhost/ser") ... 1.4.2. user_column (string) The name of column storing the user name of the owner of the speed dial record. Default value is "username". Example 4. Set user_column parameter ... modparam("speeddial", "user_column", "userid") ... 1.4.3. domain_column (string) The name of column storing the domain of the owner of the speed dial record. Default value is "domain". Example 5. Set domain_column parameter ... modparam("speeddial", "domain_column", "userdomain") ... 1.4.4. sd_user_column (string) The name of the column storing the user part of the short dial address. Default value is "sd_username". Example 6. Set sd_user_column parameter ... modparam("speeddial", "sd_user_column", "short_user") ... 1.4.5. sd_domain_column (string) The name of the column storing the domain of the short dial address. Default value is "sd_domain". Example 7. Set sd_domain_column parameter ... modparam("speeddial", "sd_domain_column", "short_domain") ... 1.4.6. new_uri_column (string) The name of the column containing the URI that will be use to replace the short dial URI. Default value is "new_uri". Example 8. Set new_uri_column parameter ... modparam("speeddial", "new_uri_column", "real_uri") ... 1.4.7. domain_prefix (string) If the domain of the owner (From URI) starts with the value of this parameter, then it is stripped before performing the lookup of the short number. Default value is NULL. Example 9. Set domain_prefix parameter ... modparam("speeddial", "domain_prefix", "tel.") ... 1.4.8. use_domain (int) The parameter specifies wheter or not to use the domain when searching a speed dial record (0 - no domain, 1 - use domain from From URI, 2 - use both domains, from From URI and from request URI). Default value is 0. Example 10. Set use_domain parameter ... modparam("speeddial", "use_domain", 1) ... 1.5. Functions 1.5.1. sd_lookup(table) Print a formated message using LOG function. Meaning of the parameters is as follows: * table - The name of the table storing the speed dial records. Example 11. sd_lookup usage ... if (uri =~ "sip:[0-9]{2}@.*") { sd_lookup("speeddial"); } ... 1.6. Frequently Asked Questions 1.6.1. Where can I post a question about this module? 1.6.2. How can I report a bug? 1.6.1. Where can I post a question about this module? Sent an email to or, Remember: first at all, check if your question was already answered on one of SER mailing lists: * serusers mailing list * serdev mailing list 1.6.2. How can I report a bug? Accumulate as much as possible information (SER version, ser -V output, your OS (uname -a), SER logs, network dumps, core dump files, configuration file) and send a mail to kamailio-4.0.4/obsolete/speeddial/sdlookup.c0000644000000000000000000000754012223032460017555 0ustar rootroot/* * $Id$ * * Copyright (C) 2004 Voice Sistem SRL * * This file is part of SIP Express Router. * * SPEEDDIAL SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * SPEEDDIAL SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * */ #include #include "../../dprint.h" #include "../../action.h" #include "../../config.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../lib/srdb2/db.h" #include "../../id.h" #include "../../dset.h" #include "speeddial.h" #include "sdlookup.h" #define MAX_USERURI_SIZE 256 static char useruri_buf[MAX_USERURI_SIZE]; /** * */ int sd_lookup(struct sip_msg* _msg, char* _index, char* _str2) { int i; str user_s, uid, did; db_res_t* res = NULL; db_rec_t* rec; /* init */ i = (int)(long)_index; /* Retrieve the owner of the record */ if (get_from_uid(&uid, _msg) < 0) { LOG(L_ERR, "sd_lookup: Unable to get user identity\n"); return -1; } /* Retrieve the called domain id */ if (get_to_did(&did, _msg) < 0) { LOG(L_ERR, "sd_lookup: Destination domain ID not known\n"); return -1; } tables[i].lookup_num->match[0].v.lstr = uid; tables[i].lookup_num->match[1].v.lstr = did; /* Get the called username */ if (parse_sip_msg_uri(_msg) < 0) { LOG(L_ERR, "sd_lookup: Error while parsing Request-URI\n"); goto err_badreq; } tables[i].lookup_num->match[2].v.lstr = _msg->parsed_uri.user; DBG("speeddial: Looking up (uid:%.*s,username:%.*s,did:%.*s)\n", uid.len, uid.s, _msg->parsed_uri.user.len, _msg->parsed_uri.user.s, did.len, did.s); if (db_exec(&res, tables[i].lookup_num) < 0) { ERR("speeddial: Error while executing database command\n"); goto err_server; } if (res == NULL) { DBG("speeddial: No SIP URI found for speeddial (num:%.*s, uid:%.*s," " did:%.*s)\n", _msg->parsed_uri.user.len, _msg->parsed_uri.user.s, uid.len, uid.s, did.len, did.s); return -1; } user_s.s = useruri_buf + 4; rec = db_first(res); while(rec) { if (rec->fld[0].flags & DB_NULL) goto skip; strncpy(user_s.s, rec->fld[0].v.lstr.s, rec->fld[0].v.lstr.len); user_s.len = rec->fld[0].v.lstr.len; user_s.s[user_s.len] = '\0'; goto out; skip: rec = db_next(res); } if (rec == NULL) { DBG("speeddial: No usable SIP URI found for (num:%.*s, uid:%.*s," " did:%.*s)\n", _msg->parsed_uri.user.len, _msg->parsed_uri.user.s, uid.len, uid.s, did.len, did.s); db_res_free(res); return -1; } out: /* check 'sip:' */ if(user_s.len<4 || strncmp(user_s.s, "sip:", 4)) { memcpy(useruri_buf, "sip:", 4); user_s.s -= 4; user_s.len += 4; } db_res_free(res); /* set the URI */ DBG("sd_lookup: URI of sd from R-URI [%s]\n", user_s.s); if(rewrite_uri(_msg, &user_s)<0) { LOG(L_ERR, "sd_lookup: Cannot replace the R-URI\n"); goto err_server; } return 1; err_server: if (slb.zreply(_msg, 500, "Server Internal Error") == -1) { LOG(L_ERR, "sd_lookup: Error while sending reply\n"); } return 0; err_badreq: if (slb.zreply(_msg, 400, "Bad Request") == -1) { LOG(L_ERR, "sd_lookup: Error while sending reply\n"); } return 0; } kamailio-4.0.4/obsolete/speeddial/speeddial.h0000644000000000000000000000300512223032460017644 0ustar rootroot/* * $Id$ * * Copyright (C) 2004 Voice Sistem SRL * * This file is part of SIP Express Router. * * SPEEDDIAL SER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * SPEEDDIAL SER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * * History: * --------- * */ #ifndef _SPEEDDIAL_H_ #define _SPEEDDIAL_H_ #include "../../lib/srdb2/db.h" #include "../../modules/sl/sl.h" #include "../../parser/msg_parser.h" /* Module parameters variables */ extern char* uid_column; extern char* dial_username_column; extern char* dial_did_column; extern char* new_uri_column; extern db_ctx_t* db; struct db_table_name { char* table; db_cmd_t* lookup_num; }; extern struct db_table_name* tables; extern unsigned int tables_no; extern sl_api_t slb; #endif /* _SPEEDDIAL_H_ */ kamailio-4.0.4/obsolete/speeddial/Makefile0000644000000000000000000000045112223032460017203 0ustar rootroot# $Id$ # # SPEEDDIAL Module # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=speeddial.so LIBS= DEFS+= DEFS+=-DSER_MOD_INTERFACE SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/eval/0000755000000000000000000000000012223032460014540 5ustar rootrootkamailio-4.0.4/obsolete/eval/README0000644000000000000000000002363212223032460015426 0ustar rootrootEval module =========== Author: tomas.mandys at iptel dot org The module implements expression evaluation in route script, i.e. enables e.g. addition, concatanation, value items etc. There are two basic types: integer and string. Operation are processed using values on stack and polish notation. Besides the stack there are also register that may be accessed by quick manner in run time, via select or stack manipulation functions, because they are fixed during fixup phase. Module parameters: ----------------- declare_register: string; Declares one register, multiple declaration supported Example: modparam("eval", "declare_register", "ax"); modparam("eval", "declare_register", "bx"); modparam("eval", "declare_register", "cx"); modparam("eval", "declare_register", "dx"); xlbuf_size: int; Default: 4096 Size of buffer for xlib formating. Module functions: ---------------- location: Location for stack manipulation operation. Thare are supported int/str constant, AVP, register, xlib formatted string integers: any value (included string) that may be converted to int string: any string, prefix "s:" forces string even when is "integer" xlib: string prefixed by "x:" AVP: an AVP compliant string starting with "$" register: register name declared using "declare_register" and starting with "r:" function: get value from function, "f:" Currently are supported following functions: time ... get time in seconds uuid ... get uuid using uuid library stackno ... get current stack size Example: "f:time" Note that result of any select may be used using xlib string, e.g. "x:%@eval.get[0]" stack_pos: Position on the stack. Values greater or equal than zero are counted from top, negative values counted from bottom, i.e. 0..topmost position, -1..bottommost position Example: Value Idx as or 3456 0 -5 5678 1 -4 87655 2 -3 76534 3 -2 -1543 4 -1 Stack manipulation functions return TRUE/FALSE if operation was succesfull or not. Every stack_pos/num may be evaluated in runtime using location (AVP, register, xlstr). eval_add(location[, stack_pos]); eval_push(location[, stack_pos]); Default: stack_pos: -1 Add value bellow stack_pos item. Example: eval_push("1"); eval_push("$a"); eval_push("x:%ru", -2); $pos = 1; eval_push("ABC", "$pos"); eval_insert(location[, stack_pos]); Default: stack_pos: 0 Add value above stack_pos item. Example: eval_insert("s:1"); eval_push("f:uuid"); eval_push("f:time"); eval_xchg(location[, stack_pos]); Default: stack_pos: 0 Exchange values between stack and location. Note that only register location is allowed. eval_get(location[, stack_pos]); Default: stack_pos: 0 Move value from stack to (writable) location, i.e. AVP, register. Note that enables assigning AVP value also as interger (vs. select that returns alway string) Example: eval_get("$avp", 2); eval_put(location[, stack_pos]); Default: stack_pos: 0 Move value from location to stack. eval_add_value(location[, num]); Default: num: -1 Split value (comma delimited items) and add to stack Example: eval_add_value("One,Two,Three,Four"); # add 4 items stack eval_insert_value(location[, num]); Default: num: 0 Split value (comma delimited items) and insert to stack eval_pop(location[, stack_pos]); Default: stack_pos: 0 Get value from writable location and remove it from the stack eval_remove([stack_pos[, num]]); Default: stack_pos: 0; num: 1 Remove num items from the stack starting at stack_pos. If num is negative then stops removing (-num) element before bottom. Example: $pos = 1; $num = 3; eval_remove("$pos", "$num"); eval_clear(what); Clear stack (if bit0 in what set) or registers (if bit1 in what set) eval_oper(operations[, stack_pos]); Default: stack_pos: 0 Perform comma delimited operations relating to stack_pos, i.e. get n parameters (stack_pos, stack_pos+1, ...stack_pos+n-1), put result at stack_pos and remove params (stack_pos+1, ...stack_pos+n-1). When not enough params availble then false is returned, error logged and stack is in state in time of error (i.e. probably unpredictable). Operation may contain location (avp,xlib str,register) to evaluate function in runtime, i.e. indirect functions. Note that particular operation is evaluated, not complete string: $op1="abs"; $op2="/"; eval_oper("$op1,$op2,+,*"); # OK $op = "abs,/,+,*"; eval_oper("$op"); # BAD Example: eval_push("2"); eval_push("3"); eval_push("9"); eval_oper("+,*"); # i.e. (2+3)*9 = 54 eval_insert("100", 0); $op="-"; eval_oper("$op"); # i.e. 100-54 = 64 eval_while(route_no [, stack_pos]) Default: stack_pos: 0 Implements simple looping. Route_no is called while stack[stack_pos] is true, i.e. non empty integer non equal zero or non empty string. Loop is also interrupted if route_no returns value <= 0. Example: route[LOOP] { xplog("L_ERR", "%@eval.get[1] = %@eval.pop[2]\n"); eval_oper("dec"); eval_oper("inc", 1); } eval_push(3); eval_push(1); eval_push("One"); eval_push("Two"); eval_push("Three"); eval_while("LOOP"); eval_remove(0, 2); Example for mathematicians using ser as scientic calculator: route["FACTORIAL"] { if (@eval.get[0] == "-1") { # first loop ? xplog("L_ERR", "How much is factorial of %@eval.get[1] ? \n"); eval_get("r:tmp", 1); eval_put("r:tmp"); eval_oper("sgn"); if (@eval.get[0] == "0" || @eval.reg.tmp == "1") { # zero is exception, one is optimization xplog("L_ERR", "1\n"); eval_put(1); eval_remove(1); return -1; } else if (@eval.get[0] == "-1") { # negative x is nonsense xplog("L_ERR", "not defined\n"); eval_remove(1); return -1; } # stack[0] is 1 } eval_get("r:tmp", 1); # save x eval_oper("*"); # r *= x if (@eval.reg.tmp == "2") { xplog("L_ERR", "%@eval.get[0]\n"); return -1; } eval_push("r:tmp"); eval_oper("dec", 1); # x-- } route{ eval_push("-1"); # condition eval_push(0); eval_while("FACTORIAL"); eval_remove(); eval_push("-1"); # condition eval_push(1); eval_while("FACTORIAL"); eval_remove(); eval_push("-1"); # condition eval_push(-3); eval_while("FACTORIAL"); eval_remove(); eval_push("-1"); # condition eval_push(9); eval_while("FACTORIAL"); eval_remove(); Keen ser user will implement inverse hyperbolic tangens as homework. eval_while_stack(route_no [, stack_count]) Default: stack_count: 0 Implements simple looping. Route_no is called while number of stack item is greater than stack_count, resp. lower than abs(stack_count) if stack_count is negative. Loop is also interrupted if route_no returns value <= 0. Example: route[LOOP] { xplog("L_ERR", "%@eval.pop[0]\n"); } eval_push("Three"); eval_push("Two"); eval_push("One"); eval_push("GO!"); eval_while_stack("LOOP"); eval_dump(); Dumps stack and registers to stderr. For debugging purposes. Selects: ------- @eval.pop[stack_pos] Get value of stack item and remove it from the stack @eval.get[stack_pos] Get register value @eval.reg.REGNAME Return register value. Example: if (@eval.reg.ax == @eval.get[0]) { } Note that @eval.get and @eval.reg support select_any_uri and select_any_nameaddr @eval.uuid Generates uuid, obsolete use @sys.unique Operators/functions ------------------- String <-> integer conversion is perfomed automatically. "+" ... additon, 2 (int) params "-" ... subtraction, 2 (int) params "*" ... multiplication, 2 (int) params "/" ... division, 2 (int) params "%" ... modulo, 2 (int) params "neg" ... negation, 1 (int) param "abs" ... absolute value, 1 (int) param "sgn" ... signum, 1 (int) param "dec" ... decrement, 1 (int) param "inc" ... increment, 1 (int) param "&" ... bitand, 2 (int) params "|" ... bitor, 2 (int) params "^" ... bitxor, 2 (int) params "~" ... bitnot, 1 (int) param "&&" ... and, 2 (int) params "||" ... or, 2 (int) params "!" ... not, 1 (int) params "==" ... equal, 2 (any) params "!=" ... non equal, 2 (any) params ">" ... greater, 2 (any) params ">=" ... greater or equal, 2 (any) params "<" ... lower, 2 (any) params "<=" ... lower or equal, 2 (any) params "concat" ... string concatanation, 2 (str) params "substr" ... substring, (str) param, (int) position, (int) length get substring If start is non-negative, the returned string will start at the start'th position in string, counting from zero. For instance, in the string 'abcdef', the character at position 0 is 'a', the character at position 2 is 'c', and so forth. If start is negative, the returned string will start at the start'th character from the end of string. If length is given and is positive, the string returned will contain at most length characters beginning from start (depending on the length of string). If string is less than or equal to start characters long, empty string is returned. If length is given and is negative, then that many characters will be omitted from the end of string (after the start position has been calculated when a start is negative). If start denotes a position beyond this truncation, an empty string will be returned. "strdel" ... delete substring, (str) param, (int) position, (int) length see substr "strlen" ... str length, (str) param "strstr" ... substring position, (str) string, (str) needle Return position of needle in string, -1 if not found "strupper" ... upcase, (str) str "strlower" ... locase, (str) str "(int)" ... cast item as integer "(str)" ... cast item as string Value oparation: working with comma delimited list of values, e.g. Record-route, route, etc. "valat" ... get value at index, (str) values, (int) idx Get values[idx], positive/negative logic similar to stack manipulation "valuris" ... get values in "normalized" form (uri only), (str) values "valrev" ... get values in reversal order, (str) values "subval" ... get subvalues, (str) values, (idx) index, (len) length (... see substr) "strvalat" ... (str) values, (str) needle, get index of string in value (-1 if string does not exist) "valcount" ... get number of items "geturi" ... get uri part, "<", ">" are stripped, (str) addr "valconcat" ... concatenate n stack items to value, n<=0 returns empty string, (int) n, (str) v1 ... (str) vn kamailio-4.0.4/obsolete/eval/eval.c0000644000000000000000000014627012223032460015645 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "../../route.h" #include "../../sr_module.h" #include "../../mem/mem.h" #include "../../str.h" #include "../../error.h" #include "../../config.h" #include "../../trim.h" #include "../../select.h" #include "../../ut.h" #include "../../modules/xprint/xp_lib.h" #include "../../select_buf.h" #include "../../globals.h" #include "../../route.h" #include "../../parser/msg_parser.h" #include "../../action.h" #include "../../script_cb.h" #include "../../dset.h" #include "../../usr_avp.h" MODULE_VERSION #define MODULE_NAME "eval" enum {evtVoid=0, evtInt, evtStr}; struct eval_str { str s; int cnt; }; struct eval_value { union { long n; struct eval_str *s; } u; int type; }; struct register_item { char *name; struct eval_value value; struct register_item *next; }; struct stack_item { struct eval_value value; struct stack_item *prev; struct stack_item *next; }; static int stack_no = 0; static struct stack_item *stack_head = 0; static struct stack_item *stack_tail = 0; static struct register_item* registers = 0; #define destroy_value(val) { \ if ((val).type == evtStr && (val).u.s && (val).u.s->cnt > 0) { \ (val).u.s->cnt--; \ if ((val).u.s->cnt == 0) \ pkg_free((val).u.s); \ } \ (val).type = evtVoid; \ } #define assign_value(dest, src) { \ if (&(dest) != &(src)) { \ destroy_value(dest); \ dest = src; \ if ((dest).type == evtStr && (dest).u.s && (dest).u.s->cnt > 0) \ (dest).u.s->cnt++; \ } \ } static int get_as_int(struct eval_value *value, long* val) { switch (value->type) { case evtInt: *val = value->u.n; return 1; case evtStr: if (value->u.s->s.s && value->u.s->s.len && value->u.s->s.len <= 25) { char *err; char buf[25+1]; memcpy(buf, value->u.s->s.s, value->u.s->s.len); buf[value->u.s->s.len] = '\0'; *val = strtol(buf, &err, 10); if (*err == 0) return 1; } ERR(MODULE_NAME": cannot convert '%.*s' as int\n", value->u.s->s.len, value->u.s->s.s); return -1; default: BUG("Bad value type %d\n", value->type); return -1; } } static void get_as_str(struct eval_value *value, str *s) { static char buf[25]; switch (value->type) { case evtInt: s->len = snprintf(buf, sizeof(buf)-1, "%ld", value->u.n); s->s = buf; break; case evtStr: *s = value->u.s->s; break; default: s->s = 0; s->len = 0; break; } } static int get_as_bool(struct eval_value *value) { switch (value->type) { case evtVoid: return 0; case evtInt: return value->u.n != 0; case evtStr: return (value->u.s->s.s && value->u.s->s.len > 0); default: BUG("Bad value type %d\n", value->type); return -1; } } static struct eval_str* eval_str_malloc(str* s) { struct eval_str* p; p = pkg_malloc(sizeof(*p)+s->len); if (p) { p->s.s = (char*)p+sizeof(*p); if (s->len && s->s != 0) memcpy(p->s.s, s->s, s->len); if (s->s == 0 && s->len) s->s = p->s.s; p->s.len = s->len; p->cnt = 1; } return p; } /* taken from modules/textops */ #define is_space(_p) ((_p) == '\t' || (_p) == '\n' || (_p) == '\r' || (_p) == ' ') static void get_uri_and_skip_until_params(str *param_area, str *uri) { int i, quoted, uri_pos, uri_done; uri->len = 0; uri->s = 0; uri_done = 0; for (i=0; ilen && param_area->s[i]!=';'; ) { /* [ *(token LSW)/quoted-string ] "<" addr-spec ">" | addr-spec */ /* skip name */ for (quoted=0, uri_pos=i; ilen; i++) { if (!quoted) { if (param_area->s[i] == '\"') { quoted = 1; uri_pos = -1; } else if (param_area->s[i] == '<' || param_area->s[i] == ';' || is_space(param_area->s[i])) break; } else if (param_area->s[i] == '\"' && param_area->s[i-1] != '\\') quoted = 0; } if (uri_pos >= 0 && !uri_done) { uri->s = param_area->s+uri_pos; uri->len = param_area->s+i-uri->s; } /* skip uri */ while (ilen && is_space(param_area->s[i])) i++; if (ilen && param_area->s[i]=='<') { uri->s = param_area->s+i; uri->len = 0; for (quoted=0; ilen; i++) { if (!quoted) { if (param_area->s[i] == '\"') quoted = 1; else if (param_area->s[i] == '>') { uri->len = param_area->s+i-uri->s+1; uri_done = 1; break; } } else if (param_area->s[i] == '\"' && param_area->s[i-1] != '\\') quoted = 0; } } } param_area->s+= i; param_area->len-= i; } static int find_next_value(char** start, char* end, str* val, str* lump_val) { int quoted = 0; lump_val->s = *start; while (*start < end && is_space(**start) ) (*start)++; val->s = *start; while ( *start < end && (**start != ',' || quoted) ) { if (**start == '\"' && (!quoted || (*start)[-1]!='\\') ) quoted = ~quoted; (*start)++; } val->len = *start - val->s; while (val->len > 0 && is_space(val->s[val->len-1])) val->len--; /* we cannot automatically strip quotes!!! an example why: "name" ;param="bar" if (val->len >= 2 && val->s[0] == '\"' && val->s[val->len-1] == '\"') { val->s++; val->len -= 2; } */ while (*start < end && **start != ',') (*start)++; if (*start < end) { (*start)++; } lump_val->len = *start - lump_val->s; return (*start < end); } #define MAX_HF_VALUES 30 static int parse_hf_values(str s, int* n, str** vals) { static str values[MAX_HF_VALUES]; char *start, *end; str lump_val; *n = 0; *vals = values; if (!s.s) return 1; start = s.s; end = start+s.len; while (start < end) { find_next_value(&start, end, &values[*n], &lump_val); if (*n >= MAX_HF_VALUES) { ERR(MODULE_NAME": too many values\n"); return -1; } (*n)++; } return 1; } static void destroy_stack() { struct stack_item *p; while (stack_head) { destroy_value(stack_head->value); p = stack_head; stack_head = stack_head->next; pkg_free(p); } stack_tail = stack_head; stack_no = 0; } static void destroy_register_values() { struct register_item *p; for (p=registers; p; p=p->next) { destroy_value(p->value); } } static void remove_stack_item(struct stack_item *s) { if (s->prev) s->prev->next = s->next; else stack_head = s->next; if (s->next) s->next->prev = s->prev; else stack_tail = s->prev; destroy_value(s->value); pkg_free(s); stack_no--; } static void insert_stack_item(struct stack_item *s, struct stack_item *pivot, int behind) { if (stack_head == NULL) { s->prev = s->next = 0; } else if (behind) { if (pivot) { s->next = pivot->next; s->prev = pivot; } else { s->next = 0; s->prev = stack_tail; /* bottom (tail) */ } } else { if (pivot) { s->prev = pivot->prev; s->next = pivot; } else { s->next = stack_head; /* top (head) */ s->prev = 0; } } if (!s->prev) stack_head = s; else s->prev->next = s; if (!s->next) stack_tail = s; else s->next->prev = s; stack_no++; } static int declare_register(modparam_t type, char* param) { struct register_item **p; char *c; for (c=param; *c; c++) { if ( (*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || (*c == '_') ) { ; } else { ERR(MODULE_NAME": illegal register name\n"); return E_CFG; } } for (p = ®isters; *p!= 0; p = &(*p)->next); *p = pkg_malloc(sizeof(**p)); if (!*p) return E_OUT_OF_MEM; memset(*p, 0, sizeof(**p)); (*p)->name = param; return 0; } static int mod_pre_script_cb(struct sip_msg *msg, unsigned int flags, void *param) { destroy_stack(); destroy_register_values(); return 1; } static struct register_item* find_register(char* s, int len) { struct register_item *p; for (p=registers; p; p=p->next) { if (strlen(p->name) == len && strncasecmp(p->name, s, len) == 0) break; } return p; } static struct stack_item* find_stack_item(int n) { struct stack_item *p; if ((n >= 0 && n >= stack_no) || (n<0 && -n > stack_no)) { return NULL; } p = NULL; if (n >= 0) { for (p = stack_head; p && n>0; p=p->next, n--); } else { for (p = stack_tail, n=-n-1; p && n>0; p=p->prev, n--); } return p; } /* module exported functions */ static void print_eval_value(struct eval_value* v) { switch (v->type) { case evtStr: if (v->u.s) fprintf(stderr, "s:'%.*s', cnt:%d\n", v->u.s->s.len, v->u.s->s.s, v->u.s->cnt); else fprintf(stderr, "s:\n"); break; case evtInt: fprintf(stderr, "i:%ld\n", v->u.n); break; default:; fprintf(stderr, "type:%d\n", v->type); break; } } static int eval_dump_func(struct sip_msg *msg, char *param1, char *param2) { struct stack_item *si; struct register_item *ri; int i; fprintf(stderr, "Stack (no=%d):\n", stack_no); for (si=stack_head, i=0; si; si=si->next, i++) { fprintf(stderr, "# %.2d ", i); print_eval_value(&si->value); } for (si=stack_tail, i=-1; si; si=si->prev, i--) { fprintf(stderr, "#%.2d ", i); print_eval_value(&si->value); } fprintf(stderr, "Registers:\n"); for (ri=registers; ri; ri=ri->next) { fprintf(stderr, "%s: ", ri->name); print_eval_value(&ri->value); } return 1; } static int xlbuf_size = 4096; static xl_print_log_f* xl_print = NULL; static xl_parse_format_f* xl_parse = NULL; #define NO_SCRIPT -1 enum {esotAdd, esotInsert, esotXchg, esotPut, esotGet, esotPop, esotAddValue, esotInsertValue}; enum {esovtInt, esovtStr, esovtAvp, esovtXStr, esovtRegister, esovtFunc, esovtSelect}; enum {esofNone=0, esofTime, esofUuid, esofStackNo}; struct eval_location_func { int type; char *name; }; static struct eval_location_func loc_functions[] = { {esofTime, "time"}, {esofUuid, "uuid"}, {esofStackNo, "stackno"}, {esofNone, NULL} }; struct eval_location { int value_type; union { int n; struct eval_str s; xl_elog_t* xl; struct register_item *reg; avp_ident_t avp; select_t* select; struct eval_location_func *func; } u; }; struct eval_stack_oper { int oper_type; struct eval_location loc; }; static int parse_location(str s, struct eval_location *p) { if (s.len >= 2 && s.s[1] == ':') { switch (s.s[0]) { case 'r': p->u.reg = find_register(s.s+2, s.len-2); if (!p->u.reg) { ERR(MODULE_NAME": register '%.*s' not found\n", s.len-2, s.s+2); return E_CFG; } p->value_type = esovtRegister; break; case 'x': if (!xl_print) { xl_print=(xl_print_log_f*)find_export("xprint", NO_SCRIPT, 0); if (!xl_print) { ERR(MODULE_NAME": cannot find \"xprint\", is module xprint loaded?\n"); return E_UNSPEC; } } if (!xl_parse) { xl_parse=(xl_parse_format_f*)find_export("xparse", NO_SCRIPT, 0); if (!xl_parse) { ERR(MODULE_NAME": cannot find \"xparse\", is module xprint loaded?\n"); return E_UNSPEC; } } if(xl_parse(s.s+2, &p->u.xl) < 0) { ERR(MODULE_NAME": wrong xl_lib format '%s'\n", s.s+2); return E_UNSPEC; } p->value_type = esovtXStr; break; case 'f': { struct eval_location_func* f; s.s += 2; s.len -= 2; for (f=loc_functions; f->type != esofNone; f++) { if (strlen(f->name)==s.len && strncasecmp(s.s, f->name, s.len) == 0) { p->value_type = esovtFunc; p->u.func = f; break; } } if (!f) { ERR(MODULE_NAME": unknown function '%.*s'\n", s.len, s.s); return E_CFG; } break; } case 's': s.s += 2; s.len -= 2; /* no break */ default: p->u.s.s = s; p->u.s.cnt = 0; p->value_type = esovtStr; break; } } else { char *err; if (s.len > 1 && s.s[0]=='$') { s.s++; s.len--; if (parse_avp_ident(&s, &p->u.avp) == 0) { if (p->u.avp.flags & AVP_NAME_RE) { ERR(MODULE_NAME": avp regex not allowed\n"); return E_CFG; } p->value_type = esovtAvp; return 1; } s.s--; s.len++; } else if (s.len > 1 && s.s[0]=='@') { if (parse_select(&s.s, &p->u.select) >= 0) { p->value_type = esovtSelect; return 1; } } p->u.n = strtol(s.s, &err, 10); if (*err) { p->u.s.s = s; p->u.s.cnt = 0; p->value_type = esovtStr; } else { p->value_type = esovtInt; } } return 1; } static int eval_xl(struct sip_msg *msg, xl_elog_t* xl, str* s) { static char *xlbuf=NULL; int xllen = 0; if (!xlbuf) { xlbuf = (char*) pkg_malloc((xlbuf_size+1)*sizeof(char)); if (!xlbuf) { ERR(MODULE_NAME": eval_xl: No memory left for format buffer\n"); return E_OUT_OF_MEM; } } xllen = xlbuf_size; if (xl_print(msg, xl, xlbuf, &xllen) < 0) { ERR(MODULE_NAME": eval_xl: Error while formatting result\n"); return E_UNSPEC; } s->s = xlbuf; s->len = xllen; return 1; } SELECT_F(select_sys_unique) static int eval_location(struct sip_msg *msg, struct eval_location* so, struct eval_value* v, int get_static_str) { static struct eval_str ss; v->type = evtVoid; switch (so->value_type) { case esovtInt: v->type = evtInt; v->u.n = so->u.n; break; case esovtStr: v->type = evtStr; v->u.s = &so->u.s; break; case esovtXStr: { str s; int ret; ret = eval_xl(msg, so->u.xl, &s); if (ret < 0) return ret; if (get_static_str) { ss.s = s; ss.cnt = 0; v->u.s = &ss; } else { v->u.s = eval_str_malloc(&s); if (!v->u.s) { ERR(MODULE_NAME": out of memory to allocate xl string\n"); return E_OUT_OF_MEM; } } v->type = evtStr; break; } case esovtRegister: if (get_static_str) *v = so->u.reg->value; /* do not incement cnt */ else assign_value(*v, so->u.reg->value); break; case esovtAvp: { avp_t* avp; avp_value_t val; if (so->u.avp.flags & AVP_INDEX_ALL) avp = search_first_avp(so->u.avp.flags & ~AVP_INDEX_ALL, so->u.avp.name, &val, NULL); else avp = search_avp_by_index(so->u.avp.flags, so->u.avp.name, &val, so->u.avp.index); if (!avp) { ERR(MODULE_NAME": avp '%.*s'[%d] not found\n", so->u.avp.name.s.len, so->u.avp.name.s.s, so->u.avp.index); return -1; } if (avp->flags & AVP_VAL_STR) { if (get_static_str) { ss.s = val.s; ss.cnt = 0; v->u.s = &ss; } else { v->u.s = eval_str_malloc(&val.s); if (!v->u.s) { ERR(MODULE_NAME": out of memory to allocate avp string\n"); return E_OUT_OF_MEM; } } v->type = evtStr; } else { v->type = evtInt; v->u.n = val.n; } break; } case esovtSelect: { str s; int ret = run_select(&s, so->u.select, msg); if (ret < 0 || ret > 0) return -1; if (get_static_str) { ss.s = s; ss.cnt = 0; v->u.s = &ss; } else { v->u.s = eval_str_malloc(&s); if (!v->u.s) { ERR(MODULE_NAME": out of memory to allocate select string\n"); return E_OUT_OF_MEM; } } v->type = evtStr; break; } case esovtFunc: { switch (so->u.func->type) { case esofTime: { time_t stamp; stamp = time(NULL); v->type = evtInt; v->u.n = stamp; break; } case esofUuid: { str s; select_sys_unique(&s, 0, msg); if (get_static_str) { ss.s = s; ss.cnt = 0; v->u.s = &ss; } else { v->u.s = eval_str_malloc(&s); if (!v->u.s) { ERR(MODULE_NAME": out of memory to allocate uuid string\n"); return E_OUT_OF_MEM; } } v->type = evtStr; break; } case esofStackNo: v->type = evtInt; v->u.n = stack_no; break; default: BUG("bad func type (%d)\n", so->u.func->type); return -1; } break; } default: BUG("Bad value type (%d)\n", so->value_type); return -1; } return 1; } static int fixup_location_12( void** param, int param_no) { struct eval_location *so; str s; s.s = *param; s.len = strlen(s.s); so = pkg_malloc(sizeof(*so)); if (!so) return E_OUT_OF_MEM; if (parse_location(s, so) < 0) { ERR(MODULE_NAME": parse location error '%s'\n", s.s); return E_CFG; } *param = so; return 0; } static int fixup_stack_oper(void **param, int param_no, int oper_type) { str s; struct eval_stack_oper *p; int ret; if (param_no == 2) { return fixup_location_12(param, param_no); } p = pkg_malloc(sizeof(*p)); if (!p) return E_OUT_OF_MEM; p->oper_type = oper_type; s.s = *param; s.len = strlen(s.s); *param = p; ret = parse_location(s, &p->loc); if (ret < 0) return ret; switch (p->oper_type) { case esotXchg: if (p->loc.value_type == esovtAvp || p->loc.value_type == esovtSelect) { ERR(MODULE_NAME": avp non supported for xchg\n"); return E_CFG; } /* no break */ case esotPop: case esotGet: if (p->loc.value_type != esovtRegister && p->loc.value_type != esovtAvp) { ERR(MODULE_NAME": non supported read only location\n"); return E_CFG; } break; default:; } return 0; } static int eval_stack_oper_func(struct sip_msg *msg, char *param1, char *param2) { int ret, idx; struct stack_item *pivot; struct eval_stack_oper *so; struct run_act_ctx ra_ctx; so = (struct eval_stack_oper *)param1; if (param2) { long l; struct eval_value v; eval_location(msg, (struct eval_location*) param2, &v, 1); ret = get_as_int(&v, &l); if (ret < 0) return ret; idx = l; } else { switch (so->oper_type) { /* default values */ case esotAdd: case esotAddValue: idx = -1; break; default: idx = 0; break; } } pivot = find_stack_item(idx); if ( !(pivot!=NULL || ((so->oper_type == esotAdd || so->oper_type == esotAddValue) && idx == -1) || ((so->oper_type == esotInsert || so->oper_type == esotInsertValue) && idx == 0)) ) return -1; switch (so->oper_type) { case esotGet: case esotPop: switch (so->loc.value_type) { case esovtRegister: assign_value(so->loc.u.reg->value, pivot->value); if (so->oper_type == esotPop) remove_stack_item(pivot); return 1; case esovtAvp: { struct action a; avp_spec_t attr; a.type = ASSIGN_T; a.count = 2; a.val[0].type = AVP_ST; attr.type = so->loc.u.avp.flags; attr.name = so->loc.u.avp.name; attr.index = so->loc.u.avp.index; a.val[0].u.attr = &attr; switch (pivot->value.type) { case evtInt: a.val[1].type = NUMBER_ST; a.val[1].u.number = pivot->value.u.n; break; case evtStr: if (pivot->value.u.s) a.val[1].u.str = pivot->value.u.s->s; else a.val[1].u.str.len = 0; a.val[1].type = STRING_ST; break; default: return -1; } a.next = 0; init_run_actions_ctx(&ra_ctx); ret = do_action(&ra_ctx, &a, msg); if (so->oper_type == esotPop) remove_stack_item(pivot); return ret<0?-1:1; } default: BUG("Bad value type (%d) for get/pop\n", so->loc.value_type); return -1; } break; case esotXchg: switch (so->loc.value_type) { case esovtRegister: { struct eval_value v; v = so->loc.u.reg->value; so->loc.u.reg->value = pivot->value; pivot->value = v; return 1; } default: BUG("Bad value type (%d) for xchg\n", so->loc.value_type); return -1; } break; case esotInsert: case esotAdd: case esotPut: { struct eval_value v; eval_location(msg, &so->loc, &v, 0); if (so->oper_type == esotInsert || so->oper_type == esotAdd) { struct stack_item *si; si = pkg_malloc(sizeof(*si)); if (!si) { ERR(MODULE_NAME": out of memory\n"); destroy_value(v); return -1; } si->value = v; insert_stack_item(si, pivot, so->oper_type == esotAdd); return 1; } else { destroy_value(pivot->value); pivot->value = v; return 1; } break; } case esotInsertValue: case esotAddValue: { struct eval_value v; str s, *vals; int i, n; struct eval_str* es; struct stack_item *si; eval_location(msg, &so->loc, &v, 0); get_as_str(&v, &s); if ((parse_hf_values(s, &n, &vals) < 0) || n == 0) { destroy_value(v); return -1; } si = pkg_malloc(sizeof(*si)); if (!si) { ERR(MODULE_NAME": out of memory\n"); destroy_value(v); return -1; } si->value.type = evtInt; si->value.u.n = n; insert_stack_item(si, pivot, so->oper_type == esotAddValue); pivot = si; for (i=0; ivalue.type = evtStr; si->value.u.s = es; insert_stack_item(si, pivot, 1); pivot = si; } destroy_value(v); return 1; } default: BUG("Unexpected operation (%d)\n", so->oper_type); return -1; } } static int eval_add_fixup( void** param, int param_no) { return fixup_stack_oper(param, param_no, esotAdd); } static int eval_insert_fixup( void** param, int param_no) { return fixup_stack_oper(param, param_no, esotInsert); } static int eval_put_fixup( void** param, int param_no) { return fixup_stack_oper(param, param_no, esotPut); } static int eval_get_fixup( void** param, int param_no) { return fixup_stack_oper(param, param_no, esotGet); } static int eval_pop_fixup( void** param, int param_no) { return fixup_stack_oper(param, param_no, esotPop); } static int eval_xchg_fixup( void** param, int param_no) { return fixup_stack_oper(param, param_no, esotXchg); } static int eval_add_value_fixup( void** param, int param_no) { return fixup_stack_oper(param, param_no, esotAddValue); } static int eval_insert_value_fixup( void** param, int param_no) { return fixup_stack_oper(param, param_no, esotInsertValue); } static int eval_remove_func(struct sip_msg *msg, char *param1, char *param2) { struct stack_item *p, *p2; int ret, len, start; struct eval_value v; if (param1) { long l; eval_location(msg, (struct eval_location*) param1, &v, 1); ret = get_as_int(&v, &l); if (ret < 0) return ret; start = l; } else start = 0; p = find_stack_item(start); if (p) { if (param2) { long l; eval_location(msg, (struct eval_location*) param2, &v, 1); ret = get_as_int(&v, &l); if (ret < 0) return ret; len = l; } else len = 1; if (start < 0) { start = stack_no + start; if (start < 0) start = 0; } else { if (start > stack_no) start = stack_no; } if (len < 0) { len = stack_no - start + len; if (len < 0) len = 0; } else { if (start + len > stack_no) len = stack_no - start; } for (; len > 0 && p; len--) { p2 = p; p = p->next; remove_stack_item(p2); } return 1; } else return -1; } static int eval_clear_func(struct sip_msg *msg, char *param1, char *param2) { int n; if (get_int_fparam(&n, msg, (fparam_t*)param1)<0) { ERR(MODULE_NAME": eval_clear: Invalid number specified\n"); return -1; } if (n & 1) destroy_stack(); if (n & 2) destroy_register_values(); return 1; } enum {esftNone=0, esftAdd, esftSub, esftMultiplication, esftDivision, esftModulo, esftNeg, esftAbs, esftSgn, esftDec, esftInc, esftConcat, esftSubstr, esftStrLen, esftStrStr, esftStrDel, esftStrUpper, esftStrLower, esftCastAsInt, esftCastAsStr, esftValueAt, esftValueUris, esftValueRev, esftSubValue, esftValueCount, esftValueConcat, esftStrValueAt, esftGetUri, esftAnd, esftOr, esftNot, esftBitAnd, esftBitOr, esftBitNot, esftBitXor, esftEQ, esftNE, esftGT, esftGE, esftLW, esftLE}; struct eval_function_def { int type; char *name; int arg_no; }; static struct eval_function_def eval_functions[] = { {esftAdd, "+", 2}, {esftSub, "-", 2}, {esftMultiplication, "*", 2}, {esftDivision, "/", 2}, {esftModulo, "%", 2}, {esftNeg, "neg", 1}, {esftAbs, "abs", 1}, {esftDec, "dec", 1}, {esftInc, "inc", 1}, {esftSgn, "sgn", 1}, {esftConcat, "concat", 2}, {esftSubstr, "substr", 3}, {esftStrLen, "strlen", 1}, {esftStrStr, "strstr", 2}, {esftStrDel, "strdel", 3}, {esftStrUpper, "strupper", 1}, {esftStrLower, "strlower", 1}, {esftCastAsInt, "(int)", 1}, {esftCastAsStr, "(str)", 1}, {esftValueAt, "valat", 2}, {esftValueUris, "valuris", 1}, {esftValueRev, "valrev", 1}, {esftSubValue, "subval", 3}, {esftValueCount, "valcount", 1}, {esftValueConcat, "valconcat", 1}, {esftStrValueAt, "strvalat", 2}, {esftGetUri, "geturi", 1}, {esftAnd, "&&", 2}, {esftOr, "||", 2}, {esftNot, "!", 1}, {esftBitAnd, "&", 2}, {esftBitOr, "|", 2}, {esftBitNot, "~", 1}, {esftBitXor, "^", 2}, {esftEQ, "==", 2}, {esftNE, "!=", 2}, {esftGT, ">", 2}, {esftGE, ">=", 2}, {esftLW, "<", 2}, {esftLE, "<=", 2}, {esftNone, NULL} }; struct eval_function { int resolved; /* is oper.d valid ? */ union { struct eval_function_def *d; struct eval_location loc; } oper; struct eval_function* next; }; static int eval_stack_func_fixup( void** param, int param_no) { char *c, *c2; struct eval_function_def* d; struct eval_function **p, *head; if (param_no == 2) { return fixup_location_12(param, param_no); } head = 0; p = &head; c = *param; while (*c) { str s; struct eval_location so; while( (*c<=' ' || *c == ',') && *c ) c++; if (*c == '\0') break; c2 = c; while (*c && *c!=',') c++; while (c > c2 && *(c-1) <= ' ') c--; s.s = c2; s.len = c-c2; if (parse_location(s, &so) < 0) { ERR(MODULE_NAME": parse operation error near '%s'\n", c2); return E_CFG; } *p = pkg_malloc(sizeof(**p)); if (!*p) return E_OUT_OF_MEM; (*p)->next = 0; switch (so.value_type) { case esovtStr: for (d=eval_functions; d->type; d++) { if (strlen(d->name) == so.u.s.s.len && strncasecmp(d->name, so.u.s.s.s, so.u.s.s.len)==0) { (*p)->oper.d = d; break; } } if (!d->type) { ERR(MODULE_NAME": unknown eval function near '%s'\n", so.u.s.s.s); return E_CFG; } (*p)->resolved = 1; break; case esovtAvp: case esovtXStr: case esovtRegister: case esovtSelect: case esovtFunc: (*p)->oper.loc = so; (*p)->resolved = 0; break; default: ERR(MODULE_NAME": location %d not allowed\n", so.value_type); return E_CFG; } p = &(*p)->next; } *param = head; return 0; } #ifndef _GNU_SOURCE void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); #endif static int eval_stack_func_func(struct sip_msg *msg, char *param1, char *param2) { struct eval_function *f; struct stack_item *pivot; struct eval_function_def *d; int stack_idx = 0; int ret = -1; if (param2) { long l; int ret; struct eval_value v; eval_location(msg, (struct eval_location*) param2, &v, 1); ret = get_as_int(&v, &l); if (ret < 0) return ret; stack_idx = l; } for (f = (struct eval_function*) param1; f; f=f->next, ret = 1) { if (f->resolved) { d = f->oper.d; } else { str fn; struct eval_value v; eval_location(msg, &f->oper.loc, &v, 1); get_as_str(&v, &fn); for (d=eval_functions; d->type; d++) { if (strlen(d->name) == fn.len && strncasecmp(d->name, fn.s, fn.len)==0) { break; } } if (!d->type) { ERR(MODULE_NAME": unknown eval function '%.*s'\n", fn.len, fn.s); return -1; } } DEBUG(MODULE_NAME": eval_oper: %s, stack_idx: %d, stack_no: %d\n", d->name, stack_idx, stack_no); if ( ((stack_idx >= 0) && (stack_idx+d->arg_no > stack_no)) || ((stack_idx < 0) && (stack_no+stack_idx < 0 || stack_no+stack_idx+d->arg_no > stack_no)) ) { ERR(MODULE_NAME": operation out of stack range\n"); return -1; } pivot = find_stack_item(stack_idx); if (!pivot) { BUG("stack test error\n"); return -1; } switch (d->type) { case esftAdd: case esftSub: case esftMultiplication: case esftDivision: case esftModulo: case esftAnd: case esftOr: case esftBitAnd: case esftBitOr: case esftBitXor: { long a, b; if (get_as_int(&pivot->value, &a) < 0) return -1; if (get_as_int(&pivot->next->value, &b) < 0) return -1; switch (d->type) { case esftAdd: a = a + b; break; case esftSub: a = a - b; break; case esftMultiplication: a = a * b; break; case esftDivision: if (b == 0) { ERR(MODULE_NAME": division by zero\n"); return -1; } a = a / b; break; case esftModulo: if (b == 0) { ERR(MODULE_NAME": division by zero\n"); return -1; } a = a % b; break; case esftAnd: a = a && b; break; case esftOr: a = a || b; break; case esftBitAnd: a = a & b; break; case esftBitOr: a = a | b; break; case esftBitXor: a = a ^ b; break; } destroy_value(pivot->value); pivot->value.type = evtInt; pivot->value.u.n = a; remove_stack_item(pivot->next); break; } case esftNeg: case esftAbs: case esftSgn: case esftDec: case esftInc: case esftNot: case esftBitNot: case esftCastAsInt: { long a; if (get_as_int(&pivot->value, &a) < 0) return -1; switch (d->type) { case esftNeg: a = -a; break; case esftAbs: a = abs(a); break; case esftSgn: if (a < 0) a = -1; else if (a > 0) a = 1; else a = 0; break; case esftDec: a--; break; case esftInc: a++; break; case esftNot: a = !a; break; case esftBitNot: a = ~a; break; case esftCastAsInt: break; } destroy_value(pivot->value); pivot->value.type = evtInt; pivot->value.u.n = a; break; } case esftCastAsStr: if (pivot->value.type != evtStr) { str s; get_as_str(&pivot->value, &s); destroy_value(pivot->value); pivot->value.u.s = eval_str_malloc(&s); if (!pivot->value.u.s) { ERR(MODULE_NAME": out of memory\n"); return -1; } pivot->value.type = evtStr; } break; case esftEQ: case esftNE: case esftGT: case esftGE: case esftLW: case esftLE: { long a; if (pivot->value.type == evtStr || pivot->next->value.type == evtStr) { str s1, s2; int l; get_as_str(&pivot->value, &s1); get_as_str(&pivot->next->value, &s2); l = (s1.len < s2.len)?s1.len:s2.len; if (l > 0) a = strncasecmp(s1.s, s2.s, l); else a = 0; switch (d->type) { case esftEQ: a = a == 0 && s1.len == s2.len; break; case esftNE: a = a != 0 || s1.len != s2.len; break; case esftGT: a = a > 0 || (a == 0 && s1.len > s2.len); break; case esftGE: a = a > 0 || (a == 0 && s1.len >= s2.len); break; case esftLW: a = a < 0 || (a == 0 && s1.len < s2.len); break; case esftLE: a = a < 0 || (a == 0 && s1.len <= s2.len); break; } } else { long b; if (get_as_int(&pivot->value, &a) < 0) return -1; if (get_as_int(&pivot->next->value, &b) < 0) return -1; switch (d->type) { case esftEQ: a = a == b; break; case esftNE: a = a != b; break; case esftGT: a = a > b; break; case esftGE: a = a >= b; break; case esftLW: a = a < b; break; case esftLE: a = a <= b; break; } } destroy_value(pivot->value); pivot->value.type = evtInt; pivot->value.u.n = a; remove_stack_item(pivot->next); break; } case esftConcat: { char buf[25]; str s, s1, s2; struct eval_str* es; get_as_str(&pivot->value, &s1); if (pivot->value.type == evtInt && pivot->next->value.type == evtInt) { memcpy(buf, s1.s, s1.len); /* result in static buffer */ s1.s = buf; } get_as_str(&pivot->next->value, &s2); s.len = s1.len + s2.len; s.s = 0; es = eval_str_malloc(&s); if (!es) { ERR(MODULE_NAME": out of memory\n"); return -1; } memcpy(s.s, s1.s, s1.len); memcpy(s.s+s1.len, s2.s, s2.len); destroy_value(pivot->value); pivot->value.type = evtStr; pivot->value.u.s = es; remove_stack_item(pivot->next); break; } case esftSubstr: { long start, len; str s1; struct eval_str* es; get_as_str(&pivot->value, &s1); if (get_as_int(&pivot->next->value, &start) < 0) return -1; if (get_as_int(&pivot->next->next->value, &len) < 0) return -1; if (start < 0) { start = s1.len + start; if (start < 0) start = 0; } else { if (start > s1.len) start = s1.len; } if (len < 0) { len = s1.len - start + len; if (len < 0) len = 0; } else { if (start + len > s1.len) len = s1.len - start; } s1.s += start; s1.len = len; es = eval_str_malloc(&s1); if (!es) { ERR(MODULE_NAME": out of memory\n"); return -1; } destroy_value(pivot->value); pivot->value.type = evtStr; pivot->value.u.s = es; remove_stack_item(pivot->next); remove_stack_item(pivot->next); break; } case esftStrLen: { long len; str s1; get_as_str(&pivot->value, &s1); len = s1.len; destroy_value(pivot->value); pivot->value.type = evtInt; pivot->value.u.n = len; break; } case esftStrStr: { char buf[25], *p; str s1, s2; get_as_str(&pivot->value, &s1); if (pivot->value.type == evtInt && pivot->next->value.type == evtInt) { memcpy(buf, s1.s, s1.len); /* result in static buffer */ s1.s = buf; } get_as_str(&pivot->next->value, &s2); p = (char *) memmem(s1.s, s1.len, s2.s, s2.len); destroy_value(pivot->value); pivot->value.type = evtInt; pivot->value.u.n = p?p-s1.s:-1; remove_stack_item(pivot->next); break; } case esftStrDel: { long start, len; str s1, s; struct eval_str* es; get_as_str(&pivot->value, &s1); if (get_as_int(&pivot->next->value, &start) < 0) return -1; if (get_as_int(&pivot->next->next->value, &len) < 0) return -1; if (start < 0) { start = s1.len + start; if (start < 0) start = 0; } else { if (start > s1.len) start = s1.len; } if (len < 0) { len = s1.len - start + len; if (len < 0) len = 0; } else { if (start + len > s1.len) len = s1.len - start; } s.s = 0; s.len = s1.len - len; es = eval_str_malloc(&s); if (!es) { ERR(MODULE_NAME": out of memory\n"); return -1; } if (start > 0) memcpy(s.s, s1.s, start); memcpy(s.s+start, s1.s+start+len, s1.len-(start+len)); destroy_value(pivot->value); pivot->value.type = evtStr; pivot->value.u.s = es; remove_stack_item(pivot->next); remove_stack_item(pivot->next); break; } case esftStrUpper: case esftStrLower: { str s1; int i; struct eval_str* es; get_as_str(&pivot->value, &s1); es = eval_str_malloc(&s1); if (!es) { ERR(MODULE_NAME": out of memory\n"); return -1; } for (i=0; is.len; i++) es->s.s[i] = (d->type == esftStrUpper) ? toupper(es->s.s[i]) : tolower(es->s.s[i]); destroy_value(pivot->value); pivot->value.type = evtStr; pivot->value.u.s = es; break; } case esftValueAt: { str s1, *vals; long idx; int n; struct eval_str* es; get_as_str(&pivot->value, &s1); if (get_as_int(&pivot->next->value, &idx) < 0) return -1; if (parse_hf_values(s1, &n, &vals) < 0) return -1; if (idx < 0|| idx >= n) { ERR(MODULE_NAME": index (%ld) of of range (%d)\n", idx, n); return -1; } es = eval_str_malloc(vals+idx); if (!es) { ERR(MODULE_NAME": out of memory\n"); return -1; } destroy_value(pivot->value); pivot->value.type = evtStr; pivot->value.u.s = es; remove_stack_item(pivot->next); break; } case esftStrValueAt: { char buf[25]; str s1, s2, *vals; int i, n; get_as_str(&pivot->value, &s1); if (pivot->value.type == evtInt && pivot->next->value.type == evtInt) { memcpy(buf, s1.s, s1.len); /* result in static buffer */ s1.s = buf; } get_as_str(&pivot->next->value, &s2); if (parse_hf_values(s1, &n, &vals) < 0) return -1; for (i=0; ivalue); pivot->value.type = evtInt; pivot->value.u.n = (i>=n)?-1:i; remove_stack_item(pivot->next); break; } case esftValueCount: { str s1, *vals; int n; get_as_str(&pivot->value, &s1); if (parse_hf_values(s1, &n, &vals) < 0) return -1; destroy_value(pivot->value); pivot->value.type = evtInt; pivot->value.u.n = n; break; } case esftSubValue: { long start, len; int i, n, pos; str s1, s, *vals; struct eval_str* es; get_as_str(&pivot->value, &s1); if (get_as_int(&pivot->next->value, &start) < 0) return -1; if (get_as_int(&pivot->next->next->value, &len) < 0) return -1; if (parse_hf_values(s1, &n, &vals) < 0) return -1; if (start < 0) { start = n + start; if (start < 0) start = 0; } else { if (start > n) start = n; } if (len < 0) { len = n - start + len; if (len < 0) len = 0; } else { if (start + len > n) len = n - start; } s.len = 0; for (i=0; i 0) s.s[pos++] = ','; memcpy(s.s+pos, vals[start+i].s, vals[start+i].len); pos += vals[start+i].len; } destroy_value(pivot->value); pivot->value.type = evtStr; pivot->value.u.s = es; remove_stack_item(pivot->next); remove_stack_item(pivot->next); break; } case esftValueConcat: { long n; int i, pos; str s1, s; struct eval_str* es; struct stack_item *si; if (get_as_int(&pivot->value, &n) < 0) return -1; for (si=pivot->next, s.len=0, i=0; inext) { get_as_str(&si->value, &s1); s.len += s1.len+1; } if (s.len) s.len--; s.s = 0; es = eval_str_malloc(&s); if (!es) { ERR(MODULE_NAME": out of memory\n"); return -1; } for (si=pivot->next, i=0, pos=0; inext) { if (pos > 0) s.s[pos++] = ','; get_as_str(&si->value, &s1); memcpy(s.s+pos, s1.s, s1.len); pos += s1.len; } destroy_value(pivot->value); pivot->value.type = evtStr; pivot->value.u.s = es; for (si=pivot->next, i=0; inext; remove_stack_item(si2); } break; } case esftValueRev: { int i, n, pos; str s1, s, *vals; struct eval_str* es; get_as_str(&pivot->value, &s1); if (parse_hf_values(s1, &n, &vals) < 0) return -1; s.len = 0; for (i=0; i=0; i--) { if (pos > 0) s.s[pos++] = ','; memcpy(s.s+pos, vals[i].s, vals[i].len); pos += vals[i].len; } destroy_value(pivot->value); pivot->value.type = evtStr; pivot->value.u.s = es; break; } case esftValueUris: { int i, n, pos; str s1, s, *vals; struct eval_str* es; get_as_str(&pivot->value, &s1); if (parse_hf_values(s1, &n, &vals) < 0) return -1; s.len = 0; for (i=0; i 0) s.s[pos++] = ','; hval1 = *(vals+i); get_uri_and_skip_until_params(&hval1, &huri); if (huri.len) { /* TODO: normalize uri, lowercase except quoted params */ memcpy(s.s+pos, huri.s, huri.len); pos += huri.len; } } es->s.len = pos; destroy_value(pivot->value); pivot->value.type = evtStr; pivot->value.u.s = es; break; } case esftGetUri: { str s1, huri; struct eval_str* es; get_as_str(&pivot->value, &s1); get_uri_and_skip_until_params(&s1, &huri); if (huri.len && *(huri.s) == '<') { huri.s++; /* strip < & > */ huri.len-=2; } es = eval_str_malloc(&huri); if (!es) { ERR(MODULE_NAME": out of memory\n"); return -1; } destroy_value(pivot->value); pivot->value.type = evtStr; pivot->value.u.s = es; break; } default: BUG("Bad operation %d\n", d->type); return -1; } } return ret; } static int eval_while_fixup(void **param, int param_no) { if (param_no == 2) { return fixup_location_12(param, param_no); } else if (param_no == 1) { int n; n = route_get(&main_rt, (char*) *param); if (n == -1) { ERR(MODULE_NAME": eval_while: bad route\n"); return E_CFG; } pkg_free(*param); *param=(void*) (intptr_t) n; } return 0; } static int eval_while_func(struct sip_msg *msg, char *route_no, char *param2) { int ret, idx; struct stack_item *pivot; struct run_act_ctx ra_ctx; if (param2) { long l; struct eval_value v; eval_location(msg, (struct eval_location*) param2, &v, 1); ret = get_as_int(&v, &l); if (ret < 0) return ret; idx = l; } else { idx = 0; /* default values */ } ret = -1; while (1) { pivot = find_stack_item(idx); if (!pivot) break; if (get_as_bool(&pivot->value) <= 0) break; if ((intptr_t)route_no >= main_rt.idx) { BUG("invalid routing table number #%d of %d\n", (int)(intptr_t) route_no, main_rt.idx); return -1; } if (!main_rt.rlist[(intptr_t) route_no]) { WARN(MODULE_NAME": route not declared (hash:%d)\n", (int)(intptr_t) route_no); return -1; } /* exec the routing script */ init_run_actions_ctx(&ra_ctx); ret = run_actions(&ra_ctx, main_rt.rlist[(intptr_t) route_no], msg); if (ret <= 0) break; } return ret; } static int eval_while_stack_func(struct sip_msg *msg, char *route_no, char *param2) { int ret, count; struct run_act_ctx ra_ctx; if (param2) { long l; struct eval_value v; eval_location(msg, (struct eval_location*) param2, &v, 1); ret = get_as_int(&v, &l); if (ret < 0) return ret; count = l; } else { count = 0; /* default values */ } ret = -1; while ((count >= 0 && stack_no > count) || (count < 0 && stack_no < -count)) { if ((intptr_t)route_no >= main_rt.idx) { BUG("invalid routing table number #%d of %d\n", (int)(intptr_t) route_no, main_rt.idx); return -1; } if (!main_rt.rlist[(intptr_t) route_no]) { WARN(MODULE_NAME": route not declared (hash:%d)\n", (int)(intptr_t) route_no); return -1; } /* exec the routing script */ init_run_actions_ctx(&ra_ctx); ret = run_actions(&ra_ctx, main_rt.rlist[(intptr_t) route_no], msg); if (ret <= 0) break; } return ret; } /* select functions */ static int sel_value2str(str* res, struct eval_value *v, int force_copy) { res->len = 0; switch (v->type) { case evtInt: { char buf[30]; res->len = snprintf(buf, sizeof(buf)-1, "%ld", v->u.n); res->s = get_static_buffer(res->len); if (res->s) memcpy(res->s, buf, res->len); else res->len = 0; break; } case evtStr: if (v->u.s) { *res = v->u.s->s; if (force_copy && res->len) { res->s = get_static_buffer(res->len); if (res->s) memcpy(res->s, v->u.s->s.s, res->len); else res->len = 0; } } break; } return 0; } static int sel_eval(str* res, select_t* s, struct sip_msg* msg) { /* dummy */ return 0; } static int sel_register(str* res, select_t* s, struct sip_msg* msg) { if (msg == 0) { struct register_item *p = find_register(s->params[2].v.s.s, s->params[2].v.s.len); if (p == 0) { ERR(MODULE_NAME": select: register '%.*s' not found\n", s->params[2].v.s.len, s->params[2].v.s.s); return E_CFG; } s->params[2].v.p = p; s->params[2].type = SEL_PARAM_PTR; } else { return sel_value2str(res, &((struct register_item *)s->params[2].v.p)->value, 0); } return 0; } static int sel_get_and_remove(str* res, select_t* s, struct sip_msg* msg) { struct stack_item* p; res->len = 0; p = find_stack_item(s->params[2].v.i); if (p) { sel_value2str(res, &p->value, 1); remove_stack_item(p); } return 0; } static int sel_get(str* res, select_t* s, struct sip_msg* msg) { struct stack_item* p; res->len = 0; p = find_stack_item(s->params[2].v.i); if (p) { sel_value2str(res, &p->value, 0); } return 0; } SELECT_F(select_any_nameaddr) SELECT_F(select_any_uri) SELECT_F(select_anyheader_params) select_row_t sel_declaration[] = { { NULL, SEL_PARAM_STR, STR_STATIC_INIT(MODULE_NAME), sel_eval, SEL_PARAM_EXPECTED}, { sel_eval, SEL_PARAM_STR, STR_STATIC_INIT("pop"), sel_get_and_remove, CONSUME_NEXT_INT }, { sel_eval, SEL_PARAM_STR, STR_STATIC_INIT("get"), sel_get, CONSUME_NEXT_INT }, { sel_eval, SEL_PARAM_STR, STR_STATIC_INIT("reg"), sel_register, CONSUME_NEXT_STR|FIXUP_CALL }, { sel_get, SEL_PARAM_STR, STR_STATIC_INIT("nameaddr"), select_any_nameaddr, NESTED | CONSUME_NEXT_STR}, { sel_get, SEL_PARAM_STR, STR_STATIC_INIT("uri"), select_any_uri, NESTED | CONSUME_NEXT_STR}, { sel_get, SEL_PARAM_STR, STR_STATIC_INIT("params"), select_anyheader_params, NESTED}, { sel_register, SEL_PARAM_STR, STR_STATIC_INIT("nameaddr"), select_any_nameaddr, NESTED | CONSUME_NEXT_STR}, { sel_register, SEL_PARAM_STR, STR_STATIC_INIT("uri"), select_any_uri, NESTED | CONSUME_NEXT_STR}, { sel_register, SEL_PARAM_STR, STR_STATIC_INIT("params"), select_anyheader_params, NESTED}, /* for backward compatability only, use @sys.unique */ { sel_eval, SEL_PARAM_STR, STR_STATIC_INIT("uuid"), select_sys_unique, 0}, { NULL, SEL_PARAM_INT, STR_NULL, NULL, 0} }; static int mod_init() { register_script_cb(mod_pre_script_cb, REQUEST_CB | ONREPLY_CB | PRE_SCRIPT_CB, 0); register_select_table(sel_declaration); return 0; } static int child_init(int rank) { return 0; } static void destroy_mod(void) { struct register_item *p; destroy_stack(); destroy_register_values(); while (registers) { p = registers; registers = registers->next; pkg_free(p); } } /* * Exported functions */ static cmd_export_t cmds[] = { {MODULE_NAME"_add", eval_stack_oper_func, 2, eval_add_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_add", eval_stack_oper_func, 1, eval_add_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_push", eval_stack_oper_func, 2, eval_add_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_push", eval_stack_oper_func, 1, eval_add_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_insert", eval_stack_oper_func, 2, eval_insert_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_insert", eval_stack_oper_func, 1, eval_insert_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_xchg", eval_stack_oper_func, 2, eval_xchg_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_xchg", eval_stack_oper_func, 1, eval_xchg_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_get", eval_stack_oper_func, 2, eval_get_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_get", eval_stack_oper_func, 1, eval_get_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_put", eval_stack_oper_func, 2, eval_put_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_put", eval_stack_oper_func, 1, eval_put_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_pop", eval_stack_oper_func, 2, eval_pop_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_pop", eval_stack_oper_func, 1, eval_pop_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_add_value", eval_stack_oper_func, 2, eval_add_value_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_add_value", eval_stack_oper_func, 1, eval_add_value_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_insert_value", eval_stack_oper_func, 2, eval_insert_value_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_insert_value", eval_stack_oper_func, 1, eval_insert_value_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_remove", eval_remove_func, 0, fixup_location_12, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_remove", eval_remove_func, 1, fixup_location_12, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_remove", eval_remove_func, 2, fixup_location_12, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_clear", eval_clear_func, 1, fixup_int_12, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_oper", eval_stack_func_func, 2, eval_stack_func_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_oper", eval_stack_func_func, 1, eval_stack_func_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_while", eval_while_func, 1, eval_while_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_while", eval_while_func, 2, eval_while_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_while_stack", eval_while_stack_func, 1, eval_while_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_while_stack", eval_while_stack_func, 2, eval_while_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {MODULE_NAME"_dump", eval_dump_func, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE}, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"declare_register", PARAM_STRING|PARAM_USE_FUNC, (void*) declare_register}, {"xlbuf_size", PARAM_INT, &xlbuf_size}, {0, 0, 0} }; struct module_exports exports = { MODULE_NAME, cmds, /* Exported commands */ 0, /* RPC */ params, /* Exported parameters */ mod_init, /* module initialization function */ 0, /* response function*/ destroy_mod, /* destroy function */ 0, /* oncancel function */ child_init /* per-child init function */ }; kamailio-4.0.4/obsolete/eval/Makefile0000644000000000000000000000034212223032460016177 0ustar rootroot# $Id$ # # eval module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=eval.so DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/acc_radius/0000755000000000000000000000000012223032460015706 5ustar rootrootkamailio-4.0.4/obsolete/acc_radius/acc_radius.c0000644000000000000000000007141512223032460020157 0ustar rootroot/* * Accounting module * * $Id$ * * Copyright (C) 2001-2003 FhG FOKUS * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include "../../rad_dict.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../modules/tm/t_hooks.h" #include "../../modules/tm/tm_load.h" #include "../../modules/tm/h_table.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_from.h" #include "../../parser/digest/digest.h" #include "../../usr_avp.h" #include "../../id.h" #include "../../modules/tm/tm_load.h" #include "../../parser/parse_rr.h" #include "../../trim.h" #include "../acc_syslog/attrs.h" /* * FIXME: * - Quote attribute values properly * - Explicitly called accounting function will generate Acct-Status-Type * set to failed (rad_status does not work properly in this case), it * should generate Interim-Update * - INVITEs with to tag should generate Interim-Update * - Configurable option which allows to switch From/To based on the * value of ftag route parameter */ /* * a: attr * c: sip_callid * d: to_tag * f: sip_from * g: flags * i: inbound_ruri * m: sip_method * n: sip_cseq * o: outbound_ruri * p: source_ip * r: from_tag * s: server_id * t: sip_to * u: digest_username * x: request_timestamp * D: to_did * F: from_uri * I: from_uid * M: from_did * R: digest_realm * P: source_port * S: sip_status * T: to_uri * U: to_uid * X: response_timestamp */ #define ALL_LOG_FMT "acdfgimnoprstuxDFIMPRSTUX" #define ALL_LOG_FMT_LEN (sizeof(ALL_LOG_FMT) - 1) MODULE_VERSION struct tm_binds tmb; static int mod_init( void ); static int fix_log_flag( modparam_t type, void* val); static int fix_log_missed_flag( modparam_t type, void* val); static int early_media = 0; /* Enable/disable early media (183) accounting */ static int failed_transactions = 0; /* Enable/disable accounting of failed (>= 300) transactions */ static int report_cancels = 0; /* Enable/disable CANCEL reporting */ static int report_ack = 0; /* Enable/disable end-to-end ACK reports */ static int log_flag = 0; /* Flag that marks transactions to be accounted */ static int log_missed_flag = 0; /* Transaction having this flag set will be accounted in missed calls when fails */ static char* log_fmt = ALL_LOG_FMT; /* Formating string that controls what information will be collected and accounted */ /* Attribute-value pairs */ static char* attrs_param = ""; avp_ident_t* avps; int avps_n; static char *radius_config = "/usr/local/etc/radiusclient-ng/radiusclient.conf"; static int service_type = -1; static int swap_dir = 0; static void *rh; static struct attr attrs[A_MAX]; static struct val vals[V_MAX]; static int acc_rad_request0(struct sip_msg *rq, char *p1, char *p2); static int acc_rad_missed0(struct sip_msg *rq, char *p1, char *p2); static int acc_rad_request1(struct sip_msg *rq, char *p1, char *p2); static int acc_rad_missed1(struct sip_msg *rq, char *p1, char *p2); static cmd_export_t cmds[] = { {"acc_rad_log", acc_rad_request0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"acc_rad_missed", acc_rad_missed0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"acc_rad_log", acc_rad_request1, 1, fixup_var_int_1, REQUEST_ROUTE | FAILURE_ROUTE}, {"acc_rad_missed", acc_rad_missed1, 1, fixup_var_int_1, REQUEST_ROUTE | FAILURE_ROUTE}, {0, 0, 0, 0, 0} }; static param_export_t params[] = { {"early_media", PARAM_INT, &early_media }, {"failed_transactions", PARAM_INT, &failed_transactions }, {"report_ack", PARAM_INT, &report_ack }, {"report_cancels", PARAM_INT, &report_cancels }, {"log_flag", PARAM_INT, &log_flag }, {"log_flag", PARAM_STRING|PARAM_USE_FUNC, fix_log_flag}, {"log_missed_flag", PARAM_INT, &log_missed_flag }, {"log_missed_flag", PARAM_STRING|PARAM_USE_FUNC, fix_log_missed_flag}, {"log_fmt", PARAM_STRING, &log_fmt }, {"attrs", PARAM_STRING, &attrs_param }, {"radius_config", PARAM_STRING, &radius_config }, {"service_type", PARAM_INT, &service_type }, {"swap_direction", PARAM_INT, &swap_dir }, {0, 0, 0} }; struct module_exports exports= { "acc_radius", cmds, /* exported functions */ 0, /* RPC methods */ params, /* exported params */ mod_init, /* initialization module */ 0, /* response function */ 0, /* destroy function */ 0, /* oncancel function */ 0 /* per-child init function */ }; /* fixes log_flag param (resolves possible named flags) */ static int fix_log_flag( modparam_t type, void* val) { return fix_flag(type, val, "acc_radius", "log_flag", &log_flag); } /* fixes log_missed_flag param (resolves possible named flags) */ static int fix_log_missed_flag( modparam_t type, void* val) { return fix_flag(type, val, "acc_radius", "log_missed_flag", &log_missed_flag); } static inline int skip_cancel(struct sip_msg *msg) { return (msg->REQ_METHOD == METHOD_CANCEL) && report_cancels == 0; } static int check_ftag(struct sip_msg* msg, str* uri) { param_hooks_t hooks; param_t* params; char* semi; struct to_body* from; str t; t = *uri; params = 0; semi = q_memchr(t.s, ';', t.len); if (!semi) { DBG("No ftag parameter found\n"); return -1; } t.len -= semi - uri->s + 1; t.s = semi + 1; trim_leading(&t); if (parse_params(&t, CLASS_URI, &hooks, ¶ms) < 0) { ERR("Error while parsing parameters\n"); return -1; } if (!hooks.uri.ftag) { DBG("No ftag parameter found\n"); goto err; } from = get_from(msg); if (!from || !from->tag_value.len || !from->tag_value.s) { DBG("No from tag parameter found\n"); goto err; } if (from->tag_value.len == hooks.uri.ftag->body.len && !strncmp(from->tag_value.s, hooks.uri.ftag->body.s, hooks.uri.ftag->body.len)) { DBG("Route ftag and From tag are same\n"); free_params(params); return 0; } else { DBG("Route ftag and From tag are NOT same\n"); free_params(params); return 1; } err: if (params) free_params(params); return -1; } static int get_direction(struct sip_msg* msg) { int ret; if (parse_orig_ruri(msg) < 0) { return -1; } if (!msg->parsed_orig_ruri_ok) { ERR("Error while parsing original Request-URI\n"); return -1; } ret = check_self(&msg->parsed_orig_ruri.host, msg->parsed_orig_ruri.port_no ? msg->parsed_orig_ruri.port_no : SIP_PORT, 0);/* match all protos*/ if (ret < 0) return -1; if (ret > 0) { /* Route is in ruri */ return check_ftag(msg, &msg->first_line.u.request.uri); } else { if (msg->route) { if (parse_rr(msg->route) < 0) { ERR("Error while parsing Route HF\n"); return -1; } ret = check_ftag(msg, &((rr_t*)msg->route->parsed)->nameaddr.uri); if (msg->route->parsed) free_rr((rr_t**)(void*)&msg->route->parsed); return ret; } else { DBG("No Route headers found\n"); return -1; } } } int verify_fmt(char *fmt) { if (!fmt) { LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string zero\n"); return -1; } if (!(*fmt)) { LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string empty\n"); return -1; } if (strlen(fmt) > ALL_LOG_FMT_LEN) { LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string too long\n"); return -1; } while(*fmt) { if (!strchr(ALL_LOG_FMT, *fmt)) { LOG(L_ERR, "ERROR:acc:verify_fmt: char in log_fmt invalid: %c\n", *fmt); return -1; } fmt++; } return 1; } /* * Return true if accounting is enabled and the * transaction is marked for accounting */ static inline int is_acc_on(struct sip_msg *rq) { return log_flag && isflagset(rq, log_flag) == 1; } /* * Return true if missed_call accounting is enabled * and the transaction has the flag set */ static inline int is_mc_on(struct sip_msg *rq) { return log_missed_flag && isflagset(rq, log_missed_flag) == 1; } static inline void preparse_req(struct sip_msg *rq) { /* try to parse from for From-tag for accounted transactions; * don't be worried about parsing outcome -- if it failed, * we will report N/A. There is no need to parse digest credentials * here even if we account them, because the authentication function * will do it before us and if not then we will account n/a. */ parse_headers(rq, HDR_CALLID_F | HDR_FROM_F | HDR_TO_F | HDR_CSEQ_F | HDR_ROUTE_F, 0 ); parse_from_header(rq); } /* is this reply of interest for accounting ? */ static inline int should_acc_reply(struct cell* t, int code) { struct sip_msg *r; r = t->uas.request; /* validation */ if (r == 0) { LOG(L_ERR, "ERROR:acc:should_acc_reply: 0 request\n"); return 0; } /* negative transactions reported otherwise only if explicitly * demanded */ if (!failed_transactions && code >= 300) return 0; if (!is_acc_on(r)) return 0; if (skip_cancel(r)) return 0; if (code < 200 && ! (early_media && code == 183)) return 0; return 1; /* seed is through, we will account this reply */ } /* Extract username attribute from authorized credentials */ static inline str* cred_user(struct sip_msg* rq) { struct hdr_field* h; auth_body_t* cred; get_authorized_cred(rq->proxy_auth, &h); if (!h) get_authorized_cred(rq->authorization, &h); if (!h) return 0; cred = (auth_body_t*)(h->parsed); if (!cred || !cred->digest.username.user.len) return 0; return &cred->digest.username.user; } /* Extract realm attribute from authorized credentials */ static inline str* cred_realm(struct sip_msg* rq) { str* realm; struct hdr_field* h; auth_body_t* cred; get_authorized_cred(rq->proxy_auth, &h); if (!h) get_authorized_cred(rq->authorization, &h); if (!h) return 0; cred = (auth_body_t*)(h->parsed); if (!cred) return 0; realm = GET_REALM(&cred->digest); if (!realm->len || !realm->s) { return 0; } return realm; } /* Return To header field from the request in case of faked reply or * missing To header field in the reply */ static inline struct hdr_field* valid_to(struct cell* t, struct sip_msg* reply) { if (reply == FAKED_REPLY || !reply || !reply->to) { return t->uas.request->to; } else { return reply->to; } } /* create an array of str's for accounting using a formatting string; * this is the heart of the accounting module -- it prints whatever * requested in a way, that can be used for syslog, radius, * sql, whatsoever * tm sip_msg_clones does not clone (shmmem-zed) parsed fields, other then Via1,2. Such fields clone now or use from rq_rp */ static int fmt2rad(char *fmt, struct sip_msg *rq, str* ouri, struct hdr_field *to, unsigned int code, VALUE_PAIR** send, time_t req_time) /* Timestamp of the request */ { static unsigned int cseq_num, src_port, src_ip; static time_t rq_time, rs_time; int cnt; struct to_body* from, *pto; str val, *cr, *at; struct cseq_body *cseq; struct attr* attr; int dir; cnt = 0; dir = -2; /* we don't care about parsing here; either the function * was called from script, in which case the wrapping function * is supposed to parse, or from reply processing in which case * TM should have preparsed from REQUEST_IN callback. */ while(*fmt) { if (cnt == ALL_LOG_FMT_LEN) { LOG(L_ERR, "ERROR:acc:fmt2rad: Formatting string is too long\n"); return 0; } attr = 0; switch(*fmt) { case 'a': /* attr */ at = print_attrs(avps, avps_n, 0); if (at) { attr = &attrs[A_SER_ATTR]; val = *at; } break; case 'c': /* sip_callid */ if (rq->callid && rq->callid->body.len) { attr = &attrs[A_ACCT_SESSION_ID]; val = rq->callid->body; } break; case 'd': /* to_tag */ if (swap_dir && dir == -2) dir = get_direction(rq); if (dir <= 0) { if (to && (pto = (struct to_body*)(to->parsed)) && pto->tag_value.len) { attr = &attrs[A_SIP_TO_TAG]; val = pto->tag_value; } } else { if (rq->from && (from = get_from(rq)) && from->tag_value.len) { attr = &attrs[A_SIP_TO_TAG]; val = from->tag_value; } } break; case 'f': /* sip_from */ if (rq->from && rq->from->body.len) { attr = &attrs[A_SER_FROM]; val = rq->from->body; } break; case 'g': /* flags */ attr = &attrs[A_SER_FLAGS]; val.s = (char*)&rq->flags; val.len = sizeof(unsigned int); break; case 'i': /* inbound_ruri */ attr = &attrs[A_SER_ORIGINAL_REQUEST_ID]; val = rq->first_line.u.request.uri; break; case 'm': /* sip_method */ attr = &attrs[A_SIP_METHOD]; val = rq->first_line.u.request.method; break; case 'n': /* sip_cseq */ if (rq->cseq && (cseq = get_cseq(rq)) && cseq->number.len) { attr = &attrs[A_SIP_CSEQ]; str2int(&cseq->number, &cseq_num); val.s = (char*)&cseq_num; val.len = sizeof(unsigned int); } break; case 'o': /* outbound_ruri */ attr = &attrs[A_SIP_TRANSLATED_REQUEST_ID]; val = *ouri; break; case 'p': /* Source IP address */ attr = &attrs[A_SIP_SOURCE_IP_ADDRESS]; src_ip = ntohl(rq->rcv.src_ip.u.addr32[0]); val.s = (char*)&src_ip; val.len = sizeof(src_ip); break; case 'r': /* from_tag */ if (swap_dir && dir == -2) dir = get_direction(rq); if (dir <= 0) { if (rq->from && (from = get_from(rq)) && from->tag_value.len) { attr = &attrs[A_SIP_FROM_TAG]; val = from->tag_value; } } else { if (to && (pto = (struct to_body*)(to->parsed)) && pto->tag_value.len) { attr = &attrs[A_SIP_FROM_TAG]; val = pto->tag_value; } } break; case 's': /* server_id */ attr = &attrs[A_SER_SERVER_ID]; val.s = (char*)&server_id; val.len = sizeof(int); break; case 't': /* sip_to */ if (to && to->body.len) { attr = &attrs[A_SER_TO]; val = to->body; } break; case 'u': /* digest_username */ cr = cred_user(rq); if (cr) { attr = &attrs[A_SER_DIGEST_USERNAME]; val = *cr; } break; case 'x': /* request_timestamp */ attr = &attrs[A_SER_REQUEST_TIMESTAMP]; rq_time = req_time; val.s = (char*)&rq_time; val.len = sizeof(time_t); break; case 'D': /* to_did */ break; case 'F': /* from_uri */ if (swap_dir && dir == -2) dir = get_direction(rq); if (dir <= 0) { if (rq->from && (from = get_from(rq)) && from->uri.len) { attr = &attrs[A_CALLING_STATION_ID]; val = from->uri; } } else { if (rq->to && (pto = get_to(rq)) && pto->uri.len) { attr = &attrs[A_CALLING_STATION_ID]; val = pto->uri; } } break; case 'I': /* from_uid */ if (get_from_uid(&val, rq) < 0) { attr = &attrs[A_SER_FROM_UID]; } break; case 'M': /* from_did */ break; case 'P': /* Source port */ attr = &attrs[A_SIP_SOURCE_PORT]; src_port = rq->rcv.src_port; val.s = (char*)&src_port; val.len = sizeof(unsigned int); break; case 'R': /* digest_realm */ cr = cred_realm(rq); if (cr) { attr = &attrs[A_SER_DIGEST_REALM]; val = *cr; } break; case 'S': /* sip_status */ attr = &attrs[A_SIP_RESPONSE_CODE]; val.s = (char*)&code; val.len = sizeof(unsigned int); break; case 'T': /* to_uri */ if (swap_dir && dir == -2) dir = get_direction(rq); if (dir <= 0) { if (rq->to && (pto = get_to(rq)) && pto->uri.len) { attr = &attrs[A_CALLED_STATION_ID]; val = pto->uri; } } else { if (rq->from && (from = get_from(rq)) && from->uri.len) { attr = &attrs[A_CALLED_STATION_ID]; val = from->uri; } } break; case 'U': /* to_uid */ if (get_from_uid(&val, rq) < 0) { attr = &attrs[A_SER_TO_UID]; } break; case 'X': /* response_timestamp */ attr = &attrs[A_SER_RESPONSE_TIMESTAMP]; rs_time = time(0); val.s = (char*)&rs_time; val.len = sizeof(time_t); break; default: LOG(L_CRIT, "BUG:acc:fmt2rad: unknown char: %c\n", *fmt); return -1; } /* switch (*fmt) */ if (attr) { if (!rc_avpair_add(rh, send, ATTRID(attr->v), val.s, val.len, VENDOR(attr->v))) { LOG(L_ERR, "ERROR:acc:fmt2rad: Failed to add attribute %s\n", attr->n); return -1; } } fmt++; cnt++; } /* while (*fmt) */ return 0; } /* * Return the value of Acc-Status-Type attribute based on SIP method * and response code */ static inline UINT4 rad_status(struct sip_msg *rq, unsigned int code) { /* Faked reply */ if (code == 0) { return vals[V_FAILED].v; } /* Successful call start */ if ((rq->REQ_METHOD == METHOD_INVITE || rq->REQ_METHOD == METHOD_ACK) && code >= 200 && code < 300) { return vals[V_START].v; } /* Successful call termination */ if ((rq->REQ_METHOD == METHOD_BYE || rq->REQ_METHOD == METHOD_CANCEL)) { return vals[V_STOP].v; } /* Successful transaction */ if (code >= 200 && code < 300) { return vals[V_INTERIM_UPDATE].v; } /* Otherwise it failed */ return vals[V_FAILED].v; } /* * Add User-Name attribute */ static inline int add_user_name(struct sip_msg* rq, void* rh, VALUE_PAIR** send) { struct sip_uri puri; str* user, *realm; str user_name; struct to_body* from; user = cred_user(rq); /* try to take it from credentials */ realm = cred_realm(rq); if (!user || !realm) { if (rq->from && (from = get_from(rq)) && from->uri.len) { if (parse_uri(from->uri.s, from->uri.len, &puri) < 0 ) { LOG(L_ERR, "ERROR:acc:add_user_name: Bad From URI\n"); return -1; } user = &puri.user; realm = &puri.host; } else { DBG("acc:add_user_name: Neither digest nor From found, mandatory attribute User-Name not added\n"); return -1; } } user_name.len = user->len + 1 + realm->len; user_name.s = pkg_malloc(user_name.len); if (!user_name.s) { LOG(L_ERR, "ERROR:acc:add_user_name: no memory\n"); return -1; } memcpy(user_name.s, user->s, user->len); user_name.s[user->len] = '@'; memcpy(user_name.s + user->len + 1, realm->s, realm->len); if (!rc_avpair_add(rh, send, ATTRID(attrs[A_USER_NAME].v), user_name.s, user_name.len, VENDOR(attrs[A_USER_NAME].v))) { LOG(L_ERR, "ERROR:acc:add_user_name: Failed to add User-Name attribute\n"); pkg_free(user_name.s); return -1; } pkg_free(user_name.s); return 0; } /* skip leading text and begin with first item's * separator ", " which will be overwritten by the * leading text later * */ static int log_request(struct sip_msg* rq, str* ouri, struct hdr_field* to, unsigned int code, time_t req_time) { VALUE_PAIR *send; UINT4 av_type; send = NULL; if (skip_cancel(rq)) return 1; if (fmt2rad(log_fmt, rq, ouri, to, code, &send, req_time) < 0) goto error; /* Add Acct-Status-Type attribute */ av_type = rad_status(rq, code); if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_ACCT_STATUS_TYPE].v), &av_type, -1, VENDOR(attrs[A_ACCT_STATUS_TYPE].v))) { ERR("Add Status-Type\n"); goto error; } /* Add Service-Type attribute */ av_type = (service_type != -1) ? service_type : vals[V_SIP_SESSION].v; if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_SERVICE_TYPE].v), &av_type, -1, VENDOR(attrs[A_SERVICE_TYPE].v))) { ERR("add STATUS_TYPE\n"); goto error; } /* Add User-Name attribute */ if (add_user_name(rq, rh, &send) < 0) goto error; /* Send the request out */ if (rc_acct(rh, SIP_PORT, send) != OK_RC) { ERR("RADIUS accounting request failed\n"); goto error; } rc_avpair_free(send); return 1; error: rc_avpair_free(send); return -1; } static void log_reply(struct cell* t , struct sip_msg* reply, unsigned int code, time_t req_time) { str* ouri; if (t->relayed_reply_branch >= 0) { ouri = &t->uac[t->relayed_reply_branch].uri; } else { ouri = GET_NEXT_HOP(t->uas.request); } log_request(t->uas.request, ouri, valid_to(t, reply), code, req_time); } static void log_ack(struct cell* t , struct sip_msg *ack, time_t req_time) { struct sip_msg *rq; struct hdr_field *to; rq = t->uas.request; if (ack->to) to = ack->to; else to = rq->to; log_request(ack, GET_RURI(ack), to, t->uas.status, req_time); } static void log_missed(struct cell* t, struct sip_msg* reply, unsigned int code, time_t req_time) { str* ouri; if (t->relayed_reply_branch >= 0) { ouri = &t->uac[t->relayed_reply_branch].uri; } else { ouri = GET_NEXT_HOP(t->uas.request); } log_request(t->uas.request, ouri , valid_to(t, reply), code, req_time); } /* these wrappers parse all what may be needed; they don't care about * the result -- accounting functions just display "unavailable" if there * is nothing meaningful */ static int acc_rad_request0(struct sip_msg *rq, char* p1, char* p2) { preparse_req(rq); return log_request(rq, GET_RURI(rq), rq->to, 0, time(0)); } /* these wrappers parse all what may be needed; they don't care about * the result -- accounting functions just display "unavailable" if there * is nothing meaningful */ static int acc_rad_missed0(struct sip_msg *rq, char* p1, char* p2) { preparse_req(rq); return log_request(rq, GET_RURI(rq), rq->to, 0, time(0)); } /* these wrappers parse all what may be needed; they don't care about * the result -- accounting functions just display "unavailable" if there * is nothing meaningful */ static int acc_rad_request1(struct sip_msg *rq, char* p1, char* p2) { int code; preparse_req(rq); if (get_int_fparam(&code, rq, (fparam_t*)p1) < 0) { code = 0; } return log_request(rq, GET_RURI(rq), rq->to, code, time(0)); } /* these wrappers parse all what may be needed; they don't care about * the result -- accounting functions just display "unavailable" if there * is nothing meaningful */ static int acc_rad_missed1(struct sip_msg *rq, char* p1, char* p2) { int code; preparse_req(rq); if (get_int_fparam(&code, rq, (fparam_t*)p1) < 0) { code = 0; } return log_request(rq, GET_RURI(rq), rq->to, code, time(0)); } static void ack_handler(struct cell* t, int type, struct tmcb_params* ps) { if (is_acc_on(t->uas.request)) { preparse_req(ps->req); log_ack(t, ps->req, (time_t)*(ps->param)); } } /* initiate a report if we previously enabled MC accounting for this t */ static void failure_handler(struct cell *t, int type, struct tmcb_params* ps) { /* validation */ if (t->uas.request == 0) { DBG("DBG:acc:failure_handler: No uas.request, skipping local transaction\n"); return; } if (is_invite(t) && ps->code >= 300) { if (is_mc_on(t->uas.request)) { log_missed(t, ps->rpl, ps->code, (time_t)*(ps->param)); resetflag(t->uas.request, log_missed_flag); } } } /* initiate a report if we previously enabled accounting for this t */ static void replyout_handler(struct cell* t, int type, struct tmcb_params* ps) { if (t->uas.request == 0) { DBG("DBG:acc:replyout_handler: No uas.request, local transaction, skipping\n"); return; } /* acc_onreply is bound to TMCB_REPLY which may be called * from _reply, like when FR hits; we should not miss this * event for missed calls either */ failure_handler(t, type, ps); if (!should_acc_reply(t, ps->code)) return; if (is_acc_on(t->uas.request)) log_reply(t, ps->rpl, ps->code, (time_t)*(ps->param)); } /* parse incoming replies before cloning */ static void replyin_handler(struct cell *t, int type, struct tmcb_params* ps) { /* validation */ if (t->uas.request == 0) { LOG(L_ERR, "ERROR:acc:replyin_handler:replyin_handler: 0 request\n"); return; } /* don't parse replies in which we are not interested */ /* missed calls enabled ? */ if (((is_invite(t) && ps->code >= 300 && is_mc_on(t->uas.request)) || should_acc_reply(t, ps->code)) && (ps->rpl && ps->rpl != FAKED_REPLY)) { parse_headers(ps->rpl, HDR_TO_F, 0); } } /* prepare message and transaction context for later accounting */ void on_req(struct cell* t, int type, struct tmcb_params *ps) { time_t req_time; /* Pass the timestamp of the request as a parameter to callbacks */ req_time = time(0); if (is_acc_on(ps->req) || is_mc_on(ps->req)) { if (tmb.register_tmcb(0, t, TMCB_RESPONSE_OUT, replyout_handler, (void*)req_time, 0) <= 0) { LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_RESPONSE_OUT callback\n"); return; } if (report_ack) { if (tmb.register_tmcb(0, t, TMCB_E2EACK_IN, ack_handler, (void*)req_time, 0) <= 0) { LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_E2EACK_IN callback\n"); return; } } if (tmb.register_tmcb(0, t, TMCB_ON_FAILURE_RO, failure_handler, (void*)req_time, 0) <= 0) { LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_ON_FAILURE_RO callback\n"); return; } if (tmb.register_tmcb(0, t, TMCB_RESPONSE_IN, replyin_handler, (void*)req_time, 0) <= 0) { LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_RESPONSE_IN callback\n"); return; } /* do some parsing in advance */ preparse_req(ps->req); /* also, if that is INVITE, disallow silent t-drop */ if (ps->req->REQ_METHOD == METHOD_INVITE) { DBG("DEBUG: noisy_timer set for accounting\n"); t->flags |= T_NOISY_CTIMER_FLAG; } } } static int mod_init(void) { DICT_VENDOR *vend; load_tm_f load_tm; /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) { LOG(L_ERR, "ERROR:acc:mod_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) return -1; if (verify_fmt(log_fmt)==-1) return -1; /* register callbacks*/ /* listen for all incoming requests */ if (tmb.register_tmcb( 0, 0, TMCB_REQUEST_IN, on_req, 0, 0) <= 0) { LOG(L_ERR,"ERROR:acc:mod_init: cannot register TMCB_REQUEST_IN " "callback\n"); return -1; } memset(attrs, 0, sizeof(attrs)); memset(vals, 0, sizeof(vals)); attrs[A_USER_NAME].n = "User-Name"; attrs[A_SERVICE_TYPE].n = "Service-Type"; attrs[A_CALLED_STATION_ID].n = "Called-Station-Id"; attrs[A_CALLING_STATION_ID].n = "Calling-Station-Id"; attrs[A_ACCT_STATUS_TYPE].n = "Acct-Status-Type"; attrs[A_ACCT_SESSION_ID].n = "Acct-Session-Id"; attrs[A_SIP_METHOD].n = "Sip-Method"; attrs[A_SIP_RESPONSE_CODE].n = "Sip-Response-Code"; attrs[A_SIP_CSEQ].n = "Sip-CSeq"; attrs[A_SIP_TO_TAG].n = "Sip-To-Tag"; attrs[A_SIP_FROM_TAG].n = "Sip-From-Tag"; attrs[A_SIP_TRANSLATED_REQUEST_ID].n = "Sip-Translated-Request-Id"; attrs[A_SIP_SOURCE_IP_ADDRESS].n = "Sip-Source-IP-Address"; attrs[A_SIP_SOURCE_PORT].n = "Sip-Source-Port"; attrs[A_SER_ATTR].n = "SER-Attr"; attrs[A_SER_FROM].n = "SER-From"; attrs[A_SER_FLAGS].n = "SER-Flags"; attrs[A_SER_ORIGINAL_REQUEST_ID].n = "SER-Original-Request-Id"; attrs[A_SER_TO].n = "SER-To"; attrs[A_SER_DIGEST_USERNAME].n = "SER-Digest-Username"; attrs[A_SER_DIGEST_REALM].n = "SER-Digest-Realm"; attrs[A_SER_REQUEST_TIMESTAMP].n = "SER-Request-Timestamp"; attrs[A_SER_TO_DID].n = "SER-To-DID"; attrs[A_SER_FROM_UID].n = "SER-From-UID"; attrs[A_SER_FROM_DID].n = "SER-From-DID"; attrs[A_SER_TO_UID].n = "SER-To-UID"; attrs[A_SER_RESPONSE_TIMESTAMP].n = "SER-Response-Timestamp"; attrs[A_SER_SERVER_ID].n = "SER-Server-ID"; vals[V_START].n = "Start"; vals[V_STOP].n = "Stop"; vals[V_INTERIM_UPDATE].n = "Interim-Update"; vals[V_FAILED].n = "Failed"; vals[V_SIP_SESSION].n = "Sip-Session"; /* open log */ rc_openlog("ser"); /* read config */ if ((rh = rc_read_config(radius_config)) == NULL) { LOG(L_ERR, "ERROR:acc:mod_init: Error opening radius config file: %s\n", radius_config); return -1; } /* read dictionary */ if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary")) != 0) { LOG(L_ERR, "ERROR:acc:mod_init: Error reading radius dictionary\n"); return -1; } vend = rc_dict_findvend(rh, "iptelorg"); if (vend == NULL) { ERR("RADIUS dictionary is missing required vendor 'iptelorg'\n"); return -1; } INIT_AV(rh, attrs, vals, "acc", -1, -1); if (service_type != -1) { vals[V_SIP_SESSION].v = service_type; } if (parse_attrs(&avps, &avps_n, attrs_param) < 0) { ERR("Error while parsing 'attrs' module parameter\n"); return -1; } return 0; } kamailio-4.0.4/obsolete/acc_radius/Makefile0000644000000000000000000000041112223032460017342 0ustar rootroot# $Id$ # # RADIUS Accounting module # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs include ../../Makefile.radius auto_gen= NAME=acc_radius.so DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/ratelimit/0000755000000000000000000000000012223032460015603 5ustar rootrootkamailio-4.0.4/obsolete/ratelimit/ratelimit.c0000644000000000000000000002635112223032460017750 0ustar rootroot/* * $Id$ * * ratelimit module * * Copyright (C) 2006 Hendrik Scholz * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "../../mem/shm_mem.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../timer.h" #include "../../ut.h" MODULE_VERSION /* use RED queuing algorithm? */ #define RL_WITH_RED 1 /* timer interval length in seconds, tunable via modparam */ #define RL_TIMER_INTERVAL 10 #ifndef rpc_lf #define rpc_lf(rpc, c) rpc->add(c, "s","") #endif /* globally visible parameters tunable via modparam and RPC interface */ int *invite_limit, *register_limit, *subscribe_limit = NULL; /* storage for initial modparam values */ int invite_limit_mp = 0; int register_limit_mp = 0; int subscribe_limit_mp = 0; int timer_interval = RL_TIMER_INTERVAL; /* in seconds */ /* internal counters */ int *invite_counter = NULL; int *register_counter = NULL; int *subscribe_counter = NULL; #if defined(RL_WITH_RED) /* load levels for Random Early Detection algorithm */ int *invite_load = NULL; int *register_load = NULL; int *subscribe_load = NULL; #endif /** module functions */ static int mod_init(void); static int child_init(int); static int rl_check(struct sip_msg*, char *, char *); #if defined (RL_WITH_RED) static int rl_limit_check(int, int, int); #else static int rl_limit_check(int, int); #endif static void timer(unsigned int, void *); static void destroy(void); static rpc_export_t rpc_methods[]; static cmd_export_t cmds[]={ {"rl_check", rl_check, 0, 0, REQUEST_ROUTE}, {0,0,0,0,0} }; static param_export_t params[]={ {"invite_limit", PARAM_INT, &invite_limit_mp}, {"register_limit", PARAM_INT, ®ister_limit_mp}, {"subscribe_limit", PARAM_INT, &subscribe_limit_mp}, {"timer_interval", PARAM_INT, &timer_interval}, {0,0,0} }; /** module exports */ struct module_exports exports= { "ratelimit", cmds, rpc_methods, params, mod_init, /* module initialization function */ (response_function) 0, (destroy_function) destroy, 0, child_init /* per-child init function */ }; /* initialize ratelimit module */ static int mod_init(void) { DBG("RATELIMIT: initializing ...\n"); /* register timer to reset counters */ if (register_timer(timer, 0, timer_interval) < 0) { LOG(L_ERR, "RATELIMIT:ERROR: could not register timer function\n"); return -1; } invite_counter = shm_malloc(sizeof(int)); register_counter = shm_malloc(sizeof(int)); subscribe_counter = shm_malloc(sizeof(int)); if (!invite_counter || !register_counter || !subscribe_counter) { LOG(L_ERR, "RATELIMIT:ERROR: no memory for counters\n"); return -1; } *invite_counter = 0; *register_counter = 0; *subscribe_counter = 0; invite_limit = shm_malloc(sizeof(int)); register_limit = shm_malloc(sizeof(int)); subscribe_limit = shm_malloc(sizeof(int)); if (!invite_limit || !register_limit || !subscribe_limit) { LOG(L_ERR, "RATELIMIT:ERROR: no memory for limit settings\n"); return -1; } /* obtain limits from modparam */ *invite_limit = invite_limit_mp; *register_limit = register_limit_mp; *subscribe_limit = subscribe_limit_mp; #if defined (RL_WITH_RED) /* these are only needed when using RED */ invite_load = shm_malloc(sizeof(int)); register_load = shm_malloc(sizeof(int)); subscribe_load = shm_malloc(sizeof(int)); if (!invite_load || !register_load || !subscribe_load) { LOG(L_ERR, "RATELIMIT:ERROR: no memory for load levels\n"); return -1; } *invite_load = -1; /* -1 = first run identifier */ *register_load = -1; *subscribe_load = -1; #endif return 0; } /* generic SER module functions */ static int child_init(int rank) { DBG("RATELIMIT:init_child #%d / pid <%d>\n", rank, getpid()); return 0; } static void destroy(void) { DBG("RATELIMIT: destroy module ...\n"); } /* ratelimit check * * return values: * -1: over limit (aka too many messages of that request type) * 0: internal error (not a request) * 1: within limit (let message through) */ static int rl_check(struct sip_msg* msg, char *_foo, char *_bar) { DBG("RATELIMIT:rl_check:invoked\n"); if (msg->first_line.type != SIP_REQUEST) { DBG("RATELIMIT:rl_check:not a request\n"); return 0; } if (msg->first_line.u.request.method_value == METHOD_INVITE) { if (*invite_limit == 0) return 1; *invite_counter = *invite_counter + 1; #if defined(RL_WITH_RED) return rl_limit_check(*invite_counter, *invite_limit, *invite_load); #else return rl_limit_check(*invite_counter, *invite_limit); #endif } else if (msg->first_line.u.request.method_value == METHOD_REGISTER) { if (*register_limit == 0) return 1; *register_counter = *register_counter + 1; #if defined(RL_WITH_RED) return rl_limit_check(*register_counter, *register_limit, *register_load); #else return rl_limit_check(*register_counter, *register_limit); #endif } else if (msg->first_line.u.request.method_value == METHOD_SUBSCRIBE) { if (*subscribe_limit == 0) return 1; *subscribe_counter = *subscribe_counter + 1; #if defined(RL_WITH_RED) return rl_limit_check(*subscribe_counter, *subscribe_limit, *subscribe_load); #else return rl_limit_check(*subscribe_counter, *subscribe_limit); #endif } else { return 0; } return -1; } /* timer housekeeping, invoked each timer interval to reset counters */ static void timer(unsigned int ticks, void *param) { DBG("RATELIMIT:timer:invoked\n"); #if defined(RL_WITH_RED) /* calculate load levels for RED */ if (*invite_limit > 0) { if (*invite_counter < *invite_limit) *invite_load = 0; else *invite_load = (int) (*invite_counter / *invite_limit); } if (*register_limit > 0) { if (*register_counter < *register_limit) *register_load = 0; else *register_load = (int) (*register_counter / *register_limit); } if (*subscribe_limit > 0) { if (*subscribe_counter < *subscribe_limit) *subscribe_load = 0; else *subscribe_load = (int) (*subscribe_counter / *subscribe_limit); } #endif /* clear counters */ if (*invite_limit > 0) *invite_counter = 0; if (*register_limit > 0) *register_counter = 0; if (*subscribe_limit > 0) *subscribe_counter = 0; } #if defined(RL_WITH_RED) /* rl_limit_check() RED implementation * * RED (Random Early Detection) is a queue management algorithm that tries * to counter the usual tail drop issues as seen in the trivial implementation * below. * * We monitor the load level and start dropping messages early on to achieve * an even utilization throughout the timer interval. * * The algorithm has no load indication during the first interval so we revert * to 'tail drop' for a simple start. * */ static int rl_limit_check(int cnt, int limit, int load) { DBG("RATELIMIT:rl_limit_check: invoked\n"); /* first run? */ if (load == -1) return (cnt > limit) ? -1 : 1; /* low load, no drops */ if (load <= 1) return 1; /* RED implementation, every load'th packet is let through */ return (!(cnt % load)) ? 1 : -1; } #else /* * rl_limit_check() trivial implementation (aka tail drop) * * check if counter is above limit and in that case return -1. * Returns 1 if still within limit. * * Caveats: * * If the timer interval is too large this 'algorithm' might cause * end device synchronization and bad traffic patterns, i.e. full traffic/load * in the beginning of the timer interval and no traffic at all once * the limit has been reached for the remaining interval. * */ static int rl_limit_check(int cnt, int limit) { DBG("RATELIMIT:rl_limit_check: invoked\n"); return (cnt > limit) ? -1 : 1; } #endif /* queue management algorithm selection */ /* * RPC functions * * rpc_stats() dumps the current config/statistics * rpc_{invite|register|subscribe}() set the limits * rpc_timer() sets the timer interval length * */ /* rpc function documentation */ static const char *rpc_stats_doc[2] = { "Print ratelimit statistics", 0 }; static const char *rpc_invite_doc[2] = { "Set INVITEs per timer interval limit", 0 }; static const char *rpc_register_doc[2] = { "Set REGISTERs per timer interval limit", 0 }; static const char *rpc_subscribe_doc[2] = { "Set SUBSCRIBEs per timer interval limit", 0 }; static const char *rpc_timer_doc[2] = { "Set the ratelimit timer_interval length", 0 }; /* rpc function implementations */ static void rpc_stats(rpc_t *rpc, void *c) { #if defined(RL_WITH_RED) if (rpc->printf(c, " INVITE: %d/%d (drop rate: %d)", *invite_counter, *invite_limit, *invite_load) < 0) return; rpc_lf(rpc, c); if (rpc->printf(c, " REGISTER: %d/%d (drop rate: %d)", *register_counter, *register_limit, *register_load) < 0) return; rpc_lf(rpc, c); if (rpc->printf(c, "SUBSCRIBE: %d/%d (drop rate: %d)", *subscribe_counter, *subscribe_limit, *subscribe_load) < 0) return; rpc_lf(rpc, c); #else if (rpc->printf(c, " INVITE: %d/%d", *invite_counter, *invite_limit) < 0) return; rpc_lf(rpc, c); if (rpc->printf(c, " REGISTER: %d/%d", *register_counter, *register_limit) < 0) return; rpc_lf(rpc, c); if (rpc->printf(c, "SUBSCRIBE: %d/%d", *subscribe_counter, *subscribe_limit) < 0) return; rpc_lf(rpc, c); #endif } static void rpc_invite(rpc_t *rpc, void *c) { int limit; if (rpc->scan(c, "d", &limit) < 1) { rpc->fault(c, 400, "Limit expected"); return; } if (limit < 0) { rpc->fault(c, 400, "limit must be >= 0 (0 = unlimited)"); return; } DBG("RATELIMIT:setting INVITE limit to %d messages\n", limit); *invite_limit = limit; } static void rpc_register(rpc_t *rpc, void *c) { int limit; if (rpc->scan(c, "d", &limit) < 1) { rpc->fault(c, 400, "Limit expected"); return; } if (limit < 0) { rpc->fault(c, 400, "limit must be >= 0 (0 = unlimited)"); return; } DBG("RATELIMIT:setting REGISTER limit to %d messages\n", limit); *register_limit = limit; } static void rpc_subscribe(rpc_t *rpc, void *c) { int limit; if (rpc->scan(c, "d", &limit) < 1) { rpc->fault(c, 400, "Limit expected"); return; } if (limit < 0) { rpc->fault(c, 400, "limit must be >= 0 (0 = unlimited)"); return; } DBG("RATELIMIT:setting SUBSCRIBE limit to %d messages\n", limit); *subscribe_limit = limit; } static void rpc_timer(rpc_t *rpc, void *c) { rpc->fault(c, 400, "Not yet implemented"); } static rpc_export_t rpc_methods[] = { {"rl.stats", rpc_stats, rpc_stats_doc, 0}, {"rl.invite_limit", rpc_invite, rpc_invite_doc, 0}, {"rl.register_limit", rpc_register, rpc_register_doc, 0}, {"rl.subscribe_limit", rpc_subscribe, rpc_subscribe_doc, 0}, {"rl.timer_interval", rpc_timer, rpc_timer_doc, 0}, {0, 0, 0, 0} }; kamailio-4.0.4/obsolete/ratelimit/doc/0000755000000000000000000000000012223032460016350 5ustar rootrootkamailio-4.0.4/obsolete/ratelimit/doc/functions.xml0000644000000000000000000000203612223032460021103 0ustar rootroot
$Revision$ $Date$ Functions
<function>rl.check()</function> Add the current request to the internal counters and determine if this request is above the limit. The ratelimit module has different internal counters for the supported request types and if a counter is exceeded the function returns an error code. <function>rl.check</function> usage ... if (method==INVITE) { if (!rl_check()) { sl_send_reply("503", "Service Unavailable"); break; } } ...
kamailio-4.0.4/obsolete/ratelimit/doc/ratelimit.xml0000644000000000000000000000752312223032460021073 0ustar rootroot
Hendrik Scholz hscholz@raisdorf.net 2006 Freenet Cityline GmbH $Revision$ $Date$ Ratelimit Module
Overview This module implements rate limiting for SIP requests. In contrast to the PIKE module this limits the flow based on a per SIP request type basis and not per source IP. The XML-RPC interface can be used to change tunables while running SER. Currently supported requests are INVITE, REGISTER and SUBSCRIBE.
Use Cases Limiting the rate messages are processed on a system directly influences the load. The ratelimit module can be used to protect a single host or to protect a SER cluster when run on the dispatching box in front. A sample configuration snippet might look like this: if (method=="INVITE" || method="REGISTER" || method=="SUBSCRIBE") { if (!rl_check()) { sl_send_reply("503", "Service Unavailable"); break; } }; Upon every incoming request listed above rl_check is invoked. It returns an OK code if the current per request load is below the configured threshold. If the load is exceeded the function returns an error and an administrator can discard requests with a stateless response.
Rate Limiting Algorithms As of writing the ratelimit module supports two different algorithms to be used by rl_check to determine whether a message should be blocked or not.
Tail drop This is a trivial algorithm that imposes some risks when used in conjunction with long timer intervals. At the start of each interval an internal counter is reset and incremented for each incoming message. Once the counter hits the configured limit rl_check returns an error. The downside of this algorithm is that it can lead to SIP client synchronization. During a relatively long interval only the first requests (i.e. REGISTERs) would make it through. Following messages (i.e. RE-REGISTERs) will all hit the SIP proxy at the same time when a common Expire timer expired. Other requests will be retransmissed after given time, the same on all devices with the same firmware/by the same vendor.
Random Early Detection (RED) Random Early Detection tries to circumvent the synchronization problem imposed by the tail drop algorithm by measuring the average load and adapting the drop rate dynamically. When running with the RED algorithm (enabled by default) SER will return errors to the SER routing engine every n'th packet trying to evenly spread the measured load of the last timer interval onto the current interval. As a negative side effect SER might drop messages although the limit might not be reached within the interval. Decrease the timer interval if you encounter this.
kamailio-4.0.4/obsolete/ratelimit/doc/fifo.xml0000644000000000000000000000345512223032460020024 0ustar rootroot
$Revision$ $Date$ FIFO Interface The number of allowed requests per interval as well as the interval length itself can be modified over the FIFO interface of SER. In addition rate limiting statistics are provided upon request. rl.stats - Get current per request limits as well as current load levels for all request types. rl.invite_limit - Set the number of allowed INVITE requests per interval to the given value as the sole parameter. A limit of 0 turns of rate limiting of INVITE messages. rl.register_limit - Set the number of allowed REGISTER requests per interval to the given value as the sole parameter. A limit of 0 turns of rate limiting of REGISTER messages. rl.subscribe_limit - Set the number of allowed SUBSCRIBE requests per interval to the given value as the sole parameter. A limit of 0 turns of rate limiting of SUBSCRIBE messages.
kamailio-4.0.4/obsolete/ratelimit/doc/Makefile0000644000000000000000000000013212223032460020004 0ustar rootrootdocs = ratelimit.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/ratelimit/doc/params.xml0000644000000000000000000000443012223032460020356 0ustar rootroot
$Revision$ $Date$ Parameters
<varname>timer_interval</varname> (integer) The initial length of a timer interval in seconds. All amounts of messages have to be divided by this timer to get a messages per second value. Default value is 5 seconds.
<varname>invite_limit</varname> (integer) The initial number of allowed INVITE requests per timer interval. Once this number has been reached within one interval rl_check returns an error. If running in Random Early Detection (RED) mode (default) rl_check preemtively starts dropping packets. Default value is 0 which means unlimited requests are permitted.
<varname>register_limit</varname> (integer) The initial number of allowed REGISTER requests per timer interval. Once this number has been reached within one interval rl_check returns an error. If running in Random Early Detection (RED) mode (default) rl_check preemtively starts dropping packets. Default value is 0 which means unlimited requests are permitted.
<varname>subscribe_limit</varname> (integer) The initial number of allowed SUBSCRIBE requests per timer interval. Once this number has been reached within one interval rl_check returns an error. If running in Random Early Detection (RED) mode (default) rl_check preemtively starts dropping packets. Default value is 0 which means unlimited requests are permitted.
kamailio-4.0.4/obsolete/ratelimit/README0000644000000000000000000001263712223032460016474 0ustar rootroot Ratelimit Module Hendrik Scholz Copyright © 2006 Freenet Cityline GmbH Revision History Revision $Revision$ $Date$ _________________________________________________________________ Overview This module implements rate limiting for SIP requests. In contrast to the PIKE module this limits the flow based on a per SIP request type basis and not per source IP. The XML-RPC interface can be used to change tunables while running SER. Currently supported requests are INVITE, REGISTER and SUBSCRIBE. Use Cases Limiting the rate messages are processed on a system directly influences the load. The ratelimit module can be used to protect a single host or to protect a SER cluster when run on the dispatching box in front. A sample configuration snippet might look like this: if (method=="INVITE" || method="REGISTER" || method=="SUBSCRIBE") { if (!rl_check()) { sl_send_reply("503", "Service Unavailable"); break; } }; Upon every incoming request listed above rl_check is invoked. It returns an OK code if the current per request load is below the configured threshold. If the load is exceeded the function returns an error and an administrator can discard requests with a stateless response. Rate Limiting Algorithms As of writing the ratelimit module supports two different algorithms to be used by rl_check to determine whether a message should be blocked or not. Tail drop This is a trivial algorithm that imposes some risks when used in conjunction with long timer intervals. At the start of each interval an internal counter is reset and incremented for each incoming message. Once the counter hits the configured limit rl_check returns an error. The downside of this algorithm is that it can lead to SIP client synchronization. During a relatively long interval only the first requests (i.e. REGISTERs) would make it through. Following messages (i.e. RE-REGISTERs) will all hit the SIP proxy at the same time when a common Expire timer expired. Other requests will be retransmissed after given time, the same on all devices with the same firmware/by the same vendor. Random Early Detection (RED) Random Early Detection tries to circumvent the synchronization problem imposed by the tail drop algorithm by measuring the average load and adapting the drop rate dynamically. When running with the RED algorithm (enabled by default) SER will return errors to the SER routing engine every n'th packet trying to evenly spread the measured load of the last timer interval onto the current interval. As a negative side effect SER might drop messages although the limit might not be reached within the interval. Decrease the timer interval if you encounter this. Parameters timer_interval (integer) The initial length of a timer interval in seconds. All amounts of messages have to be divided by this timer to get a messages per second value. Default value is 5 seconds. invite_limit (integer) The initial number of allowed INVITE requests per timer interval. Once this number has been reached within one interval rl_check returns an error. If running in Random Early Detection (RED) mode (default) rl_check preemtively starts dropping packets. Default value is 0 which means unlimited requests are permitted. register_limit (integer) The initial number of allowed REGISTER requests per timer interval. Once this number has been reached within one interval rl_check returns an error. If running in Random Early Detection (RED) mode (default) rl_check preemtively starts dropping packets. Default value is 0 which means unlimited requests are permitted. subscribe_limit (integer) The initial number of allowed SUBSCRIBE requests per timer interval. Once this number has been reached within one interval rl_check returns an error. If running in Random Early Detection (RED) mode (default) rl_check preemtively starts dropping packets. Default value is 0 which means unlimited requests are permitted. Functions rl.check() Add the current request to the internal counters and determine if this request is above the limit. The ratelimit module has different internal counters for the supported request types and if a counter is exceeded the function returns an error code. Example 1. rl.check usage ... if (method==INVITE) { if (!rl_check()) { sl_send_reply("503", "Service Unavailable"); break; } } ... FIFO Interface The number of allowed requests per interval as well as the interval length itself can be modified over the FIFO interface of SER. In addition rate limiting statistics are provided upon request. * rl.stats - Get current per request limits as well as current load levels for all request types. * rl.invite_limit - Set the number of allowed INVITE requests per interval to the given value as the sole parameter. A limit of 0 turns of rate limiting of INVITE messages. * rl.register_limit - Set the number of allowed REGISTER requests per interval to the given value as the sole parameter. A limit of 0 turns of rate limiting of REGISTER messages. * rl.subscribe_limit - Set the number of allowed SUBSCRIBE requests per interval to the given value as the sole parameter. A limit of 0 turns of rate limiting of SUBSCRIBE messages. kamailio-4.0.4/obsolete/ratelimit/Makefile0000644000000000000000000000036312223032460017245 0ustar rootroot# $Id$ # # ratelimit module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=ratelimit.so LIBS= DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/nathelper/0000755000000000000000000000000012223032460015573 5ustar rootrootkamailio-4.0.4/obsolete/nathelper/nhelpr_funcs.c0000644000000000000000000002232012223032460020424 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2003-11-06 body len is computed using the message len (it's * not taken any more from the msg. content-length) (andrei) */ #include #include #include #include #include "nhelpr_funcs.h" #include "../../dprint.h" #include "../../config.h" #include "../../ut.h" #include "../../forward.h" #include "../../resolve.h" #include "../../globals.h" #include "../../udp_server.h" #include "../../pt.h" #include "../../parser/msg_parser.h" #include "../../trim.h" #include "../../parser/parse_from.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_uri.h" #define READ(val) \ (*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24)) #define advance(_ptr,_n,_str,_error) \ do{\ if ((_ptr)+(_n)>(_str).s+(_str).len)\ goto _error;\ (_ptr) = (_ptr) + (_n);\ }while(0); #define one_of_16( _x , _t ) \ (_x==_t[0]||_x==_t[15]||_x==_t[8]||_x==_t[2]||_x==_t[3]||_x==_t[4]\ ||_x==_t[5]||_x==_t[6]||_x==_t[7]||_x==_t[1]||_x==_t[9]||_x==_t[10]\ ||_x==_t[11]||_x==_t[12]||_x==_t[13]||_x==_t[14]) #define one_of_8( _x , _t ) \ (_x==_t[0]||_x==_t[7]||_x==_t[1]||_x==_t[2]||_x==_t[3]||_x==_t[4]\ ||_x==_t[5]||_x==_t[6]) int check_content_type(struct sip_msg *msg) { static unsigned int appl[16] = { 0x6c707061/*appl*/,0x6c707041/*Appl*/,0x6c705061/*aPpl*/, 0x6c705041/*APpl*/,0x6c507061/*apPl*/,0x6c507041/*ApPl*/, 0x6c505061/*aPPl*/,0x6c505041/*APPl*/,0x4c707061/*appL*/, 0x4c707041/*AppL*/,0x4c705061/*aPpL*/,0x4c705041/*APpL*/, 0x4c507061/*apPL*/,0x4c507041/*ApPL*/,0x4c505061/*aPPL*/, 0x4c505041/*APPL*/}; static unsigned int icat[16] = { 0x74616369/*icat*/,0x74616349/*Icat*/,0x74614369/*iCat*/, 0x74614349/*ICat*/,0x74416369/*icAt*/,0x74416349/*IcAt*/, 0x74414369/*iCAt*/,0x74414349/*ICAt*/,0x54616369/*icaT*/, 0x54616349/*IcaT*/,0x54614369/*iCaT*/,0x54614349/*ICaT*/, 0x54416369/*icAT*/,0x54416349/*IcAT*/,0x54414369/*iCAT*/, 0x54414349/*ICAT*/}; static unsigned int ion_[8] = { 0x006e6f69/*ion_*/,0x006e6f49/*Ion_*/,0x006e4f69/*iOn_*/, 0x006e4f49/*IOn_*/,0x004e6f69/*ioN_*/,0x004e6f49/*IoN_*/, 0x004e4f69/*iON_*/,0x004e4f49/*ION_*/}; static unsigned int sdp_[8] = { 0x00706473/*sdp_*/,0x00706453/*Sdp_*/,0x00704473/*sDp_*/, 0x00704453/*SDp_*/,0x00506473/*sdP_*/,0x00506453/*SdP_*/, 0x00504473/*sDP_*/,0x00504453/*SDP_*/}; str str_type; unsigned int x; char *p; if (!msg->content_type) { LOG(L_WARN,"WARNING: check_content_type: Content-TYPE header absent!" "let's assume the content is text/plain ;-)\n"); return 1; } trim_len(str_type.len,str_type.s,msg->content_type->body); p = str_type.s; advance(p,4,str_type,error_1); x = READ(p-4); if (!one_of_16(x,appl)) goto other; advance(p,4,str_type,error_1); x = READ(p-4); if (!one_of_16(x,icat)) goto other; advance(p,3,str_type,error_1); x = READ(p-3) & 0x00ffffff; if (!one_of_8(x,ion_)) goto other; /* skip spaces and tabs if any */ while (*p==' ' || *p=='\t') advance(p,1,str_type,error_1); if (*p!='/') { LOG(L_ERR, "ERROR:check_content_type: parse error:" "no / found after primary type\n"); goto error; } advance(p,1,str_type,error_1); while ((*p==' ' || *p=='\t') && p+1 found valid\n", (int)(p-str_type.s), str_type.s); return 1; } else { LOG(L_ERR,"ERROR:check_content_type: bad end for type!\n"); return -1; } error_1: LOG(L_ERR,"ERROR:check_content_type: parse error: body ended :-(!\n"); error: return -1; other: LOG(L_ERR,"ERROR:check_content_type: invalid type for a message\n"); return -1; } /* * Get message body and check Content-Type header field */ int extract_body(struct sip_msg *msg, str *body ) { char c; int skip; body->s = get_body(msg); if (body->s==0) { LOG(L_ERR, "ERROR: extract_body: failed to get the message body\n"); goto error; } body->len = msg->len -(int)(body->s-msg->buf); if (body->len==0) { LOG(L_ERR, "ERROR: extract_body: message body has length zero\n"); goto error; } /* no need for parse_headers(msg, EOH), get_body will * parse everything */ /*is the content type correct?*/ if (check_content_type(msg)==-1) { LOG(L_ERR,"ERROR: extract_body: content type mismatching\n"); goto error; } for (skip = 0; skip < body->len; skip++) { c = body->s[body->len - skip - 1]; if (c != '\r' && c != '\n') break; } if (skip == body->len) { LOG(L_ERR, "ERROR: extract_body: empty body\n"); goto error; } body->len -= skip; /*DBG("DEBUG:extract_body:=|%.*s|\n",body->len,body->s);*/ return 1; error: return -1; } /* * ser_memmem() returns the location of the first occurrence of data * pattern b2 of size len2 in memory block b1 of size len1 or * NULL if none is found. Obtained from NetBSD. */ void * ser_memmem(const void *b1, const void *b2, size_t len1, size_t len2) { /* Initialize search pointer */ char *sp = (char *) b1; /* Initialize pattern pointer */ char *pp = (char *) b2; /* Initialize end of search address space pointer */ char *eos = sp + len1 - len2; /* Sanity check */ if(!(b1 && b2 && len1 && len2)) return NULL; while (sp <= eos) { if (*sp == *pp) if (memcmp(sp, pp, len2) == 0) return sp; sp++; } return NULL; } /* * Some helper functions taken verbatim from tm module. */ /* * Extract Call-ID value * assumes the callid header is already parsed * (so make sure it is, before calling this function or * it might fail even if the message _has_ a callid) */ int get_callid(struct sip_msg* _m, str* _cid) { if ((parse_headers(_m, HDR_CALLID_F, 0) == -1)) { LOG(L_ERR, "get_callid(): parse_headers() failed\n"); return -1; } if (_m->callid == NULL) { LOG(L_ERR, "get_callid(): Call-ID not found\n"); return -1; } _cid->s = _m->callid->body.s; _cid->len = _m->callid->body.len; trim(_cid); return 0; } /* * Extract tag from To header field of a response * assumes the to header is already parsed, so * make sure it really is before calling this function */ int get_to_tag(struct sip_msg* _m, str* _tag) { if (!_m->to) { LOG(L_ERR, "get_to_tag(): To header field missing\n"); return -1; } if (get_to(_m)->tag_value.len) { _tag->s = get_to(_m)->tag_value.s; _tag->len = get_to(_m)->tag_value.len; } else { _tag->s = 0; /* fixes gcc 4.0 warnings */ _tag->len = 0; } return 0; } /* * Extract tag from From header field of a request */ int get_from_tag(struct sip_msg* _m, str* _tag) { if (parse_from_header(_m) == -1) { LOG(L_ERR, "get_from_tag(): Error while parsing From header\n"); return -1; } if (get_from(_m)->tag_value.len) { _tag->s = get_from(_m)->tag_value.s; _tag->len = get_from(_m)->tag_value.len; } else { _tag->len = 0; } return 0; } /* * Extract URI from the Contact header field */ int get_contact_uri(struct sip_msg* _m, struct sip_uri *uri, contact_t** _c) { if ((parse_headers(_m, HDR_CONTACT_F, 0) == -1) || !_m->contact) return -1; if (!_m->contact->parsed && parse_contact(_m->contact) < 0) { DBG("nathelper: Error while parsing Contact body\n"); return -1; } *_c = ((contact_body_t*)_m->contact->parsed)->contacts; if (*_c == NULL) { DBG("nathelper: Error while parsing Contact body\n"); return -1; } if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) { DBG("nathelper: Error while parsing Contact URI\n"); return -1; } return 0; } kamailio-4.0.4/obsolete/nathelper/nathelper.cfg0000644000000000000000000000310112223032460020231 0ustar rootroot# # $Id$ # # example script showing use of nathelper module # (incomplete for sake of brevity) # # ----------- global configuration parameters ------------------------ # debugging mode debug=3 fork=no log_stderror=yes # ------------------ module loading ---------------------------------- loadmodule "modules/nathelper/nathelper.so" loadmodule "modules/textops/textops.so" loadmodule "modules/tm/tm.so" loadmodule "modules/rr/rr.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/sl/sl.so" # ------------------ request-routing script -------------------------- # main routing logic route{ # compulsory processing of Route header fields and adding RR loose_route(); # ATA's are symmetric but don't advertise it -- force use of rport if (search("User-Agent: Cisco ATA.*")) { setflag(1); # remember this is ATA force_rport(); fix_nated_contact(); }; /* registration (uses rewritten contacts) */ if (method=="REGISTER") { save("foo.bar.com"); break; }; if (method=="INVITE") { record_route(); if (isflagset(1)) { # ATA ? fix_nated_sdp("3"); }; /* set up reply processing */ t_on_reply("1"); }; if (method == "INVITE" || method == "CANCEL") { if (!lookup("foo.bar.com")) { sl_send_reply("404", "Not Found"); break; }; }; /* set up reply processing and forward statefuly */ t_relay(); } # all incoming replies for t_onrepli-ed transactions enter here onreply_route[1] { if (status=~"2[0-9][0-9]" && search("Server: Cisco ATA.*")) fix_nated_contact(); fix_nated_sdp("3"); } kamailio-4.0.4/obsolete/nathelper/nathelper.c0000644000000000000000000007553612223032460017741 0ustar rootroot/* $Id$ * * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2003-10-09 nat_uac_test introduced (jiri) * * 2003-11-06 nat_uac_test permitted from onreply_route (jiri) * * 2003-12-01 unforce_rtp_proxy introduced (sobomax) * * 2004-01-07 RTP proxy support updated to support new version of the * RTP proxy (20040107). * * force_rtp_proxy() now inserts a special flag * into the SDP body to indicate that this session already * proxied and ignores sessions with such flag. * * Added run-time check for version of command protocol * supported by the RTP proxy. * * 2004-01-16 Integrated slightly modified patch from Tristan Colgate, * force_rtp_proxy function with IP as a parameter (janakj) * * 2004-01-28 nat_uac_test extended to allow testing SDP body (sobomax) * * nat_uac_test extended to allow testing top Via (sobomax) * * 2004-02-21 force_rtp_proxy now accepts option argument, which * consists of string of chars, each of them turns "on" * some feature, currently supported ones are: * * `a' - flags that UA from which message is received * doesn't support symmetric RTP; * `l' - force "lookup", that is, only rewrite SDP when * corresponding session is already exists in the * RTP proxy. Only makes sense for SIP requests, * replies are always processed in "lookup" mode; * `i' - flags that message is received from UA in the * LAN. Only makes sense when RTP proxy is running * in the bridge mode. * * force_rtp_proxy can now be invoked without any arguments, * as previously, with one argument - in this case argument * is treated as option string and with two arguments, in * which case 1st argument is option string and the 2nd * one is IP address which have to be inserted into * SDP (IP address on which RTP proxy listens). * * 2004-03-12 Added support for IPv6 addresses in SDPs. Particularly, * force_rtp_proxy now can work with IPv6-aware RTP proxy, * replacing IPv4 address in SDP with IPv6 one and vice versa. * This allows creating full-fledged IPv4<->IPv6 gateway. * See 4to6.cfg file for example. * * Two new options added into force_rtp_proxy: * * `f' - instructs nathelper to ignore marks inserted * by another nathelper in transit to indicate * that the session is already goes through another * proxy. Allows creating chain of proxies. * `r' - flags that IP address in SDP should be trusted. * Without this flag, nathelper ignores address in the * SDP and uses source address of the SIP message * as media address which is passed to the RTP proxy. * * Protocol between nathelper and RTP proxy in bridge * mode has been slightly changed. Now RTP proxy expects SER * to provide 2 flags when creating or updating session * to indicate direction of this session. Each of those * flags can be either `e' or `i'. For example `ei' means * that we received INVITE from UA on the "external" network * network and will send it to the UA on "internal" one. * Also possible `ie' (internal->external), `ii' * (internal->internal) and `ee' (external->external). See * example file alg.cfg for details. * * 2004-03-15 If the rtp proxy test failed (wrong version or not started) * retry test from time to time, when some *rtpproxy* function * is invoked. Minimum interval between retries can be * configured via rtpproxy_disable_tout module parameter (default * is 60 seconds). Setting it to -1 will disable periodic * rechecks completely, setting it to 0 will force checks * for each *rtpproxy* function call. (andrei) * * 2004-03-22 Fix assignment of rtpproxy_retr and rtpproxy_tout module * parameters. * * 2004-03-22 Fix get_body position (should be called before get_callid) * (andrei) * * 2004-03-24 Fix newport for null ip address case (e.g onhold re-INVITE) * (andrei) * * 2004-09-30 added received port != via port test (andrei) * * 2004-10-10 force_socket option introduced (jiri) * * 2005-02-24 Added support for using more than one rtp proxy, in which * case traffic will be distributed evenly among them. In addition, * each such proxy can be assigned a weight, which will specify * which share of the traffic should be placed to this particular * proxy. * * Introduce failover mechanism, so that if SER detects that one * of many proxies is no longer available it temporarily decreases * its weight to 0, so that no traffic will be assigned to it. * Such "disabled" proxies are periodically checked to see if they * are back to normal in which case respective weight is restored * resulting in traffic being sent to that proxy again. * * Those features can be enabled by specifying more than one "URI" * in the rtpproxy_sock parameter, optionally followed by the weight, * which if absent is assumed to be 1, for example: * * rtpproxy_sock="unix:/foo/bar=4 udp:1.2.3.4:3456=3 udp:5.6.7.8:5432=1" * * 2005-02-25 Force for pinging the socket returned by USRLOC (bogdan) * * 2005-03-22 Support for multiple media streams added (netch) * * 2005-04-27 Support for doing natpinging using real SIP requests added. * Requires tm module for doing its work. Old method (sending UDP * with 4 zero bytes can be selected by specifying natping_method="null". * * 2005-12-23 Support for selecting particular RTP proxy node has been added. * In force_rtp_proxy() it can be done via new N modifier, followed * by the index (starting at 0) of the node in the rtpproxy_sock * parameter. For example, in the example above force_rtp_proxy("N1") will * will select node udp:1.2.3.4:3456. In unforce_rtp_proxy(), the same * can be done by specifying index as an argument directly, i.e. * unforce_rtp_proxy(1). * * Since nathelper is not transaction or call stateful, care should be * taken to ensure that force_rtp_proxy() in request path matches * force_rtp_proxy() in reply path, that is the same node is selected. * * 2006-06-10 Select nathepler.rewrite_contact * pingcontact function (tma) * * 2007-04-23 Do NAT pinging in the separate dedicated process. It provides much * better scalability than doing it in the main one. * * 2007-04-23 New function start_recording() allowing to start recording RTP * session in the RTP proxy. * * 2007-08-28 natping_crlf option was introduced (jiri) * * 2007-09-12 Added stateless sip natping (andrei) * * 2007-12-10 IP address in the session header is now updated along with * address(es) in media description (andrei). * * force_rtp_proxy() now accepts zNNN parameter and passes it to the * RTP proxy. * * New functions rtpproxy_offer() and rtpproxy_answer() to allow * using RTP proxy in the cases when SDP offer is in 2xx and SDP * answer in ACK. * */ #include "nhelpr_funcs.h" #include "nathelper.h" #include "../../flags.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../error.h" #include "../../forward.h" #include "../../mem/mem.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" #include "../../parser/parse_uri.h" #include "../../parser/parser_f.h" #include "../../resolve.h" #include "../../timer.h" #include "../../trim.h" #include "../../ut.h" #include "../registrar/sip_msg.h" #include "../../msg_translator.h" #include "../usrloc/usrloc.h" #include "../../usr_avp.h" #include "../../socket_info.h" #include "../../select.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_VERSION #if !defined(AF_LOCAL) #define AF_LOCAL AF_UNIX #endif #if !defined(PF_LOCAL) #define PF_LOCAL PF_UNIX #endif /* NAT UAC test constants */ #define NAT_UAC_TEST_C_1918 0x01 #define NAT_UAC_TEST_RCVD 0x02 #define NAT_UAC_TEST_V_1918 0x04 #define NAT_UAC_TEST_S_1918 0x08 #define NAT_UAC_TEST_RPORT 0x10 #define NAT_UAC_TEST_C_PORT 0x20 static int nat_uac_test_f(struct sip_msg* msg, char* str1, char* str2); static int fix_nated_contact_f(struct sip_msg *, char *, char *); static int fix_nated_sdp_f(struct sip_msg *, char *, char *); static int extract_mediaip(str *, str *, int *); static int alter_mediaip(struct sip_msg *, str *, str *, int, str *, int, int); static int fix_nated_register_f(struct sip_msg *, char *, char *); static int fixup_ping_contact(void **param, int param_no); static int ping_contact_f(struct sip_msg* msg, char* str1, char* str2); static int mod_init(void); static void mod_cleanup(void); static int child_init(int); struct socket_info* force_socket = 0; static struct { const char *cnetaddr; uint32_t netaddr; uint32_t mask; } nets_1918[] = { {"10.0.0.0", 0, 0xffffffffu << 24}, {"172.16.0.0", 0, 0xffffffffu << 20}, {"192.168.0.0", 0, 0xffffffffu << 16}, {NULL, 0, 0} }; static char *force_socket_str = 0; static cmd_export_t cmds[] = { {"fix_nated_contact", fix_nated_contact_f, 0, NULL, REQUEST_ROUTE | ONREPLY_ROUTE }, {"fix_nated_sdp", fix_nated_sdp_f, 1, fixup_int_1, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE }, {"nat_uac_test", nat_uac_test_f, 1, fixup_int_1, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE }, {"fix_nated_register", fix_nated_register_f, 0, NULL, REQUEST_ROUTE }, {"ping_contact", ping_contact_f, 1, fixup_ping_contact, REQUEST_ROUTE }, {0, 0, 0, 0, 0} }; static param_export_t params[] = { {"natping_interval", PARAM_INT, &natping_interval }, {"natping_crlf", PARAM_INT, &natping_crlf }, {"natping_method", PARAM_STRING, &natping_method }, {"natping_stateful", PARAM_INT, &natping_stateful }, {"ping_nated_only", PARAM_INT, &ping_nated_only }, {"force_socket", PARAM_STRING, &force_socket_str }, {0, 0, 0} }; struct module_exports exports = { "nathelper", cmds, 0, /* RPC methods */ params, mod_init, intercept_ping_reply, /* reply processing */ mod_cleanup, /* destroy function */ 0, /* on_break */ child_init }; static int sel_nathelper(str* res, select_t* s, struct sip_msg* msg) { /* dummy */ return 0; } static int sel_rewrite_contact(str* res, select_t* s, struct sip_msg* msg); SELECT_F(select_any_nameaddr) select_row_t sel_declaration[] = { { NULL, SEL_PARAM_STR, STR_STATIC_INIT("nathelper"), sel_nathelper, SEL_PARAM_EXPECTED}, { sel_nathelper, SEL_PARAM_STR, STR_STATIC_INIT("rewrite_contact"), sel_rewrite_contact, CONSUME_NEXT_INT }, { sel_rewrite_contact, SEL_PARAM_STR, STR_STATIC_INIT("nameaddr"), select_any_nameaddr, NESTED | CONSUME_NEXT_STR}, { NULL, SEL_PARAM_INT, STR_NULL, NULL, 0} }; static int mod_init(void) { int i; struct in_addr addr; str socket_str; if (force_socket_str != NULL) { socket_str.s = force_socket_str; socket_str.len = strlen(socket_str.s); force_socket = grep_sock_info(&socket_str, 0, 0); } if (natpinger_init() < 0) { LOG(L_ERR, "nathelper: natpinger_init() failed\n"); return -1; } /* Prepare 1918 networks list */ for (i = 0; nets_1918[i].cnetaddr != NULL; i++) { if (inet_aton(nets_1918[i].cnetaddr, &addr) != 1) abort(); nets_1918[i].netaddr = ntohl(addr.s_addr) & nets_1918[i].mask; } register_select_table(sel_declaration); return 0; } static void mod_cleanup(void) { natpinger_cleanup(); } static int child_init(int rank) { if (natpinger_child_init(rank) < 0) return -1; return 0; } static int isnulladdr(str *sx, int pf) { char *cp; if (pf == AF_INET6) { for(cp = sx->s; cp < sx->s + sx->len; cp++) if (*cp != '0' && *cp != ':') return 0; return 1; } return (sx->len == 7 && memcmp("0.0.0.0", sx->s, 7) == 0); } /* * Replaces ip:port pair in the Contact: field with the source address * of the packet. */ static int fix_nated_contact_f(struct sip_msg* msg, char* str1, char* str2) { int offset, len, len1; char *cp, *buf, temp[2]; contact_t *c; struct lump *anchor; struct sip_uri uri; str hostport; if (get_contact_uri(msg, &uri, &c) == -1) return -1; /* for UAs behind NAT we have to hope that they will reuse the * TCP connection, otherwise they are lost any way. So this check * does not make too much sense. if (uri.proto != PROTO_UDP && uri.proto != PROTO_NONE) return -1; */ if ((c->uri.s < msg->buf) || (c->uri.s > (msg->buf + msg->len))) { LOG(L_ERR, "ERROR: you can't call fix_nated_contact twice, " "check your config!\n"); return -1; } offset = c->uri.s - msg->buf; anchor = del_lump(msg, offset, c->uri.len, HDR_CONTACT_T); if (anchor == 0) return -1; hostport = uri.host; if (uri.port.len > 0) hostport.len = uri.port.s + uri.port.len - uri.host.s; cp = ip_addr2a(&msg->rcv.src_ip); len = c->uri.len + strlen(cp) + 6 /* :port */ - hostport.len + 1; buf = pkg_malloc(len); if (buf == NULL) { LOG(L_ERR, "ERROR: fix_nated_contact: out of memory\n"); return -1; } temp[0] = hostport.s[0]; temp[1] = c->uri.s[c->uri.len]; c->uri.s[c->uri.len] = hostport.s[0] = '\0'; len1 = snprintf(buf, len, "%s%s:%d%s", c->uri.s, cp, msg->rcv.src_port, hostport.s + hostport.len); if (len1 < len) len = len1; hostport.s[0] = temp[0]; c->uri.s[c->uri.len] = temp[1]; if (insert_new_lump_after(anchor, buf, len, HDR_CONTACT_T) == 0) { pkg_free(buf); return -1; } c->uri.s = buf; c->uri.len = len; return 1; } static int sel_rewrite_contact(str* res, select_t* s, struct sip_msg* msg) { static char buf[500]; contact_t* c; int n, def_port_fl, len; char *cp; str hostport; struct sip_uri uri; res->len = 0; n = s->params[2].v.i; if (n <= 0) { LOG(L_ERR, "ERROR: rewrite_contact[%d]: zero or negative index not supported\n", n); return -1; } c = 0; do { if (contact_iterator(&c, msg, c) < 0 || !c) return -1; n--; } while (n > 0); if (parse_uri(c->uri.s, c->uri.len, &uri) < 0 || uri.host.len <= 0) { LOG(L_ERR, "rewrite_contact[%d]: Error while parsing Contact URI\n", s->params[2].v.i); return -1; } len = c->len - uri.host.len; if (uri.port.len > 0) len -= uri.port.len; def_port_fl = (msg->rcv.proto == PROTO_TLS && msg->rcv.src_port == SIPS_PORT) || (msg->rcv.proto != PROTO_TLS && msg->rcv.src_port == SIP_PORT); if (!def_port_fl) len += 1/*:*/+5/*port*/; if (len > sizeof(buf)) { LOG(L_ERR, "ERROR: rewrite_contact[%d]: contact too long\n", s->params[2].v.i); return -1; } hostport = uri.host; if (uri.port.len > 0) hostport.len = uri.port.s + uri.port.len - uri.host.s; res->s = buf; res->len = hostport.s - c->name.s; memcpy(buf, c->name.s, res->len); cp = ip_addr2a(&msg->rcv.src_ip); if (def_port_fl) { res->len+= snprintf(buf+res->len, sizeof(buf)-res->len, "%s", cp); } else { res->len+= snprintf(buf+res->len, sizeof(buf)-res->len, "%s:%d", cp, msg->rcv.src_port); } memcpy(buf+res->len, hostport.s+hostport.len, c->len-(hostport.s+hostport.len-c->name.s)); res->len+= c->len-(hostport.s+hostport.len-c->name.s); return 0; } /* * Test if IP address pointed to by saddr belongs to RFC1918 networks */ static inline int is1918addr(str *saddr) { struct in_addr addr; uint32_t netaddr; int i, rval; char backup; rval = -1; backup = saddr->s[saddr->len]; saddr->s[saddr->len] = '\0'; if (inet_aton(saddr->s, &addr) != 1) goto theend; netaddr = ntohl(addr.s_addr); for (i = 0; nets_1918[i].cnetaddr != NULL; i++) { if ((netaddr & nets_1918[i].mask) == nets_1918[i].netaddr) { rval = 1; goto theend; } } rval = 0; theend: saddr->s[saddr->len] = backup; return rval; } /* * test for occurrence of RFC1918 IP address in Contact HF */ static int contact_1918(struct sip_msg* msg) { struct sip_uri uri; contact_t* c; if (get_contact_uri(msg, &uri, &c) == -1) return -1; return (is1918addr(&(uri.host)) == 1) ? 1 : 0; } static int contact_rport(struct sip_msg* msg) { struct sip_uri uri; contact_t* c; if (get_contact_uri(msg, &uri, &c) == -1) { return -1; } if (msg->rcv.src_port != (uri.port_no ? uri.port_no : SIP_PORT)) { return 1; } else { return 0; } } /* * test for occurrence of RFC1918 IP address in SDP */ static int sdp_1918(struct sip_msg* msg) { str body, ip; int pf; if (extract_body(msg, &body) == -1) { LOG(L_ERR,"ERROR: sdp_1918: cannot extract body from msg!\n"); return 0; } if (extract_mediaip(&body, &ip, &pf) == -1) { LOG(L_ERR, "ERROR: sdp_1918: can't extract media IP from the SDP\n"); return 0; } if (pf != AF_INET || isnulladdr(&ip, pf)) return 0; return (is1918addr(&ip) == 1) ? 1 : 0; } /* * test for occurrence of RFC1918 IP address in top Via */ static int via_1918(struct sip_msg* msg) { return (is1918addr(&(msg->via1->host)) == 1) ? 1 : 0; } static int nat_uac_test_f(struct sip_msg* msg, char* str1, char* str2) { int tests; if (get_int_fparam(&tests, msg, (fparam_t*) str1) < 0) return -1; /* return true if any of the NAT-UAC tests holds */ /* test if the source port is different from the port in Via */ if ((tests & NAT_UAC_TEST_RPORT) && (msg->rcv.src_port != (msg->via1->port ? msg->via1->port : SIP_PORT))) { return 1; } /* * test if source address of signaling is different from * address advertised in Via */ if ((tests & NAT_UAC_TEST_RCVD) && received_test(msg)) return 1; /* * test for occurrences of RFC1918 addresses in Contact * header field */ if ((tests & NAT_UAC_TEST_C_1918) && (contact_1918(msg) > 0)) return 1; /* * test for occurrences of RFC1918 addresses in SDP body */ if ((tests & NAT_UAC_TEST_S_1918) && sdp_1918(msg)) return 1; /* * test for occurrences of RFC1918 addresses top Via */ if ((tests & NAT_UAC_TEST_V_1918) && via_1918(msg)) return 1; /* * test if source port of signaling is different from * port advertised in Contact */ if ((tests & NAT_UAC_TEST_C_PORT) && (contact_rport(msg) > 0)) return 1; /* no test succeeded */ return -1; } #define ADD_ADIRECTION 0x01 #define FIX_MEDIP 0x02 #define ADD_ANORTPPROXY 0x04 #define ADD_ADIRECTIONP 0x08 #define ADIRECTION "a=direction:active" #define ADIRECTION_LEN (sizeof(ADIRECTION) - 1) #define ADIRECTIONP "a=direction:passive" #define ADIRECTIONP_LEN (sizeof(ADIRECTIONP) - 1) #define AOLDMEDIP "a=oldmediaip:" #define AOLDMEDIP_LEN (sizeof(AOLDMEDIP) - 1) #define AOLDMEDIP6 "a=oldmediaip6:" #define AOLDMEDIP6_LEN (sizeof(AOLDMEDIP6) - 1) #define AOLDMEDPRT "a=oldmediaport:" #define AOLDMEDPRT_LEN (sizeof(AOLDMEDPRT) - 1) #define ANORTPPROXY "a=nortpproxy:yes" #define ANORTPPROXY_LEN (sizeof(ANORTPPROXY) - 1) static int fix_nated_sdp_f(struct sip_msg* msg, char* str1, char* str2) { str body, body1, oldip, newip; int level, pf; char *buf; struct lump* anchor; if (get_int_fparam(&level, msg, (fparam_t*) str1) < 0) return -1; if (extract_body(msg, &body) == -1) { LOG(L_ERR,"ERROR: fix_nated_sdp: cannot extract body from msg!\n"); return -1; } if (level & (ADD_ADIRECTION | ADD_ANORTPPROXY | ADD_ADIRECTIONP)) { msg->msg_flags |= FL_FORCE_ACTIVE; anchor = anchor_lump(msg, body.s + body.len - msg->buf, 0, 0); if (anchor == NULL) { LOG(L_ERR, "ERROR: fix_nated_sdp: anchor_lump failed\n"); return -1; } if (level & ADD_ADIRECTION) { buf = pkg_malloc((ADIRECTION_LEN + CRLF_LEN) * sizeof(char)); if (buf == NULL) { LOG(L_ERR, "ERROR: fix_nated_sdp: out of memory\n"); return -1; } memcpy(buf, CRLF, CRLF_LEN); memcpy(buf + CRLF_LEN, ADIRECTION, ADIRECTION_LEN); if (insert_new_lump_after(anchor, buf, ADIRECTION_LEN + CRLF_LEN, 0) == NULL) { LOG(L_ERR, "ERROR: fix_nated_sdp: insert_new_lump_after failed\n"); pkg_free(buf); return -1; } } if (level & ADD_ADIRECTIONP) { buf = pkg_malloc((ADIRECTIONP_LEN + CRLF_LEN) * sizeof(char)); if (buf == NULL) { LOG(L_ERR, "ERROR: fix_nated_sdp: out of memory\n"); return -1; } memcpy(buf, CRLF, CRLF_LEN); memcpy(buf + CRLF_LEN, ADIRECTIONP, ADIRECTIONP_LEN); if (insert_new_lump_after(anchor, buf, ADIRECTIONP_LEN + CRLF_LEN, 0) == NULL) { LOG(L_ERR, "ERROR: fix_nated_sdp: insert_new_lump_after failed\n"); pkg_free(buf); return -1; } } if (level & ADD_ANORTPPROXY) { buf = pkg_malloc((ANORTPPROXY_LEN + CRLF_LEN) * sizeof(char)); if (buf == NULL) { LOG(L_ERR, "ERROR: fix_nated_sdp: out of memory\n"); return -1; } memcpy(buf, CRLF, CRLF_LEN); memcpy(buf + CRLF_LEN, ANORTPPROXY, ANORTPPROXY_LEN); if (insert_new_lump_after(anchor, buf, ANORTPPROXY_LEN + CRLF_LEN, 0) == NULL) { LOG(L_ERR, "ERROR: fix_nated_sdp: insert_new_lump_after failed\n"); pkg_free(buf); return -1; } } } if (level & FIX_MEDIP) { /* Iterate all c= and replace ips in them. */ unsigned hasreplaced = 0; int pf1 = 0; str body2; char *bodylimit = body.s + body.len; newip.s = ip_addr2a(&msg->rcv.src_ip); newip.len = strlen(newip.s); body1 = body; for(;;) { if (extract_mediaip(&body1, &oldip, &pf) == -1) break; if (pf != AF_INET) { LOG(L_ERR, "ERROR: fix_nated_sdp: " "not an IPv4 address in SDP\n"); goto finalize; } if (!pf1) pf1 = pf; else if (pf != pf1) { LOG(L_ERR, "ERROR: fix_nated_sdp: mismatching " "address families in SDP\n"); return -1; } body2.s = oldip.s + oldip.len; body2.len = bodylimit - body2.s; if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf, 1) == -1) { LOG(L_ERR, "ERROR: fix_nated_sdp: can't alter media IP\n"); return -1; } hasreplaced = 1; body1 = body2; } if (!hasreplaced) { LOG(L_ERR, "ERROR: fix_nated_sdp: can't extract media IP from the SDP\n"); goto finalize; } } finalize: return 1; } static int extract_mediaip(str *body, str *mediaip, int *pf) { char *cp, *cp1; int len, nextisip; cp1 = NULL; for (cp = body->s; (len = body->s + body->len - cp) > 0;) { cp1 = ser_memmem(cp, "c=", len, 2); if (cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r') break; cp = cp1 + 2; } if (cp1 == NULL) { LOG(L_ERR, "ERROR: extract_mediaip: no `c=' in SDP\n"); return -1; } mediaip->s = cp1 + 2; mediaip->len = eat_line(mediaip->s, body->s + body->len - mediaip->s) - mediaip->s; trim_len(mediaip->len, mediaip->s, *mediaip); nextisip = 0; for (cp = mediaip->s; cp < mediaip->s + mediaip->len;) { len = eat_token_end(cp, mediaip->s + mediaip->len) - cp; if (nextisip == 1) { mediaip->s = cp; mediaip->len = len; nextisip++; break; } if (len == 3 && memcmp(cp, "IP", 2) == 0) { switch (cp[2]) { case '4': nextisip = 1; *pf = AF_INET; break; case '6': nextisip = 1; *pf = AF_INET6; break; default: break; } } cp = eat_space_end(cp + len, mediaip->s + mediaip->len); } if (nextisip != 2 || mediaip->len == 0) { LOG(L_ERR, "ERROR: extract_mediaip: " "no `IP[4|6]' in `c=' field\n"); return -1; } return 1; } static int alter_mediaip(struct sip_msg *msg, str *body, str *oldip, int oldpf, str *newip, int newpf, int preserve) { char *buf; int offset; struct lump* anchor; str omip, nip, oip; /* check that updating mediaip is really necessary */ if (oldpf == newpf && isnulladdr(oldip, oldpf)) return 0; if (newip->len == oldip->len && memcmp(newip->s, oldip->s, newip->len) == 0) return 0; /* * Since rewriting the same info twice will mess SDP up, * apply simple anti foot shooting measure - put flag on * messages that have been altered and check it when * another request comes. */ #if 0 /* disabled: * - alter_mediaip is called twice if 2 c= lines are present * in the sdp (and we want to allow it) * - the message flags are propagated in the on_reply_route * => if we set the flags for the request they will be seen for the * reply too, but we don't want that * --andrei */ if (msg->msg_flags & FL_SDP_IP_AFS) { LOG(L_ERR, "ERROR: alter_mediaip: you can't rewrite the same " "SDP twice, check your config!\n"); return -1; } #endif if (preserve != 0) { anchor = anchor_lump(msg, body->s + body->len - msg->buf, 0, 0); if (anchor == NULL) { LOG(L_ERR, "ERROR: alter_mediaip: anchor_lump failed\n"); return -1; } if (oldpf == AF_INET6) { omip.s = AOLDMEDIP6; omip.len = AOLDMEDIP6_LEN; } else { omip.s = AOLDMEDIP; omip.len = AOLDMEDIP_LEN; } buf = pkg_malloc(omip.len + oldip->len + CRLF_LEN); if (buf == NULL) { LOG(L_ERR, "ERROR: alter_mediaip: out of memory\n"); return -1; } memcpy(buf, CRLF, CRLF_LEN); memcpy(buf + CRLF_LEN, omip.s, omip.len); memcpy(buf + CRLF_LEN + omip.len, oldip->s, oldip->len); if (insert_new_lump_after(anchor, buf, omip.len + oldip->len + CRLF_LEN, 0) == NULL) { LOG(L_ERR, "ERROR: alter_mediaip: insert_new_lump_after failed\n"); pkg_free(buf); return -1; } } if (oldpf == newpf) { nip.len = newip->len; nip.s = pkg_malloc(nip.len); if (nip.s == NULL) { LOG(L_ERR, "ERROR: alter_mediaip: out of memory\n"); return -1; } memcpy(nip.s, newip->s, newip->len); } else { nip.len = newip->len + 2; nip.s = pkg_malloc(nip.len); if (nip.s == NULL) { LOG(L_ERR, "ERROR: alter_mediaip: out of memory\n"); return -1; } memcpy(nip.s + 2, newip->s, newip->len); nip.s[0] = (newpf == AF_INET6) ? '6' : '4'; nip.s[1] = ' '; } oip = *oldip; if (oldpf != newpf) { do { oip.s--; oip.len++; } while (*oip.s != '6' && *oip.s != '4'); } offset = oip.s - msg->buf; anchor = del_lump(msg, offset, oip.len, 0); if (anchor == NULL) { LOG(L_ERR, "ERROR: alter_mediaip: del_lump failed\n"); pkg_free(nip.s); return -1; } #if 0 msg->msg_flags |= FL_SDP_IP_AFS; #endif if (insert_new_lump_after(anchor, nip.s, nip.len, 0) == 0) { LOG(L_ERR, "ERROR: alter_mediaip: insert_new_lump_after failed\n"); pkg_free(nip.s); return -1; } return 0; } /* * Auxiliary for some functions. * Returns pointer to first character of found line, or NULL if no such line. */ #define DSTIP_PARAM ";dstip=" #define DSTIP_PARAM_LEN (sizeof(DSTIP_PARAM) - 1) #define DSTPORT_PARAM ";dstport=" #define DSTPORT_PARAM_LEN (sizeof(DSTPORT_PARAM) - 1) /* * Create received SIP uri that will be either * passed to registrar in an AVP or apended * to Contact header field as a parameter */ static int create_rcv_uri(str* uri, struct sip_msg* m) { static char buf[MAX_URI_SIZE]; char* p; str src_ip, src_port, dst_ip, dst_port; int len; str proto; if (!uri || !m) { LOG(L_ERR, "create_rcv_uri: Invalid parameter value\n"); return -1; } src_ip.s = ip_addr2a(&m->rcv.src_ip); src_ip.len = strlen(src_ip.s); src_port.s = int2str(m->rcv.src_port, &src_port.len); dst_ip = m->rcv.bind_address->address_str; dst_port = m->rcv.bind_address->port_no_str; switch(m->rcv.proto) { case PROTO_NONE: case PROTO_UDP: proto.s = 0; /* Do not add transport parameter, UDP is default */ proto.len = 0; break; case PROTO_TCP: proto.s = "TCP"; proto.len = 3; break; case PROTO_TLS: proto.s = "TLS"; proto.len = 3; break; case PROTO_SCTP: proto.s = "SCTP"; proto.len = 4; break; default: LOG(L_ERR, "BUG: create_rcv_uri: Unknown transport protocol\n"); return -1; } len = 4 + src_ip.len + 1 + src_port.len; if (proto.s) { len += TRANSPORT_PARAM_LEN; len += proto.len; } len += DSTIP_PARAM_LEN + dst_ip.len; len += DSTPORT_PARAM_LEN + dst_port.len; if (m->rcv.src_ip.af == AF_INET6) { len += 2; } if (len > MAX_URI_SIZE) { LOG(L_ERR, "create_rcv_uri: Buffer too small\n"); return -1; } p = buf; /* as transport=tls is deprecated shouldnt this be sips in case of TLS? */ memcpy(p, "sip:", 4); p += 4; if (m->rcv.src_ip.af == AF_INET6) *p++ = '['; memcpy(p, src_ip.s, src_ip.len); p += src_ip.len; if (m->rcv.src_ip.af == AF_INET6) *p++ = ']'; *p++ = ':'; memcpy(p, src_port.s, src_port.len); p += src_port.len; if (proto.s) { memcpy(p, TRANSPORT_PARAM, TRANSPORT_PARAM_LEN); p += TRANSPORT_PARAM_LEN; memcpy(p, proto.s, proto.len); p += proto.len; } memcpy(p, DSTIP_PARAM, DSTIP_PARAM_LEN); p += DSTIP_PARAM_LEN; memcpy(p, dst_ip.s, dst_ip.len); p += dst_ip.len; memcpy(p, DSTPORT_PARAM, DSTPORT_PARAM_LEN); p += DSTPORT_PARAM_LEN; memcpy(p, dst_port.s, dst_port.len); p += dst_port.len; uri->s = buf; uri->len = len; return 0; } /* * Create an AVP to be used by registrar with the source IP and port * of the REGISTER */ static int fix_nated_register_f(struct sip_msg* msg, char* str1, char* str2) { contact_t* c; struct lump* anchor; char* param; str uri; if (create_rcv_uri(&uri, msg) < 0) { return -1; } if (contact_iterator(&c, msg, 0) < 0) { return -1; } while(c) { param = (char*)pkg_malloc(RECEIVED_LEN + 2 + uri.len); if (!param) { ERR("No memory left\n"); return -1; } memcpy(param, RECEIVED, RECEIVED_LEN); param[RECEIVED_LEN] = '\"'; memcpy(param + RECEIVED_LEN + 1, uri.s, uri.len); param[RECEIVED_LEN + 1 + uri.len] = '\"'; anchor = anchor_lump(msg, c->name.s + c->len - msg->buf, 0, 0); if (anchor == NULL) { ERR("anchor_lump failed\n"); return -1; } if (insert_new_lump_after(anchor, param, RECEIVED_LEN + 1 + uri.len + 1, 0) == 0) { ERR("insert_new_lump_after failed\n"); pkg_free(param); return -1; } if (contact_iterator(&c, msg, c) < 0) { return -1; } } return 1; } static int fixup_ping_contact(void **param, int param_no) { int ret; if (param_no == 1) { ret = fix_param(FPARAM_AVP, param); if (ret <= 0) return ret; if (fix_param(FPARAM_STR, param) != 0) return -1; } return 0; } static int ping_contact_f(struct sip_msg *msg, char *str1, char *str2) { struct dest_info dst; str s; avp_t *avp; avp_value_t val; switch (((fparam_t *)str1)->type) { case FPARAM_AVP: if (!(avp = search_first_avp(((fparam_t *)str1)->v.avp.flags, ((fparam_t *)str1)->v.avp.name, &val, 0))) { return -1; } else { if ((avp->flags & AVP_VAL_STR) == 0) return -1; s = val.s; } break; case FPARAM_STRING: s = ((fparam_t *)str1)->v.str; break; default: ERR("BUG: Invalid parameter value in ping_contact\n"); return -1; } init_dest_info(&dst); return natping_contact(s, &dst); } kamailio-4.0.4/obsolete/nathelper/natping.c0000644000000000000000000003243712223032460017410 0ustar rootroot/* $Id$ * * Copyright (C) 2005-2008 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * LONG-YEARS-AGO, natping.c was born without history tracking in it * 2007-08-28 natping_crlf option was introduced (jiri) * */ #include #include #include "../usrloc/usrloc.h" #include "../../modules/tm/tm_load.h" #include "../../dprint.h" #include "../../parser/parse_hostport.h" #include "../../resolve.h" #include "../../cfg/cfg_struct.h" #include "nathelper.h" int natping_interval = 0; /* * If this parameter is set then the natpinger will ping only contacts * that have the NAT flag set in user location database */ int ping_nated_only = 0; /* * If this parameter is set, then pings will not * be full requests but only CRLFs */ int natping_crlf = 1; /* * Ping method. Any word except NULL is treated as method name. */ char *natping_method = NULL; int natping_stateful = 0; static pid_t aux_process = -1; static usrloc_api_t ul; /* TM bind */ static struct tm_binds tmb; static int cblen = 0; static char sbuf[4] = (CRLF CRLF); static void natping(unsigned int ticks, void *param); static void natping_cycle(void); int natpinger_init(void) { bind_usrloc_t bind_usrloc; load_tm_f load_tm; char *p; if (natping_interval > 0) { bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0); if (bind_usrloc == NULL) { LOG(L_ERR, "ERROR: nathelper::natpinger_init: Can't find usrloc module\n"); return -1; } if (bind_usrloc(&ul) < 0) { return -1; } if (natping_method != NULL) { for (p = natping_method; *p != '\0'; ++p) *p = toupper(*p); if (strcmp(natping_method, "NULL") == 0) natping_method = NULL; } if (natping_method != NULL) { /* import the TM auto-loading function */ load_tm = (load_tm_f)find_export("load_tm", NO_SCRIPT, 0); if (load_tm == NULL) { LOG(L_ERR, "ERROR: nathelper::natpinger_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm(&tmb) == -1) return -1; } /* * Use timer only in single process. For forked SER, * use separate process (see natpinger_child_init()) */ if (dont_fork) { register_timer(natping, NULL, natping_interval); } else { register_procs(1); /* register the separate natpinger process */ /* The process will keep updating its configuration */ cfg_register_child(1); } if (natping_method == NULL) { if (natping_crlf == 0) LOG(L_WARN, "WARNING: nathelper::natpinger_init: " "natping_crlf==0 has no effect, please also set " "natping_method\n"); if (natping_stateful != 0) LOG(L_WARN, "WARNING: nathelper::natpinger_init: " "natping_stateful!=0 has no effect, please also set " "natping_method\n"); } else if (natping_crlf != 0 && natping_stateful != 0) { LOG(L_WARN, "WARNING: nathelper::natpinger_init: " "natping_crlf!=0 has no effect when the" "natping_stateful!=0\n"); } } return 0; } int natpinger_child_init(int rank) { /* If forking is prohibited, use only timer. */ if (dont_fork) return 0; /* * Fork only from PROC_MAIN (see doc/modules_init.txt) and only * if ping is requested. */ if ((rank != PROC_MAIN) || (natping_interval == 0)) return 0; /* * Create a new "ser" process, with access to the tcp send, but * don't call child_init() for this process (no need, it's just a pinger). */ aux_process = fork_process(PROC_NOCHLDINIT, "nathelper pinger", 1); if (aux_process == -1) { LOG(L_ERR, "natping_child_init(): fork: %s\n", strerror(errno)); return -1; } if (aux_process == 0) { /* initialize the config framework */ if (cfg_child_init()) return -1; natping_cycle(); /* UNREACHED */ _exit(1); } return 0; } int natpinger_cleanup(void) { if (aux_process != -1) kill(aux_process, SIGTERM); return 0; } static void natping_cycle(void) { signal(SIGTERM, SIG_DFL); /* no special treat */ for(;;) { sleep(natping_interval); /* update the local config */ cfg_update(); natping(0, NULL); } } #define PING_FROM "f:" #define PING_FROM_LEN (sizeof(PING_FROM)-1) #define PING_FROMTAG ";tag=1" #define PING_FROMTAG_LEN (sizeof(PING_FROMTAG)-1) #define PING_TO "t:" #define PING_TO_LEN (sizeof(PING_TO)-1) #define PING_CALLID "i:" #define PING_CALLID_LEN (sizeof(PING_CALLID)-1) #define PING_CSEQ "CSeq: 1" #define PING_CSEQ_LEN (sizeof(PING_CSEQ)-1) #define PING_CLEN "l: 0" #define PING_CLEN_LEN (sizeof(PING_CLEN)-1) /* * Ping branch format: * ^ prefix ^ */ #define PING_BRANCH_PREFIX MCOOKIE "-GnIp-" #define PING_BRANCH_PREFIX_LEN (sizeof(PING_BRANCH_PREFIX)-1) struct nat_ping_params { str uri; str method; str from_uri; str to_uri; struct dest_info* send_info; }; static unsigned int ping_no = 0; /* per process ping number */ /* * Build a minimal nat ping message (pkg_malloc'ed) * returns: pointer to message and sets *len on success, 0 on error * Note: the message must be pkg_free()'d * * Message format: * * * Via: ...;branch= * f: ;tag=1 * t: * c: seq * cseq: 1 * l: 0 */ char * sip_ping_builder(unsigned int* len, struct nat_ping_params* params) { str via; char branch_buf[PING_BRANCH_PREFIX_LEN+INT2STR_MAX_LEN]; str branch_str; str callid_str; char* msg; int size; char callid_no_buf[INT2STR_MAX_LEN]; int callid_no_buf_free; char* t; via.s = 0; msg = 0; callid_no_buf_free = sizeof(callid_no_buf); t = callid_no_buf; int2reverse_hex(&t, &callid_no_buf_free, ping_no + (process_no << 20)); callid_str.s = callid_no_buf; callid_str.len = (int)(t - callid_no_buf); /* build branch: MCOOKIE SEP PING_MAGIC SEP callid_str */ branch_str.len = PING_BRANCH_PREFIX_LEN + callid_str.len; if (branch_str.len > sizeof(branch_buf)) { LOG(L_WARN, "WARNING: nathelper::sip_ping_builder: branch buffer too small (%d)\n", branch_str.len); /* truncate */ callid_str.len = sizeof(branch_buf) - PING_BRANCH_PREFIX_LEN; branch_str.len = sizeof(branch_buf); } t = branch_buf; memcpy(t, PING_BRANCH_PREFIX, PING_BRANCH_PREFIX_LEN); t += PING_BRANCH_PREFIX_LEN; memcpy(t, callid_str.s, callid_str.len); branch_str.s = branch_buf; via.s = via_builder((unsigned int *)&via.len, params->send_info, &branch_str, 0, 0); if (via.s == NULL) { LOG(L_ERR, "ERROR: nathelper::sip_ping_builder: via_builder failed\n"); goto error; } size = params->method.len + 1 /* space */ + params->uri.len + 1 /* space */ + SIP_VERSION_LEN + CRLF_LEN + via.len /* CRLF included */ + PING_FROM_LEN + 1 /* space */ + params->from_uri.len /* ; included in fromtag */ + PING_FROMTAG_LEN + CRLF_LEN + PING_TO_LEN + 1 /* space */ + params->to_uri.len + CRLF_LEN + PING_CALLID_LEN + 1 /* space */ + callid_str.len + CRLF_LEN + PING_CSEQ_LEN + 1 /* space */ + params->method.len + CRLF_LEN + PING_CLEN_LEN + CRLF_LEN + CRLF_LEN; ping_no++; msg = pkg_malloc(size); if (msg == NULL) { LOG(L_ERR, "ERROR: nathelper::sip_ping_builder: out of memory\n"); goto error; } /* build the message */ t = msg; /* first line */ memcpy(t, params->method.s, params->method.len); t += params->method.len; *t = ' '; t++; memcpy(t, params->uri.s, params->uri.len); t += params->uri.len; *t = ' '; t++; memcpy(t, SIP_VERSION, SIP_VERSION_LEN); t += SIP_VERSION_LEN; memcpy(t, CRLF, CRLF_LEN); t += CRLF_LEN; /* via */ memcpy(t, via.s, via.len); t += via.len; /* from */ memcpy(t, PING_FROM, PING_FROM_LEN); t += PING_FROM_LEN; *t = ' '; t++; memcpy(t, params->from_uri.s, params->from_uri.len); t += params->from_uri.len; memcpy(t, PING_FROMTAG, PING_FROMTAG_LEN); t += PING_FROMTAG_LEN; memcpy(t, CRLF, CRLF_LEN); t += CRLF_LEN; /* to */ memcpy(t, PING_TO, PING_TO_LEN); t += PING_TO_LEN; *t = ' '; t++; memcpy(t, params->to_uri.s, params->to_uri.len); t += params->to_uri.len; memcpy(t, CRLF, CRLF_LEN); t += CRLF_LEN; /* callid */ memcpy(t, PING_CALLID, PING_CALLID_LEN); t += PING_CALLID_LEN; *t = ' '; t++; memcpy(t, callid_str.s, callid_str.len); t += callid_str.len; memcpy(t, CRLF, CRLF_LEN); t += CRLF_LEN; /* cseq */ memcpy(t, PING_CSEQ, PING_CSEQ_LEN); t += PING_CSEQ_LEN; *t = ' '; t++; memcpy(t, params->method.s, params->method.len); t += params->method.len; memcpy(t, CRLF, CRLF_LEN); t += CRLF_LEN; memcpy(t, PING_CLEN, PING_CLEN_LEN); t += PING_CLEN_LEN; memcpy(t, CRLF CRLF, 2*CRLF_LEN); /* t += 2 * CRLF_LEN; */ pkg_free(via.s); *len = size; return msg; error: if (msg != NULL) pkg_free(msg); if (via.s != NULL) pkg_free(via.s); *len = 0; return NULL; } static void natping(unsigned int ticks, void *param) { int rval, n; void *buf, *cp; str c; struct dest_info dst; buf = NULL; if (cblen > 0) { buf = pkg_malloc(cblen); if (buf == NULL) { LOG(L_ERR, "ERROR: nathelper::natping: out of memory\n"); return; } } rval = ul.get_all_ucontacts(buf, cblen, (ping_nated_only ? FL_NAT : 0)); if (rval > 0) { if (buf != NULL) pkg_free(buf); cblen = (cblen + rval) * 2; buf = pkg_malloc(cblen); if (buf == NULL) { LOG(L_ERR, "ERROR: nathelper::natping: out of memory\n"); return; } rval = ul.get_all_ucontacts(buf, cblen, (ping_nated_only ? FL_NAT : 0)); if (rval != 0) { pkg_free(buf); return; } } if (buf == NULL) return; cp = buf; n = 0; for (;;) { memcpy(&(c.len), cp, sizeof(c.len)); if (c.len == 0) break; c.s = (char *)cp + sizeof(c.len); cp = (char *)cp + sizeof(c.len) + c.len; init_dest_info(&dst); memcpy(&dst.send_sock, cp, sizeof(dst.send_sock)); cp += sizeof(dst.send_sock); if ((++n % 50) == 0) usleep(1); natping_contact(c, &dst); } pkg_free(buf); } int natping_contact(str contact, struct dest_info *dst) { struct sip_uri curi; struct hostent *he; str p_method, p_from; char proto; uac_req_t uac_r; struct nat_ping_params pp; char *ping_msg; unsigned int ping_msg_len; if (natping_method != NULL && natping_stateful != 0) { /* XXX: add send_sock handling */ p_method.s = natping_method; p_method.len = strlen(p_method.s); p_from.s = "sip:registrar@127.0.0.1:9"; /* XXX */ p_from.len = strlen(p_from.s); set_uac_req(&uac_r, &p_method, 0, 0, 0, 0, 0, 0); if (tmb.t_request(&uac_r, &contact, &contact, &p_from, 0) == -1) { LOG(L_ERR, "ERROR: nathelper::natping_contact: t_request() failed\n"); return -1; } } else { if (parse_uri(contact.s, contact.len, &curi) < 0) { LOG(L_ERR, "ERROR: nathelper::natping_contact: can't parse contact uri\n"); return -1; } if (curi.port_no == 0) curi.port_no = SIP_PORT; proto = (curi.proto != PROTO_NONE) ? curi.proto : PROTO_UDP; he = sip_resolvehost(&curi.host, &curi.port_no, &proto); if (he == NULL) { LOG(L_ERR, "ERROR: nathelper::natping_contact: can't resolve host\n"); return -1; } hostent2su(&dst->to, he, 0, curi.port_no); if (dst->send_sock == NULL || (dst->send_sock->flags & SI_IS_MCAST)) { dst->send_sock = force_socket ? force_socket : get_send_socket(0, &dst->to, proto); } if (dst->send_sock == NULL) { LOG(L_ERR, "ERROR: nathelper::natping_contact: can't get sending socket\n"); return -1; } dst->proto = proto; if (natping_method != NULL && natping_crlf == 0) { /* Stateless natping using full-blown messages */ pp.method.s = natping_method; pp.method.len = strlen(natping_method); pp.uri = contact; pp.from_uri.s = "sip:registrar@127.0.0.1:9"; /* XXX */ pp.from_uri.len = strlen(pp.from_uri.s); pp.to_uri = contact; pp.send_info = dst; ping_msg = sip_ping_builder(&ping_msg_len, &pp); if (ping_msg != NULL){ msg_send(dst, ping_msg, ping_msg_len); pkg_free(ping_msg); } else { LOG(L_ERR, "ERROR: nathelper::natping_contact: failed to build sip ping message\n"); } } else { /* Stateless natping using dummy packets */ if (proto == PROTO_UDP) udp_send(dst, (char *)sbuf, sizeof(sbuf)); else msg_send(dst, (char *)sbuf, sizeof(sbuf)); } } return 1; } int intercept_ping_reply(struct sip_msg* msg) { if (natping_stateful != 0) return 1; /* via1 is parsed automatically for replies */ if (msg->via1 != NULL && msg->via1->branch != NULL && msg->via1->branch->value.s != NULL && (msg->via1->branch->value.len > PING_BRANCH_PREFIX_LEN) && (memcmp(msg->via1->branch->value.s, PING_BRANCH_PREFIX, PING_BRANCH_PREFIX_LEN) == 0)) { /* Drop reply */ return 0; } return 1; } kamailio-4.0.4/obsolete/nathelper/doc/0000755000000000000000000000000012223032460016340 5ustar rootrootkamailio-4.0.4/obsolete/nathelper/doc/functions.xml0000644000000000000000000001204012223032460021067 0ustar rootroot
Functions
<function>fix_nated_contact()</function> Rewrites Contact HF to contain request's source address:port. <function>fix_nated_contact</function> usage ... if (search("User-Agent: Cisco ATA.*") {fix_nated_contact();}; ...
<function>fix_nated_sdp(mode)</function> Rewrites Contact HF to contain request's source address:port. Meaning of the parameters is as follows: mode - 0x01 (add direction=active), 0x02 (rewrite media IP address with source address of the message). <function>fix_nated_sdp</function> usage ... if (search("User-Agent: Cisco ATA.*") {fix_nated_sdp("3");}; ...
<function>add_rcv_param()</function> Add received parameter to Contact header fields. The parameter will contain URI created from the source IP, port, and protocol of the packet containing the SIP message. The parameter can be then processed by another registrar, this is useful, for example, when replicating register messages using t_replicate function to another registrar. <function>add_rcv_paramer</function> usage ... add_rcv_param(); ...
<function>fix_nated_register()</function> The function creates a URI consisting of the source IP, port, and protocol and stores the URI in an Attribute-Value-Pair. The URI will be appended as "received" parameter to Contact in 200 OK and registrar will store it in the user location database. <function>fix_nated_register</function> usage ... fix_nated_register(); ...
<function>nat_uac_test(mode)</function> Tries to guess if client's request originated behind a nat. The mode parameter determines what heuristics is used. If mode contains: 01 - the Contact URI host contains a private IP address (from RFC1981). 02 - the address in the topmost Via header differs from the source IP address of the request. 04 - the address in the topmost Via contains a private IP address (RFC1918). 08 - the SDP body of the request contains a private IP address (RFC1918). 16 - the port in the topmost Via differs from the source port of the request. 32 - the Contact URI port differs from the source port of the request (Warning: this is might be legal or even intended combination in non natted scenarios). All of them might be bitwise combined (which is equal to the sum of the values from the list above). If one of the test matched the function returns true.
<function>ping_contact(contact)</function> Ping contact specified by parameter. It enables pinging independently on usrloc. It may be processed e.g. via timer module. <function>ping_contact</function> usage ... $c = @get_a_contact; ping_contact($c); ...
<function>@nathelper.rewrite_contact[n]</function> Get n-th Contact value with IP:Port rewritten to source ip:port. N is counted from 1. Only IP:port is rewritten, remaining part are left unchanged. Full nameaddr is supported. <function>@nathelper.rewrite_contact</function> usage ... $c = @nathelper.rewrite_contact[1]; ... $c2 = @nathelper.rewrite_contact[1].nameaddr.uri;
kamailio-4.0.4/obsolete/nathelper/doc/nathelper.xml0000644000000000000000000000315112223032460021044 0ustar rootroot
Maxim Sobolev Sippy Software, Inc.
sobomax@sippysoft.com
2003-2008 Sippy Software, Inc.
Nathelper Module
Overview This is a module to help with NAT traversal. In particular, it helps symmetric UAs that don't advertise they are symmetric and are not able to determine their public address. fix_nated_contact rewrites Contact header field with request's source address:port pair. fix_nated_sdp adds the active direction indication to SDP (flag 0x01) and updates source IP address too (flag 0x02). Known devices that get along over NATs with nathelper are ATAs (as clients) and Cisco Gateways (since 12.2(T)) as servers. See http://www.cisco.com/en/US/products/sw/iosswrel/ps1839/products_feature_guide09186a0080110bf9.html">
kamailio-4.0.4/obsolete/nathelper/doc/Makefile0000644000000000000000000000013212223032460017774 0ustar rootrootdocs = nathelper.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/nathelper/doc/params.xml0000644000000000000000000000412412223032460020346 0ustar rootroot
Parameters
<varname>natping_interval</varname> (integer) Period of time in seconds between sending short UDP packets to all currently registered UAs to keep their NAT bindings alive. Value of 0 disables this functionality. Default value is 0. Set <varname>natping_interval</varname> parameter ... modparam("nathelper", "natping_interval", 10) ...
<varname>ping_nated_only</varname> (integer) If this variable is set then only contacts that have "behind_NAT" flag in user location database set set will get ping. Default value is 0. Set <varname>ping_nated_only</varname> parameter ... modparam("nathelper", "ping_nated_only", 1) ...
<varname>received_avp</varname> (integer) The number of the Attribute-Value-Pair (AVP) used to store the URI containing the received IP, port, and protocol. The URI is created by fix_nated_register function of nathelper module and the attribute is then used by the registrar to store the received parameters. Do not forget to change the value of corresponding parameter in registrar module if you change the value of this parameter. Default value is 42. Set <varname>received_avp</varname> parameter ... modparam("nathelper", "received_avp", 43) ...
kamailio-4.0.4/obsolete/nathelper/README0000644000000000000000000001247512223032460016464 0ustar rootroot1. Nathelper Module Maxim Sobolev Sippy Software, Inc. Copyright © 2003-2008 Sippy Software, Inc. __________________________________________________________________ 1.1. Overview 1.2. Parameters 1.2.1. natping_interval (integer) 1.2.2. ping_nated_only (integer) 1.2.3. received_avp (integer) 1.3. Functions 1.3.1. fix_nated_contact() 1.3.2. fix_nated_sdp(mode) 1.3.3. add_rcv_param() 1.3.4. fix_nated_register() 1.3.5. nat_uac_test(mode) 1.3.6. ping_contact(contact) 1.3.7. @nathelper.rewrite_contact[n] 1.1. Overview This is a module to help with NAT traversal. In particular, it helps symmetric UAs that don't advertise they are symmetric and are not able to determine their public address. fix_nated_contact rewrites Contact header field with request's source address:port pair. fix_nated_sdp adds the active direction indication to SDP (flag 0x01) and updates source IP address too (flag 0x02). Known devices that get along over NATs with nathelper are ATAs (as clients) and Cisco Gateways (since 12.2(T)) as servers. See http://www.cisco.com/en/US/products/sw/iosswrel/ps1839/products_feature _guide09186a0080110bf9.html"> 1.2. Parameters 1.2.1. natping_interval (integer) Period of time in seconds between sending short UDP packets to all currently registered UAs to keep their NAT bindings alive. Value of 0 disables this functionality. Default value is 0. Example 1. Set natping_interval parameter ... modparam("nathelper", "natping_interval", 10) ... 1.2.2. ping_nated_only (integer) If this variable is set then only contacts that have "behind_NAT" flag in user location database set set will get ping. Default value is 0. Example 2. Set ping_nated_only parameter ... modparam("nathelper", "ping_nated_only", 1) ... 1.2.3. received_avp (integer) The number of the Attribute-Value-Pair (AVP) used to store the URI containing the received IP, port, and protocol. The URI is created by fix_nated_register function of nathelper module and the attribute is then used by the registrar to store the received parameters. Do not forget to change the value of corresponding parameter in registrar module if you change the value of this parameter. Default value is 42. Example 3. Set received_avp parameter ... modparam("nathelper", "received_avp", 43) ... 1.3. Functions 1.3.1. fix_nated_contact() Rewrites Contact HF to contain request's source address:port. Example 4. fix_nated_contact usage ... if (search("User-Agent: Cisco ATA.*") {fix_nated_contact();}; ... 1.3.2. fix_nated_sdp(mode) Rewrites Contact HF to contain request's source address:port. Meaning of the parameters is as follows: * mode - 0x01 (add direction=active), 0x02 (rewrite media IP address with source address of the message). Example 5. fix_nated_sdp usage ... if (search("User-Agent: Cisco ATA.*") {fix_nated_sdp("3");}; ... 1.3.3. add_rcv_param() Add received parameter to Contact header fields. The parameter will contain URI created from the source IP, port, and protocol of the packet containing the SIP message. The parameter can be then processed by another registrar, this is useful, for example, when replicating register messages using t_replicate function to another registrar. Example 6. add_rcv_paramer usage ... add_rcv_param(); ... 1.3.4. fix_nated_register() The function creates a URI consisting of the source IP, port, and protocol and stores the URI in an Attribute-Value-Pair. The URI will be appended as "received" parameter to Contact in 200 OK and registrar will store it in the user location database. Example 7. fix_nated_register usage ... fix_nated_register(); ... 1.3.5. nat_uac_test(mode) Tries to guess if client's request originated behind a nat. The mode parameter determines what heuristics is used. If mode contains: * 01 - the Contact URI host contains a private IP address (from RFC1981). * 02 - the address in the topmost Via header differs from the source IP address of the request. * 04 - the address in the topmost Via contains a private IP address (RFC1918). * 08 - the SDP body of the request contains a private IP address (RFC1918). * 16 - the port in the topmost Via differs from the source port of the request. * 32 - the Contact URI port differs from the source port of the request (Warning: this is might be legal or even intended combination in non natted scenarios). All of them might be bitwise combined (which is equal to the sum of the values from the list above). If one of the test matched the function returns true. 1.3.6. ping_contact(contact) Ping contact specified by parameter. It enables pinging independently on usrloc. It may be processed e.g. via timer module. Example 8. ping_contact usage ... $c = @get_a_contact; ping_contact($c); ... 1.3.7. @nathelper.rewrite_contact[n] Get n-th Contact value with IP:Port rewritten to source ip:port. N is counted from 1. Only IP:port is rewritten, remaining part are left unchanged. Full nameaddr is supported. Example 9. @nathelper.rewrite_contact usage ... $c = @nathelper.rewrite_contact[1]; ... $c2 = @nathelper.rewrite_contact[1].nameaddr.uri; kamailio-4.0.4/obsolete/nathelper/nathelper.h0000644000000000000000000000310612223032460017726 0ustar rootroot/* * $Id$ * * Copyright (C) 2005-2008 Sippy Software, Inc., http://www.sippysoft.com * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _NATHELPER_H #define _NATHELPER_H /* Parameters from nathelper.c */ extern struct socket_info* force_socket; /* Functions from natping.c */ int natpinger_init(void); int natpinger_child_init(int); int natpinger_cleanup(void); int natping_contact(str, struct dest_info *); int intercept_ping_reply(struct sip_msg* msg); /* Variables from natping.c referenced from nathelper.c */ extern int natping_interval; extern int ping_nated_only; extern char *natping_method; extern int natping_stateful; extern int natping_crlf; #endif kamailio-4.0.4/obsolete/nathelper/TODO0000644000000000000000000000021412223032460016260 0ustar rootroot- "safety locks" against double use of nathelper use mistakenly flags (script controlled) as opposed to msg_flags (SER controlled); -jiri kamailio-4.0.4/obsolete/nathelper/nhelpr_funcs.h0000644000000000000000000000300412223032460020427 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _NHLPR_FUNCS_H #define _NHLPR_FUNCS_H #include "../../str.h" #include "../../parser/msg_parser.h" #include "../../parser/contact/contact.h" int extract_body(struct sip_msg * , str *); int check_content_type(struct sip_msg * ); void *ser_memmem(const void *, const void *, size_t, size_t); int get_callid(struct sip_msg *, str *); int get_to_tag(struct sip_msg *, str *); int get_from_tag(struct sip_msg *, str *); int get_contact_uri(struct sip_msg *, struct sip_uri *, contact_t **); #endif kamailio-4.0.4/obsolete/nathelper/Makefile0000644000000000000000000000036612223032460017240 0ustar rootroot# $Id$ # # print example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=nathelper.so LIBS= DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/pike/0000755000000000000000000000000012223032460014541 5ustar rootrootkamailio-4.0.4/obsolete/pike/timer.h0000644000000000000000000000314712223032460016037 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * */ #ifndef _PIKE_TIMER_H #define _PIKE_TIMER_H struct list_link { struct list_link *next; struct list_link *prev; }; #define has_timer_set(_ll) \ ((_ll)->prev || (_ll)->next) #define is_list_empty(_head) \ ((_head)->next == (_head)) void append_to_timer(struct list_link *head, struct list_link *ll ); void remove_from_timer(struct list_link *head, struct list_link *ll); void update_in_timer(struct list_link *head, struct list_link *ll); void check_and_split_timer(struct list_link *head, int time, struct list_link *split, unsigned char *mask); #endif kamailio-4.0.4/obsolete/pike/ip_tree.c0000644000000000000000000002406712223032460016345 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2004-11-05: adaptiv init lock (bogdan) */ #include #include #include #include #include #include "../../dprint.h" #include "../../mem/shm_mem.h" #include "ip_tree.h" static struct ip_tree* root = 0; static inline struct ip_node* prv_get_tree_branch(unsigned char b) { return root->entries[b].node; } /* locks a tree branch */ static inline void prv_lock_tree_branch(unsigned char b) { lock_set_get( root->entry_lock_set, root->entries[b].lock_idx); } /* unlocks a tree branch */ static inline void prv_unlock_tree_branch(unsigned char b) { lock_set_release( root->entry_lock_set, root->entries[b].lock_idx); } /* wrapper functions */ struct ip_node* get_tree_branch(unsigned char b) { return prv_get_tree_branch(b); } void lock_tree_branch(unsigned char b) { prv_lock_tree_branch(b); } void unlock_tree_branch(unsigned char b) { prv_unlock_tree_branch(b); } /* size must be a power of 2 */ static gen_lock_set_t* init_lock_set(int *size) { gen_lock_set_t *lset; lset=0; /* kill warnings */ for( ; *size ; *size=((*size)>>1) ) { LOG(L_INFO,"INFO:pike:init_lock_set: probing %d set size\n",*size); /* create a lock set */ lset = lock_set_alloc( *size ); if (lset==0) { LOG(L_INFO,"INFO:pike:init_lock_set: cannot get %d locks\n", *size); continue; } /* init lock set */ if (lock_set_init(lset)==0) { LOG(L_INFO,"INFO:pike:init_lock_set: cannot init %d locks\n", *size); lock_set_dealloc( lset ); lset = 0; continue; } /* alloc and init succesfull */ break; } if (*size==0) { LOG(L_ERR,"ERROR:pike:init_lock_set: cannot get a lock set\n"); return 0; } return lset; } /* Builds and Inits a new IP tree */ int init_ip_tree(unsigned int maximum_hits) { int size; int i; /* create the root */ root = (struct ip_tree*)shm_malloc(sizeof(struct ip_tree)); if (root==0) { LOG(L_ERR,"ERROR:pike:init_ip_tree: shm malloc failed\n"); goto error; } memset( root, 0, sizeof(struct ip_tree)); /* init lock set */ size = MAX_IP_BRANCHES; root->entry_lock_set = init_lock_set( &size ); if (root->entry_lock_set==0) { LOG(L_ERR,"ERROR:pike:init_ip_tree: failed to create locks\n"); goto error; } /* assign to each branch a lock */ for(i=0;ientries[i].node = 0; root->entries[i].lock_idx = i % size; /*DBG("DEBUG:pike:pike_ip_tree: branch %d takes lock index %d\n", i, root->entries[i].lock_idx);*/ } root->max_hits = maximum_hits; return 0; error: if (root) shm_free(root); return -1; } unsigned int get_max_hits() { return root != 0 ? root->max_hits : -1; } /* destroy an ip_node and all nodes under it; the nodes must be first removed * from any other lists/timers */ static inline void destroy_ip_node(struct ip_node *node) { struct ip_node *foo, *bar; foo = node->kids; while (foo){ bar = foo; foo = foo->next; destroy_ip_node(bar); } shm_free(node); } /* destroy and free the IP tree */ void destroy_ip_tree() { int i; if (root==0) return; /* destroy and free the lock set */ if (root->entry_lock_set) { lock_set_destroy(root->entry_lock_set); lock_set_dealloc(root->entry_lock_set); } /* destroy all the nodes */ for(i=0;ientries[i].node) destroy_ip_node(root->entries[i].node); shm_free( root ); root = 0; return; } /* builds a new ip_node corresponding to a byte value */ static inline struct ip_node *new_ip_node(unsigned char byte) { struct ip_node *new_node; new_node = (struct ip_node*)shm_malloc(sizeof(struct ip_node)); if (!new_node) { LOG(L_ERR,"ERROR:pike:new_ip_node: no more shm mem\n"); return 0; } memset( new_node, 0, sizeof(struct ip_node)); new_node->byte = byte; return new_node; } /* splits from the current node (dad) a new child */ struct ip_node *split_node(struct ip_node* dad, unsigned char byte) { struct ip_node *new_node; /* create a new node */ if ( (new_node=new_ip_node(byte))==0 ) return 0; /* the child node inherits a part of his father hits */ if (dad->hits[CURR_POS]>=1) new_node->hits[CURR_POS] = (dad->hits[CURR_POS])-1; if (dad->leaf_hits[CURR_POS]>=1) new_node->leaf_hits[PREV_POS] = (dad->leaf_hits[PREV_POS])-1; /* link the child into father's kids list -> insert it at the beginning, * is much faster */ if (dad->kids) { dad->kids->prev = new_node; new_node->next = dad->kids; } dad->kids = new_node; new_node->branch = dad->branch; new_node->prev = dad; return new_node; } #define is_hot_non_leaf(_node) \ ( (_node)->hits[PREV_POS]>=root->max_hits>>2 ||\ (_node)->hits[CURR_POS]>=root->max_hits>>2 ||\ (((_node)->hits[PREV_POS]+(_node)->hits[CURR_POS])>>1)>=\ root->max_hits>>2 ) #define is_hot_leaf(_node) \ ( (_node)->leaf_hits[PREV_POS]>=root->max_hits ||\ (_node)->leaf_hits[CURR_POS]>=root->max_hits ||\ (((_node)->leaf_hits[PREV_POS]+(_node)->leaf_hits[CURR_POS])>>1)>=\ root->max_hits ) #define is_warm_leaf(_node) \ ( (_node)->hits[CURR_POS]>=root->max_hits>>2 ) #define MAX_TYPE_VAL(_x) \ (( (1U<<(8*sizeof(_x)-1))-1 )|( (1U<<(8*sizeof(_x)-1)) )) char *node_status_array[] = {"", "WARM", "HOT", "ALL"}; node_status_t node_status(struct ip_node *node) { if ( is_hot_leaf(node) ) return NODE_STATUS_HOT; if ( is_warm_leaf(node) ) return NODE_STATUS_WARM; return NODE_STATUS_OK; } /* mark with one more hit the given IP address - */ struct ip_node* mark_node(unsigned char *ip,int ip_len, struct ip_node **father,unsigned char *flag) { struct ip_node *node; struct ip_node *kid; int byte_pos; kid = root->entries[ ip[0] ].node; node = 0; byte_pos = 0; DBG("DEBUG:pike:mark_node: search on branch %d (top=%p)\n", ip[0],kid); /* search into the ip tree the longest prefix matching the given IP */ while (kid && byte_posbyte!=(unsigned char)ip[byte_pos]) { kid = kid->next; } if (kid) { node = kid; kid = kid->kids; byte_pos++; } } DBG("DEBUG:pike:mark_node: Only first %d were matched!\n",byte_pos); *flag = 0; *father = 0; /* what have we found? */ if (byte_pos==ip_len) { /* we found the entire address */ node->flags |= NODE_IPLEAF_FLAG; /* increment it, but be careful not to overflow the value */ if(node->leaf_hits[CURR_POS]leaf_hits[CURR_POS])-1) node->leaf_hits[CURR_POS]++; if ( is_hot_leaf(node) ) *flag |= RED_NODE; } else if (byte_pos==0) { /* we hit an empty branch in the IP tree */ assert(node==0); /* add a new node containing the start byte of the IP address */ if ( (node=new_ip_node(ip[0]))==0) return 0; node->hits[CURR_POS] = 1; node->branch = ip[0]; *flag = NEW_NODE ; /* set this node as root of the branch starting with first byte of IP*/ root->entries[ ip[0] ].node = node; } else{ /* only a non-empty prefix of the IP was found */ if ( node->hits[CURR_POS]hits[CURR_POS])-1 ) node->hits[CURR_POS]++; if ( is_hot_non_leaf(node) ) { /* we have to split the node */ *flag = NEW_NODE ; DBG("DEBUG:pike:mark_node: splitting node %p [%d]\n", node,node->byte); *father = node; node = split_node(node,ip[byte_pos]); } else { /* to reduce memory usage, force to expire non-leaf nodes if they * have just a few hits -> basically, don't update the timer for * them the nr of hits is small */ if ( !is_warm_leaf(node) ) *flag = NO_UPDATE; } } return node; } /* remove and destroy a IP node along with its subtree */ void remove_node(struct ip_node *node) { DBG("DEBUG:pike:remove_node: destroying node %p\n",node); /* is it a branch root node? (these nodes have no prev (father)) */ if (node->prev==0) { assert(root->entries[node->byte].node==node); root->entries[node->byte].node = 0; } else { /* unlink it from kids list */ if (node->prev->kids==node) /* it's the head of the list! */ node->prev->kids = node->next; else /* it's somewhere in the list */ node->prev->next = node->next; if (node->next) node->next->prev = node->prev; } /* destroy the node */ node->next = node->prev = 0; destroy_ip_node(node); } void print_node(struct ip_node *node,int sp, FILE *f) { struct ip_node *foo; /* print current node */ if (!f) { DBG("[l%d] node %p; brh=%d byte=%d flags=%d, hits={%d,%d} , " "leaf_hits={%d,%d]\n", sp, node, node->branch, node->byte, node->flags, node->hits[PREV_POS],node->hits[CURR_POS], node->leaf_hits[PREV_POS],node->leaf_hits[CURR_POS]); } else { fprintf(f,"[l%d] node %p; brh=%d byte=%d flags=%d, hits={%d,%d} , " "leaf_hits={%d,%d]\n", sp, node, node->branch, node->byte, node->flags, node->hits[PREV_POS],node->hits[CURR_POS], node->leaf_hits[PREV_POS],node->leaf_hits[CURR_POS]); } /* print all the kids */ foo = node->kids; while(foo){ print_node(foo,sp+1,f); foo = foo->next; } } void print_tree( FILE *f ) { int i; DBG("DEBUG:pike:print_tree: printing IP tree\n"); for(i=0;i
Functions
<function>pike_check_req()</function> Process the source IP of the current request and returns false if the IP was exceeded the blocking limit. Meaning of the parameters is as follows: <function>pike_check_req</function> usage ... if (!pike_check_req()) { break; }; ...
kamailio-4.0.4/obsolete/pike/doc/pike.xml0000644000000000000000000000204212223032460016756 0ustar rootroot
Bogdan Iancu FhG FOKUS
iancu@fokus.fraunhofer.de
2003 FhG FOKUS
Pike Module
Overview The module keeps trace of all (or selected ones) incoming request's IP source and blocks the ones that exceeded some limit. Works simultaneous for IPv4 and IPv6 addresses.
kamailio-4.0.4/obsolete/pike/doc/rpc.xml0000644000000000000000000000231512223032460016615 0ustar rootroot
RPC calls
<function>pike.top</function> Pike.top behaves like a 'top' command and shows source IP addresses of incoming requestes to pike_check_req() function. The IP list is sorted by sum of leaf hits (prev and curr) descending and in second level by hits. Some IPs could be marked as HOT depending on theirs request rates. pike.top command takes one string parameter which specifies what kind of nodes you are interested in. Possible values are HOT or ALL. If no argument is given, it behaves as HOT was used. Marking nodes HOT is done on server side, client only presents given data and make some postprocessing like sorting. Output of this command is a simple dump of ip_tree nodes marked as ip-leafs.
kamailio-4.0.4/obsolete/pike/doc/devel.xml0000644000000000000000000000511712223032460017133 0ustar rootroot
Developer's Guide One single tree (for both IPv4 and IPv6) is used. Each node contains a byte, the IP addresses stretching from root to the leafs. Tree of IP addresses / 193 - 175 - 132 - 164 tree root / \ 142 \ 195 - 37 - 78 - 163 \ 79 - 134 To detect the whole address, step by step, from the root to the leafs, the nodes corresponding to each byte of the ip address are expanded. In order to be expended a node has to be hit for a given number of times (possible by different addresses; in the previous example, the node "37" was expanded by the 195.37.78.163 and 195.37.79.134 hits). For 193.175.132.164 with x= reqs_density_per_unit: After first req hits -> the "193" node is built. After x more hits, the "175" node is build; the hits of "193" node are split between itself and its child--both of them gone have x/2. And so on for node "132" and "164". Once "164" build the entire address can be found in the tree. "164" becomes a leaf. After it will be hit as a leaf for x times, it will become "RED" (further request from this address will be blocked). So, to build and block this address were needed 3*x hits. Now, if reqs start coming from 193.175.132.142, the first 3 bytes are already in the tree (they are shared with the previous address), so I will need only x hits (to build node "142" and to make it "RED") to make this address also to be blocked. This is the reason for the variable number of hits necessary to block an IP. The maximum number of hits to turn an address red are (n is the address's number of bytes): 1 (first byte) + x (second byte) + (x / 2) * (n - 2) (for the rest of the bytes) + (n - 1) (to turn the node to red). So, for IPv4 (n = 4) will be 3x and for IPv6 (n = 16) will be 9x. The minimum number of hits to turn an address red is x.
kamailio-4.0.4/obsolete/pike/doc/Makefile0000644000000000000000000000012512223032460016744 0ustar rootrootdocs = pike.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/pike/doc/params.xml0000644000000000000000000000415212223032460017315 0ustar rootroot
Parameters
<varname>sampling_time_unit</varname> (integer) Time period used for sampling (or the sampling accuracy ;-) ). The smaller the better, but slower. If you want to detect peeks, use a small one. To limit the access (like total number of requests on a long period of time) to a proxy resource (a gateway for ex), use a bigger value of this parameter. Default value is 2. Set <varname>sampling_time_unit</varname> parameter ... modparam("pike", "sampling_time_unit", 10) ...
<varname>reqs_density_per_unit</varname> (integer) How many requests should be allowed per sampling_time_unit before blocking all the incoming request from that IP. Practically, the blocking limit is between ( let's have x=reqs_density_per_unit) x and 3*x for IPv4 addresses and between x and 8*x for ipv6 addresses. Default value is 30. Set <varname>reqs_density_per_unit</varname> parameter ... modparam("pike", "reqs_density_per_unit", 30) ...
<varname>remove_latency</varname> (integer) For how long the IP address will be kept in memory after the last request from that IP address. It's a sort of timeout value. Default value is 120. Set <varname>remove_latency</varname> parameter ... modparam("pike", "remove_latency", 130) ...
kamailio-4.0.4/obsolete/pike/README0000644000000000000000000001136412223032460015426 0ustar rootroot1. Pike Module Bogdan Iancu FhG FOKUS Copyright © 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. Parameters 1.2.1. sampling_time_unit (integer) 1.2.2. reqs_density_per_unit (integer) 1.2.3. remove_latency (integer) 1.3. Functions 1.3.1. pike_check_req() 1.4. Developer's Guide 1.5. RPC calls 1.5.1. pike.top 1.1. Overview The module keeps trace of all (or selected ones) incoming request's IP source and blocks the ones that exceeded some limit. Works simultaneous for IPv4 and IPv6 addresses. 1.2. Parameters 1.2.1. sampling_time_unit (integer) Time period used for sampling (or the sampling accuracy ;-) ). The smaller the better, but slower. If you want to detect peeks, use a small one. To limit the access (like total number of requests on a long period of time) to a proxy resource (a gateway for ex), use a bigger value of this parameter. Default value is 2. Example 1. Set sampling_time_unit parameter ... modparam("pike", "sampling_time_unit", 10) ... 1.2.2. reqs_density_per_unit (integer) How many requests should be allowed per sampling_time_unit before blocking all the incoming request from that IP. Practically, the blocking limit is between ( let's have x=reqs_density_per_unit) x and 3*x for IPv4 addresses and between x and 8*x for ipv6 addresses. Default value is 30. Example 2. Set reqs_density_per_unit parameter ... modparam("pike", "reqs_density_per_unit", 30) ... 1.2.3. remove_latency (integer) For how long the IP address will be kept in memory after the last request from that IP address. It's a sort of timeout value. Default value is 120. Example 3. Set remove_latency parameter ... modparam("pike", "remove_latency", 130) ... 1.3. Functions 1.3.1. pike_check_req() Process the source IP of the current request and returns false if the IP was exceeded the blocking limit. Meaning of the parameters is as follows: Example 4. pike_check_req usage ... if (!pike_check_req()) { break; }; ... 1.4. Developer's Guide One single tree (for both IPv4 and IPv6) is used. Each node contains a byte, the IP addresses stretching from root to the leafs. Example 5. Tree of IP addresses / 193 - 175 - 132 - 164 tree root / \ 142 \ 195 - 37 - 78 - 163 \ 79 - 134 To detect the whole address, step by step, from the root to the leafs, the nodes corresponding to each byte of the ip address are expanded. In order to be expended a node has to be hit for a given number of times (possible by different addresses; in the previous example, the node "37" was expanded by the 195.37.78.163 and 195.37.79.134 hits). For 193.175.132.164 with x= reqs_density_per_unit: * After first req hits -> the "193" node is built. * After x more hits, the "175" node is build; the hits of "193" node are split between itself and its child--both of them gone have x/2. * And so on for node "132" and "164". * Once "164" build the entire address can be found in the tree. "164" becomes a leaf. After it will be hit as a leaf for x times, it will become "RED" (further request from this address will be blocked). So, to build and block this address were needed 3*x hits. Now, if reqs start coming from 193.175.132.142, the first 3 bytes are already in the tree (they are shared with the previous address), so I will need only x hits (to build node "142" and to make it "RED") to make this address also to be blocked. This is the reason for the variable number of hits necessary to block an IP. The maximum number of hits to turn an address red are (n is the address's number of bytes): 1 (first byte) + x (second byte) + (x / 2) * (n - 2) (for the rest of the bytes) + (n - 1) (to turn the node to red). So, for IPv4 (n = 4) will be 3x and for IPv6 (n = 16) will be 9x. The minimum number of hits to turn an address red is x. 1.5. RPC calls 1.5.1. pike.top Pike.top behaves like a 'top' command and shows source IP addresses of incoming requestes to pike_check_req() function. The IP list is sorted by sum of leaf hits (prev and curr) descending and in second level by hits. Some IPs could be marked as HOT depending on theirs request rates. pike.top command takes one string parameter which specifies what kind of nodes you are interested in. Possible values are HOT or ALL. If no argument is given, it behaves as HOT was used. Marking nodes HOT is done on server side, client only presents given data and make some postprocessing like sorting. Output of this command is a simple dump of ip_tree nodes marked as ip-leafs. kamailio-4.0.4/obsolete/pike/pike_funcs.c0000644000000000000000000002200712223032460017034 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2003-03-11 converted to the new locking interface: locking.h -- * major changes (andrei) * 2005-05-02 flags field added to node stucture -better sync between timer * and worker processes; some races eliminated (bogdan) */ #include #include #include #include //#include #include "../../mem/shm_mem.h" #include "../../locking.h" #include "../../timer.h" #include "../../ip_addr.h" #include "../../resolve.h" #include "ip_tree.h" #include "pike_funcs.h" #include "timer.h" extern gen_lock_t* timer_lock; extern struct list_link* timer; extern int timeout; void print_timer_list(struct list_link *head) { struct list_link *ll; DBG("DEBUG:pike:print_timer_list --->\n"); for ( ll=head->next ; ll!=head; ll=ll->next) { DBG("\t %p [byte=%x](expires=%d)\n", ll, ll2ipnode(ll)->byte, ll2ipnode(ll)->expires); } } int pike_check_req(struct sip_msg *msg, char *foo, char *bar) { struct ip_node *node; struct ip_node *father; unsigned char flags; struct ip_addr* ip; int update_timer; #ifdef _test /* get the ip address from second via */ if (parse_headers(msg, HDR_VIA1_F, 0)!=0 ) return -1; if (msg->via1==0 ) return -1; /* convert from string to ip_addr */ ip = str2ip( &msg->via1->host ); if (ip==0) return -1; #else ip = &(msg->rcv.src_ip); #endif update_timer=(int)(long)foo; /* first lock the proper tree branch and mark the IP with one more hit*/ lock_tree_branch( ip->u.addr[0] ); node = mark_node( ip->u.addr, ip->len, &father, &flags); if (node==0) { /* even if this is an error case, we return true in script to avoid * considering the IP as marked (bogdan) */ return 1; } DBG("DEBUG:pike_check_req: src IP [%s],node=%p; hits=[%d,%d],[%d,%d] " "node_flags=%d func_flags=%d\n", ip_addr2a( ip ), node, node->hits[PREV_POS],node->hits[CURR_POS], node->leaf_hits[PREV_POS],node->leaf_hits[CURR_POS], node->flags, flags); /* update the timer */ lock_get(timer_lock); if ( flags&NEW_NODE ) { /* put this node into the timer list and remove its father only if this has one kid and is not a LEAF_NODE*/ node->expires = get_ticks() + timeout; DBG("DEBUG:pike:pike_check_req: expires: %d, get_ticks: %d, timeout: %d", node->expires, node->expires-timeout, timeout); append_to_timer( timer, &(node->timer_ll) ); node->flags |= NODE_INTIMER_FLAG; if (father) { DBG("DEBUG:pike_check_req: father %p: flags=%d kids->next=%p\n", father,father->flags,father->kids->next); if (!(father->flags&NODE_IPLEAF_FLAG) && !father->kids->next){ /* debug */ assert( has_timer_set(&(father->timer_ll)) && (father->flags&(NODE_EXPIRED_FLAG|NODE_INTIMER_FLAG)) ); /* if the node is maked as expired by timer, let the timer * to finish and remove the node */ if ( !(father->flags&NODE_EXPIRED_FLAG) ) { remove_from_timer( timer, &(father->timer_ll) ); father->flags &= ~NODE_INTIMER_FLAG; } else { father->flags &= ~NODE_EXPIRED_FLAG; } } } } else { /* update the timer -> in timer can be only nodes * as IP-leaf(complete address) or tree-leaf */ if (node->flags&NODE_IPLEAF_FLAG || node->kids==0) { /* tree leafs which are not potential red nodes are not update in * order to make them to expire */ /* debug */ assert( has_timer_set(&(node->timer_ll)) && (node->flags&(NODE_EXPIRED_FLAG|NODE_INTIMER_FLAG)) ); /* if node exprired, ignore the current hit and let is * expire in timer process */ if (!update_timer && !(flags&NO_UPDATE) && !(node->flags&NODE_EXPIRED_FLAG) ) { node->expires = get_ticks() + timeout; update_in_timer( timer, &(node->timer_ll) ); } } else { /* debug */ assert( !has_timer_set(&(node->timer_ll)) && !(node->flags&(NODE_INTIMER_FLAG|NODE_EXPIRED_FLAG)) ); /* debug */ assert( !(node->flags&NODE_IPLEAF_FLAG) && node->kids ); } } /*print_timer_list( timer );*/ /* debug*/ lock_release(timer_lock); unlock_tree_branch( ip->u.addr[0] ); /*print_tree( 0 );*/ /* debug */ if (flags&RED_NODE) { LOG(L_WARN,"DEBUG:pike_check_req: ALARM - TOO MANY HITS on " "%s !!\n", ip_addr2a( ip ) ); return -1; } return 1; } void clean_routine(unsigned int ticks , void *param) { static unsigned char mask[32]; /* 256 positions mask */ struct list_link head; struct list_link *ll; struct ip_node *dad; struct ip_node *node; int i; /* DBG("DEBUG:pike:clean_routine: entering (%d)\n",ticks); */ /* before locking check first if the list is not empty and if can * be at least one element removed */ if ( is_list_empty( timer )) return; /* quick exit */ /* get the expired elements */ lock_get( timer_lock ); /* check again for empty list */ if (is_list_empty(timer) || (ll2ipnode(timer->next)->expires>ticks )){ lock_release( timer_lock ); return; } check_and_split_timer( timer, ticks, &head, mask); /*print_timer_list(timer);*/ /* debug */ lock_release( timer_lock ); /*print_tree( 0 );*/ /*debug*/ /* got something back? */ if ( is_list_empty(&head) ) return; /* process what we got -> don't forget to lock the tree!! */ for(i=0;i skip it */ if ( ((mask[i>>3])&(1<<(i&0x07)))==0 ) continue; lock_tree_branch( i ); for( ll=head.next ; ll!=&head ; ) { node = ll2ipnode( ll ); ll = ll->next; /* skip nodes from a different branch */ if (node->branch!=i) continue; /* unlink the node -> the list will get shorter and it will be * faster for the next branches to process it */ ll->prev->prev->next = ll; ll->prev = ll->prev->prev; node->expires = 0; node->timer_ll.prev = node->timer_ll.next = 0; if ( node->flags&NODE_EXPIRED_FLAG ) node->flags &= ~NODE_EXPIRED_FLAG; else continue; /* process the node */ DBG("DEBUG:pike:clean_routine: clean node %p (kids=%p;" "hits=[%d,%d];leaf=[%d,%d])\n", node,node->kids, node->hits[PREV_POS],node->hits[CURR_POS], node->leaf_hits[PREV_POS],node->leaf_hits[CURR_POS]); /* if it's a node, leaf for an ipv4 address inside an * ipv6 address -> just remove it from timer it will be deleted * only when all its kids will be deleted also */ if (node->kids) { assert( node->flags&NODE_IPLEAF_FLAG ); node->flags &= ~NODE_IPLEAF_FLAG; node->leaf_hits[CURR_POS] = 0; } else { /* if the node has no prev, means its a top branch node -> just * removed and destroy it */ if (node->prev!=0) { /* if this is the last kid, we have to put the father * into timer list */ if (node->prev->kids==node && node->next==0) { /* this is the last kid node */ dad = node->prev; /* put it in the list only if it's not an IP leaf * (in this case, it's already there) */ if ( !(dad->flags&NODE_IPLEAF_FLAG) ) { lock_get(timer_lock); dad->expires = get_ticks() + timeout; assert( !has_timer_set(&(dad->timer_ll)) ); append_to_timer( timer, &(dad->timer_ll)); dad->flags |= NODE_INTIMER_FLAG; lock_release(timer_lock); } else { assert( has_timer_set(&(dad->timer_ll)) ); } } } DBG("DEBUG:pike:clean_routine: rmv node %p[%d] \n", node,node->byte); /* del the node */ remove_node( node); } } /* for all expired elements */ unlock_tree_branch( i ); } /* for all branches */ } void refresh_node( struct ip_node *node) { for( ; node ; node=node->next ) { node->hits[PREV_POS] = node->hits[CURR_POS]; node->hits[CURR_POS] = 0; node->leaf_hits[PREV_POS] = node->leaf_hits[CURR_POS]; node->leaf_hits[CURR_POS] = 0; if (node->kids) refresh_node( node->kids ); } } void swap_routine( unsigned int ticks, void *param) { struct ip_node *node; int i; /* DBG("DEBUG:pike:swap_routine: entering \n"); */ for(i=0;i #include #include // TODO FIXME LCH remove arpa/inet.h after testing #include // IPv6 address is a 16 bytes long #define MAX_DEPTH 16 static unsigned int g_max_hits = 0; static void traverse_subtree( struct ip_node *node, int depth, int options ) { static unsigned char ip_addr[MAX_DEPTH]; struct ip_node *foo; DBG("pike:rpc traverse_subtree, depth: %d, byte: %d", depth, node->byte); assert( depth < MAX_DEPTH ); ip_addr[depth] = node->byte; if ( node->flags & NODE_IPLEAF_FLAG ) { int ns = node_status(node); DBG("pike:traverse_subtree: options: 0x%02x, node status: 0x%02x", options, ns); /* add to the result list if it has requested status */ switch (options) { case NODE_STATUS_HOT: if ( ns & NODE_STATUS_HOT ) pike_top_add_entry(ip_addr, depth+1, node->leaf_hits, node->hits, node->expires - get_ticks(), ns); break; case NODE_STATUS_ALL: pike_top_add_entry(ip_addr, depth+1, node->leaf_hits, node->hits, node->expires - get_ticks(), ns); break; } } else if (! node->kids) { /* TODO non IP leaf of ip_tree - it is possible to report WARM nodes here */ /* if ( options == ns ) pike_top_add_entry(ip_addr, depth+1, node->leaf_hits, node->hits, node->expires - get_ticks(), ns); */ } else { /* not a any kind of leaf - inner node */ DBG("pike:rpc traverse_subtree, not IP leaf, depth: %d, ip: %d.%d.%d.%d hits[%d,%d], expires: %d", depth, ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3], node->hits[0], node->hits[1], node->expires - get_ticks()); } foo = node->kids; while (foo) { traverse_subtree( foo, depth + 1, options ); foo = foo->next; } } static void collect_data(int options) { int i; g_max_hits = get_max_hits(); DBG("pike: collect_data"); // maybe try_lock first and than do the rest? for(i=0;i= buffsize ) { size = rv > 128 ? rv : 128; buff = (char *)realloc(buff, size); if ( buff == 0 ) return (char*)concat_err; buffsize = size; DBG("pike:rpc:concat: new buffer size for %s: %d", first, (int)buffsize); } return buff; } static void pike_top(rpc_t *rpc, void *c) { int i; void *handle; struct TopListItem_t *top_list_root; struct TopListItem_t *ti = 0; char addr_buff[40]; char *ip_addr = 0; char *leaf_hits_prev = 0; char *leaf_hits_curr = 0; char *expires = 0; char *status = 0; size_t ip_addr_size = 0; size_t leaf_hits_prev_size = 0; size_t leaf_hits_curr_size = 0; size_t expires_size = 0; size_t status_size = 0; char *stropts; int options = 0; DBG("pike: top"); /* obtain params */ if (rpc->scan(c, "s", &stropts) <= 0) stropts = "HOT"; DBG("pike:top: string options: '%s'", stropts); if ( strstr(stropts, "ALL") ) { options = NODE_STATUS_ALL; } else if ( strstr(stropts, "HOT") ) { options |= NODE_STATUS_HOT; } else if ( strstr(stropts, "WARM") ) { options |= NODE_STATUS_WARM; } DBG("pike:top: options: 0x%02x\n", options); print_tree( 0 ); collect_data(options); top_list_root = pike_top_get_root(); DBG("pike_top: top_list_root = %p", top_list_root); rpc->add(c, "{", &handle); rpc->struct_add(handle, "d", "max_hits", get_max_hits()); i = 0; // it is passed as number of rows if ( top_list_root == 0 ) { DBG("pike_top: no data"); } else { for( ti = top_list_root, i = 0; ti != 0; ti = ti->next, ++i ) { pike_top_print_addr(ti->ip_addr, ti->addr_len, addr_buff, sizeof(addr_buff)); DBG("pike:top: result[%d]: %s leaf_hits[%d,%d] hits[%d,%d] expires: %d status: 0x%02x", i, addr_buff, ti->leaf_hits[0], ti->leaf_hits[1], ti->hits[0], ti->hits[1], ti->expires, ti->status); rpc->struct_add(handle, "sddds", concat(ip_addr, ip_addr_size, "ip_addr", i), addr_buff, concat(leaf_hits_prev, leaf_hits_prev_size, "leaf_hits_prev", i), ti->leaf_hits[0], concat(leaf_hits_curr, leaf_hits_curr_size, "leaf_hits_curr", i), ti->leaf_hits[1], concat(expires, expires_size, "expires", i), ti->expires, concat(status, status_size, "status", i), node_status_array[ti->status]); } } rpc->struct_add(handle, "d", "number_of_rows", i); /* free buffers */ free(ip_addr); free(leaf_hits_prev); free(leaf_hits_curr); free(expires); free(status); pike_top_list_clear(); rpc->send(c); } /* ----- exported data structure with methods ----- */ // TODO check documentation static const char* pike_top_doc[] = { "pike.top doc.", /* Documentation string */ 0 /* Method signature(s) */ }; /* * RPC Methods exported by this module */ rpc_export_t pike_rpc_methods[] = { {"pike.top", pike_top, pike_top_doc, 0}, {0, 0, 0, 0} }; kamailio-4.0.4/obsolete/pike/timer.c0000644000000000000000000000641112223032460016027 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2005-05-02 flags field added to node stucture -better sync between timer * and worker processes; some races eliminated (bogdan) */ #include #include "../../dprint.h" #include "timer.h" #include "ip_tree.h" inline void append_to_timer(struct list_link *head, struct list_link *new_ll ) { DBG("DEBUG:pike:append_to_timer: %p in %p(%p,%p)\n", new_ll, head,head->prev,head->next); assert( !has_timer_set(new_ll) ); new_ll->prev = head->prev; head->prev->next = new_ll; head->prev = new_ll; new_ll->next = head; } inline void remove_from_timer(struct list_link *head, struct list_link *ll) { DBG("DEBUG:pike:remove_from_timer: %p from %p(%p,%p)\n", ll, head,head->prev,head->next); assert( has_timer_set(ll) ); ll->next->prev = ll->prev; ll->prev->next = ll->next; ll->next = ll->prev = 0; } void update_in_timer(struct list_link *head, struct list_link *ll) { remove_from_timer( head, ll); append_to_timer( head, ll); } /* "head" list MUST not be empty */ void check_and_split_timer(struct list_link *head, int time, struct list_link *split, unsigned char *mask) { struct list_link *ll; struct ip_node *node; unsigned char b; int i; /* reset the mask */ for(i=0;i<32;mask[i++]=0); ll = head->next; while( ll!=head && (node=ll2ipnode(ll))->expires<=time) { DBG("DEBUG:pike:check_and_split_timer: splitting %p(%p,%p)node=%p\n", ll,ll->prev,ll->next, node); /* mark the node as expired and un-mark it as being in timer list */ node->flags |= NODE_EXPIRED_FLAG; node->flags &= ~NODE_INTIMER_FLAG; b = node->branch; ll=ll->next; /*DBG("DEBUG:pike:check_and_split_timer: b=%d; [%d,%d]\n", b,b>>3,1<<(b&0x07));*/ mask[b>>3] |= (1<<(b&0x07)); } if (ll==head->next) { /* nothing to return */ split->next = split->prev = split; } else { /* the detached list begins with current beginning */ split->next = head->next; split->next->prev = split; /* and we mark the end of the split list */ split->prev = ll->prev; split->prev->next = split; /* the shortened list starts from where we suspended */ head->next = ll; ll->prev = head; } DBG("DEBUG:pike:check_and_split_timer: succ. to split (h=%p)(p=%p,n=%p)\n", head,head->prev,head->next); return; } kamailio-4.0.4/obsolete/pike/top.c0000644000000000000000000000446612223032460015521 0ustar rootroot#include "top.h" #include "../../dprint.h" #include #include #include #include #include static struct TopListItem_t *top_list_root = 0; static struct TopListItem_t *top_list_iter = 0; static char buff[128]; struct TopListItem_t *pike_top_get_root() { return top_list_root; } char *pike_top_print_addr( unsigned char *ip, int iplen, char *buff, int buffsize ) { unsigned short *ipv6_ptr = (unsigned short *)ip; memset( buff, 0, sizeof(buff)); DBG("pike:top:print_addr(iplen: %d, buffsize: %d)", iplen, buffsize); if ( iplen == 4 ) { inet_ntop(AF_INET, ip, buff, buffsize); } else if ( iplen == 16 ) { inet_ntop(AF_INET6, ip, buff, buffsize); } else { sprintf( buff, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", htons(ipv6_ptr[0]), htons(ipv6_ptr[1]), htons(ipv6_ptr[2]), htons(ipv6_ptr[3]), htons(ipv6_ptr[4]), htons(ipv6_ptr[5]), htons(ipv6_ptr[6]), htons(ipv6_ptr[7]) ); } return buff; } /* if you do not need global buffer, you can use this simpler call */ static char *print_addr(unsigned char *ip, int iplen) { return pike_top_print_addr(ip, iplen, buff, sizeof(buff)); } int pike_top_add_entry( unsigned char *ip_addr, int addr_len, unsigned int leaf_hits[2], unsigned int hits[2], unsigned int expires, node_status_t status ) { struct TopListItem_t *new_item = (struct TopListItem_t *)malloc(sizeof(struct TopListItem_t)); print_addr(ip_addr, addr_len); DBG("pike_top_add_enrty(ip: %s, leaf_hits[%d,%d], hits[%d,%d]," " expires: %d, status: %d)", buff, leaf_hits[0], leaf_hits[1], hits[0], hits[1], expires, status); assert(new_item != 0); memset( (void *)new_item, 0, sizeof(struct TopListItem_t) ); new_item->status = status; new_item->expires = expires; new_item->hits[0] = hits[0]; new_item->hits[1] = hits[1]; new_item->leaf_hits[0] = leaf_hits[0]; new_item->leaf_hits[1] = leaf_hits[1]; assert( addr_len <= 16 ); new_item->addr_len = addr_len; memcpy(new_item->ip_addr, ip_addr, addr_len); new_item->next = top_list_root; top_list_root = new_item; return 1; } void pike_top_list_clear() { struct TopListItem_t *ptr; top_list_iter = top_list_root; while (top_list_iter) { ptr = top_list_iter->next; free(top_list_iter); top_list_iter = ptr; } top_list_root = 0; memset(buff, 0, sizeof(buff)); } kamailio-4.0.4/obsolete/pike/ip_tree.h0000644000000000000000000000576612223032460016357 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2004-07-28 s/lock_set_t/gen_lock_set_t/ because of a type conflict * on darwin (andrei) * 2004-11-05 adaptiv init lock (bogdan) * 2005-06-02 flags added to ip_node structure (bogdan) */ #ifndef _IP_TREE_H #define _IP_TREE_H #include #include "../../locking.h" #include "timer.h" #define NEW_NODE (1<<0) #define RED_NODE (1<<1) #define NO_UPDATE (1<<2) #define MAX_IP_BRANCHES 256 #define PREV_POS 0 #define CURR_POS 1 #define NODE_EXPIRED_FLAG (1<<0) #define NODE_INTIMER_FLAG (1<<1) /** * Node marked as IPLEAF is final node for IP address. Does not matter if IPv4 or IPv6. * This node could not be leaf of tree, it is a leaf node of complete IP address. */ #define NODE_IPLEAF_FLAG (1<<2) struct ip_node { unsigned int expires; unsigned int leaf_hits[2]; unsigned int hits[2]; unsigned char byte; unsigned char branch; volatile unsigned short flags; struct list_link timer_ll; struct ip_node *prev; struct ip_node *next; struct ip_node *kids; }; typedef enum { NODE_STATUS_OK = 0, NODE_STATUS_WARM = 1, NODE_STATUS_HOT = 2, NODE_STATUS_ALL = 3 /** used for status matching */ } node_status_t; node_status_t node_status(struct ip_node *node); extern char *node_status_array[]; struct ip_tree { struct entry { struct ip_node *node; int lock_idx; } entries[MAX_IP_BRANCHES]; unsigned int max_hits; gen_lock_set_t *entry_lock_set; }; #define ll2ipnode(ptr) \ ((struct ip_node*)((char *)(ptr)-\ (unsigned long)(&((struct ip_node*)0)->timer_ll))) int init_ip_tree(unsigned int); void destroy_ip_tree(); unsigned int get_max_hits(); struct ip_node* mark_node( unsigned char *ip, int ip_len, struct ip_node **father, unsigned char *flag); void remove_node(struct ip_node *node); void print_tree( FILE *f); void lock_tree_branch(unsigned char b); void unlock_tree_branch(unsigned char b); struct ip_node* get_tree_branch(unsigned char b); #endif kamailio-4.0.4/obsolete/pike/top.h0000644000000000000000000000134012223032460015512 0ustar rootroot#ifndef __PIKE_TOP_H #define __PIKE_TOP_H #include "ip_tree.h" #include "../../ip_addr.h" struct TopListItem_t { int addr_len; unsigned char ip_addr[16]; unsigned short leaf_hits[2]; unsigned short hits[2]; unsigned int expires; /* in seconds */ node_status_t status; struct TopListItem_t *next; }; // returns 1 when OK and 0 when failed int pike_top_add_entry( unsigned char *ip_addr, int iplen, unsigned int leaf_hits[2], unsigned int hits[2], unsigned int expires, node_status_t status ); struct TopListItem_t *pike_top_get_root(); void pike_top_list_clear(); /* helpful functions */ char *pike_top_print_addr( unsigned char *ip_addr, int addrlen, char *buff, int buffsize ); #endif // PIKE_TOP_H kamailio-4.0.4/obsolete/pike/pike.c0000644000000000000000000001054212223032460015637 0ustar rootroot/* * $Id$ * * PIKE module * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2003-03-11 updated to the new module exports interface (andrei) * 2003-03-11 converted to the new locking interface: locking.h -- * major changes (andrei) * 2003-03-16 flags export parameter added (janakj) */ #include #include #include #include #include #include "../../sr_module.h" #include "../../error.h" #include "../../dprint.h" #include "../../ut.h" #include "../../mem/shm_mem.h" #include "../../timer.h" #include "../../locking.h" #include "ip_tree.h" #include "timer.h" #include "pike_funcs.h" #include "rpc.h" MODULE_VERSION static int pike_init(void); static int pike_exit(void); /* parameters */ static unsigned int time_unit = 2; static unsigned int max_reqs = 30; unsigned int timeout = 120; /* global variables */ gen_lock_t* timer_lock=0; struct list_link* timer = 0; static int fixup_str2int( void** param, int param_no) { unsigned long go_to; int err; if (param_no == 1) { go_to = str2s(*param, strlen(*param), &err); if (err == 0) { pkg_free(*param); *param = (void *)go_to; return 0; } else { LOG(L_ERR, "ERROR: fixup_str2int: bad number <%s>\n", (char *)(*param)); return E_CFG; } } return 0; } static int pike_check_req_0(struct sip_msg* msg, char* foo, char* bar) { return pike_check_req(msg, (char*)(long)0,bar); } static cmd_export_t cmds[]={ {"pike_check_req", pike_check_req_0, 0, 0, REQUEST_ROUTE}, {"pike_check_req", pike_check_req, 1, fixup_str2int, REQUEST_ROUTE}, {0,0,0,0,0} }; static param_export_t params[]={ {"sampling_time_unit", PARAM_INT, &time_unit}, {"reqs_density_per_unit", PARAM_INT, &max_reqs}, {"remove_latency", PARAM_INT, &timeout}, {0,0,0} }; struct module_exports exports= { "pike", cmds, pike_rpc_methods, /* RPC methods */ params, pike_init, /* module initialization function */ (response_function) 0, (destroy_function) pike_exit, /* module exit function */ 0, 0 /* per-child init function */ }; static int pike_init(void) { LOG(L_INFO,"PIKE - initializing\n"); /* alloc the timer lock */ timer_lock=lock_alloc(); if (timer_lock==0) { LOG(L_ERR,"ERROR:pike_init: alloc locks failed!\n"); goto error1; } /* init the lock */ if (lock_init(timer_lock)==0){ LOG(L_ERR, "ERROR:pike_init: init lock failed\n"); goto error1; } /* init the IP tree */ if ( init_ip_tree(max_reqs)!=0 ) { LOG(L_ERR,"ERROR:pike_init: ip_tree creation failed!\n"); goto error2; } /* init timer list */ timer = (struct list_link*)shm_malloc(sizeof(struct list_link)); if (timer==0) { LOG(L_ERR,"ERROR:pike_init: cannot alloc shm mem for timer!\n"); goto error3; } timer->next = timer->prev = timer; /* registering timing functions */ register_timer( clean_routine , 0, 1 ); register_timer( swap_routine , 0, time_unit ); return 0; error3: destroy_ip_tree(); error2: lock_destroy(timer_lock); error1: if (timer_lock) lock_dealloc(timer_lock); timer_lock = 0; return -1; } static int pike_exit(void) { LOG(L_INFO,"PIKE - destroying module\n"); /* destroy semaphore */ if (timer_lock) { lock_destroy(timer_lock); lock_dealloc(timer_lock); } /* empty the timer list head */ if (timer) { shm_free(timer); timer = 0; } /* destroy the IP tree */ destroy_ip_tree(); return 0; } kamailio-4.0.4/obsolete/pike/Makefile0000644000000000000000000000035312223032460016202 0ustar rootroot# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=pike.so LIBS= DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/textops/0000755000000000000000000000000012223032460015317 5ustar rootrootkamailio-4.0.4/obsolete/textops/textops.c0000644000000000000000000016534112223032460017203 0ustar rootroot/*$Id$ * * Example ser module, it implements the following commands: * search_append("key", "txt") - insert a "txt" after "key" * replace("txt1", "txt2") - replaces txt1 with txt2 (txt1 can be a re) * search("txt") - searches for txt (txt can be a regular expression) * append_to_reply("txt") - appends txt to the reply? * append_hf("P-foo: bar\r\n"); * * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * ------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-29: - rewriting actions (replace, search_append) now begin * at the second line -- previously, they could affect * first line too, which resulted in wrong calculation of * forwarded requests and an error consequently * - replace_all introduced * 2003-01-28 scratchpad removed (jiri) * 2003-01-18 append_urihf introduced (jiri) * 2003-03-10 module export interface updated to the new format (andrei) * 2003-03-16 flags export parameter added (janakj) * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-04-97 actions permitted to be used from failure/reply routes (jiri) * 2003-04-21 remove_hf and is_present_hf introduced (jiri) * 2003-08-19 subst added (support for sed like res:s/re/repl/flags) (andrei) * 2003-08-20 subst_uri added (like above for uris) (andrei) * 2003-09-11 updated to new build_lump_rpl() interface (bogdan) * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) * 2004-05-09: append_time introduced (jiri) * 2004-07-06 subst_user added (like subst_uri but only for user) (sobomax) * 2004-11-12 subst_user changes (old serdev mails) (andrei) * 2006-02-23 xl_lib formating, multi-value support (tma) * 2006-08-30 added static buffer support (tma) */ #include "../../comp_defs.h" #include "../../action.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../re.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_hname2.h" #include "../../onsend.h" #include "../../ut.h" #include "../../select.h" #include "../../modules/xprint/xp_lib.h" #include "../../script_cb.h" #include "../../select_buf.h" #include "../../ser_time.h" #include "../../dset.h" #include #include #include #include /* for regex */ #include #include #include MODULE_VERSION /* RFC822-conforming dates format: %a -- abbreviated week of day name (locale), %d day of month as decimal number, %b abbreviated month name (locale), %Y year with century, %T time in 24h notation */ #define TIME_FORMAT "Date: %a, %d %b %Y %H:%M:%S GMT" #define MAX_TIME 64 static int xlbuf_size = 4096; static int search_f(struct sip_msg*, char*, char*); static int replace_f(struct sip_msg*, char*, char*); static int subst_f(struct sip_msg*, char*, char*); static int subst_uri_f(struct sip_msg*, char*, char*); static int subst_user_f(struct sip_msg*, char*, char*); static int remove_hf_f(struct sip_msg* msg, char* str_hf, char* foo); static int remove_hf_re_f(struct sip_msg* msg, char* str_hf, char* foo); static int is_present_hf_f(struct sip_msg* msg, char* str_hf, char* foo); static int replace_all_f(struct sip_msg* msg, char* key, char* str); static int search_append_f(struct sip_msg*, char*, char*); static int append_to_reply_f(struct sip_msg* msg, char* key, char* str); static int append_hf(struct sip_msg* msg, char* str1, char* str2); static int append_urihf(struct sip_msg* msg, char* str1, char* str2); static int append_time_f(struct sip_msg* msg, char* , char *); static int incexc_hf_value_f(struct sip_msg* msg, char* , char *); static int include_hf_value_fixup(void**, int); static int exclude_hf_value_fixup(void**, int); static int hf_value_exists_fixup(void**, int); static int insupddel_hf_value_f(struct sip_msg* msg, char* _hname, char* _val); static int append_hf_value_fixup(void** param, int param_no); static int insert_hf_value_fixup(void** param, int param_no); static int remove_hf_value_fixup(void** param, int param_no); static int assign_hf_value_fixup(void** param, int param_no); static int remove_hf_value2_fixup(void** param, int param_no); static int assign_hf_value2_fixup(void** param, int param_no); static int fixup_xlstr(void** param, int param_no); static int fixup_regex_xlstr(void** param, int param_no); static int fixup_substre(void**, int); extern select_row_t sel_declaration[]; static int mod_init(void); static cmd_export_t cmds[]={ {"search", search_f, 1, fixup_regex_1, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|ONSEND_ROUTE}, {"search_append", search_append_f, 2, fixup_regex_xlstr, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE}, {"replace", replace_f, 2, fixup_regex_xlstr, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE}, {"replace_all", replace_all_f, 2, fixup_regex_xlstr, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE}, {"append_to_reply", append_to_reply_f, 1, fixup_xlstr, REQUEST_ROUTE}, {"append_hf", append_hf, 1, fixup_xlstr, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE | BRANCH_ROUTE}, {"append_urihf", append_urihf, 2, fixup_xlstr, REQUEST_ROUTE|FAILURE_ROUTE}, /* obsolete: use remove_hf_value(), does not support compact headers */ {"remove_hf", remove_hf_f, 1, fixup_var_str_1, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE}, {"remove_hf_re", remove_hf_re_f, 1, fixup_regex_12, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE}, /* obsolete: use @msg.HFNAME, , does not support compact headers */ {"is_present_hf", is_present_hf_f, 1, fixup_var_str_1, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE}, {"subst", subst_f, 1, fixup_substre, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE}, {"subst_uri", subst_uri_f, 1, fixup_substre, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE}, {"subst_user", subst_user_f, 1, fixup_substre, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE}, {"append_time", append_time_f, 0, 0, REQUEST_ROUTE }, {"append_hf_value", insupddel_hf_value_f, 2, append_hf_value_fixup, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"insert_hf_value", insupddel_hf_value_f, 2, insert_hf_value_fixup, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"remove_hf_value", insupddel_hf_value_f, 1, remove_hf_value_fixup, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"assign_hf_value", insupddel_hf_value_f, 2, assign_hf_value_fixup, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"remove_hf_value2", insupddel_hf_value_f, 1, remove_hf_value2_fixup, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"assign_hf_value2", insupddel_hf_value_f, 2, assign_hf_value2_fixup, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"include_hf_value", incexc_hf_value_f, 2, include_hf_value_fixup, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"exclude_hf_value", incexc_hf_value_f, 2, exclude_hf_value_fixup, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"hf_value_exists", incexc_hf_value_f, 2, hf_value_exists_fixup, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {0,0,0,0,0} }; static param_export_t params[]={ {"xlbuf_size", PARAM_INT, &xlbuf_size}, {0,0,0} }; /* no params */ struct module_exports exports= { "textops", cmds, 0, /* RPC methods */ params, mod_init, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0, /* on_cancel function */ 0, /* per-child init function */ }; static int mod_init(void) { DBG("%s - initializing\n", exports.name); register_select_table(sel_declaration); return 0; } struct xlstr { str s; xl_elog_t* xlfmt; }; static xl_print_log_f* xl_print = NULL; static xl_parse_format_f* xl_parse = NULL; #define NO_SCRIPT -1 static int fixup_xlstr(void** param, int param_no) { struct xlstr* s; s = pkg_malloc(sizeof(*s)); if (!s) return E_OUT_OF_MEM; s->s.s = *param; s->s.len = strlen(s->s.s); s->xlfmt = 0; if (strchr(s->s.s, '%')) { if (!xl_print) { xl_print=(xl_print_log_f*)find_export("xprint", NO_SCRIPT, 0); if (!xl_print) { LOG(L_CRIT,"ERROR: textops: cannot find \"xprint\", is module xprint loaded?\n"); return E_UNSPEC; } } if (!xl_parse) { xl_parse=(xl_parse_format_f*)find_export("xparse", NO_SCRIPT, 0); if (!xl_parse) { LOG(L_CRIT,"ERROR: textops: cannot find \"xparse\", is module xprint loaded?\n"); return E_UNSPEC; } } if(xl_parse(s->s.s, &s->xlfmt) < 0) { LOG(L_ERR, "ERROR: textops: wrong format '%s'\n", s->s.s); return E_UNSPEC; } } *param = s; return 0; } static int eval_xlstr(struct sip_msg* msg, struct xlstr* val, str* s) { static char *xlbuf = NULL; if (val) { if (val->xlfmt) { if (!xlbuf) { xlbuf = pkg_malloc(xlbuf_size); if (!xlbuf) { LOG(L_ERR, "ERROR: out of memory\n"); return E_OUT_OF_MEM; } } s->len = xlbuf_size-1; if (xl_print(msg, val->xlfmt, xlbuf, &s->len) < 0) { LOG(L_ERR, "ERROR: textops: eval_xlstr: Error while formating result '%.*s'\n", val->s.len, val->s.s); s->len = 0; return E_UNSPEC; } s->s = xlbuf; } else { *s = val->s; } } else s->len = 0; return 1; } static int fixup_regex_xlstr(void** param, int param_no) { if (param_no == 1) return fixup_regex_1(param, param_no); else if (param_no == 2) return fixup_xlstr(param, param_no); else return 0; } static char *get_header(struct sip_msg *msg) { return SIP_MSG_START(msg)+msg->first_line.len; } static int search_f(struct sip_msg* msg, char* key, char* str2) { /*we registered only 1 param, so we ignore str2*/ regmatch_t pmatch; char* buf; struct onsend_info* snd_inf; if ((snd_inf=get_onsend_info())!=0) buf=snd_inf->buf; else buf=msg->buf; if (regexec(((fparam_t*)key)->v.regex, buf, 1, &pmatch, 0)!=0) return -1; return 1; } static int search_append_f(struct sip_msg* msg, char* key, char* _str) { struct lump* l; regmatch_t pmatch; str str; char* s; char *begin; int off; begin=get_header(msg); /* msg->orig/buf previously .. uri problems */ off=begin-msg->buf; if (regexec(((fparam_t*)key)->v.regex, begin, 1, &pmatch, 0)!=0) return -1; if (pmatch.rm_so!=-1){ if (eval_xlstr(msg, (void*) _str, &str) < 0) return -1; if ((l=anchor_lump(msg, off+pmatch.rm_eo, 0, 0))==0) return -1; s=pkg_malloc(str.len); if (s==0){ LOG(L_ERR, "ERROR: search_append_f: mem. allocation failure\n"); return -1; } memcpy(s, str.s, str.len); if (insert_new_lump_after(l, s, str.len, 0)==0){ LOG(L_ERR, "ERROR: could not insert new lump\n"); pkg_free(s); return -1; } return 1; } return -1; } static int replace_all_f(struct sip_msg* msg, char* key, char* _str) { struct lump* l; regmatch_t pmatch; char* s; str str; char* begin; int off; int ret; int eflags; /* we need to be sure we have seen all HFs */ parse_headers(msg, HDR_EOH_F, 0); begin=get_header(msg); /* msg->orig previously .. uri problems */ ret=-1; /* pessimist: we will not find any */ eflags=0; /* match ^ at the beginning of the string*/ if (eval_xlstr(msg, (void*) _str, &str) < 0) return -1; while (beginbuf+msg->len && regexec(((fparam_t*)key)->v.regex, begin, 1, &pmatch, eflags)==0) { off=begin-msg->buf; /* change eflags, not to match any more at string start */ eflags|=REG_NOTBOL; if (pmatch.rm_so==-1){ LOG(L_ERR, "ERROR: replace_all_f: offset unknown\n"); return -1; } if ((l=del_lump(msg, pmatch.rm_so+off, pmatch.rm_eo-pmatch.rm_so, 0))==0) { LOG(L_ERR, "ERROR: replace_all_f: del_lump failed\n"); return -1; } s=pkg_malloc(str.len); if (s==0){ LOG(L_ERR, "ERROR: replace_all_f: mem. allocation failure\n"); return -1; } memcpy(s, str.s, str.len); if (insert_new_lump_after(l, s, str.len, 0)==0){ LOG(L_ERR, "ERROR: could not insert new lump\n"); pkg_free(s); return -1; } /* new cycle */ begin=begin+pmatch.rm_eo; ret=1; } /* while found ... */ return ret; } static int replace_f(struct sip_msg* msg, char* key, char* _str) { struct lump* l; regmatch_t pmatch; char* s; str str; char* begin; int off; /* we need to be sure we have seen all HFs */ parse_headers(msg, HDR_EOH_F, 0); begin=get_header(msg); /* msg->orig previously .. uri problems */ if (regexec(((fparam_t*)key)->v.regex, begin, 1, &pmatch, 0)!=0) return -1; off=begin-msg->buf; if (pmatch.rm_so!=-1){ if (eval_xlstr(msg, (void*) _str, &str) < 0) return -1; if ((l=del_lump(msg, pmatch.rm_so+off, pmatch.rm_eo-pmatch.rm_so, 0))==0) return -1; s=pkg_malloc(str.len); if (s==0){ LOG(L_ERR, "ERROR: replace_f: mem. allocation failure\n"); return -1; } memcpy(s, str.s, str.len); if (insert_new_lump_after(l, s, str.len, 0)==0){ LOG(L_ERR, "ERROR: could not insert new lump\n"); pkg_free(s); return -1; } return 1; } return -1; } /* sed-perl style re: s/regular expression/replacement/flags */ static int subst_f(struct sip_msg* msg, char* subst, char* ignored) { struct lump* l; struct replace_lst* lst; struct replace_lst* rpl; char* begin; struct subst_expr* se; int off; int ret; int nmatches; se=(struct subst_expr*)subst; begin=get_header(msg); /* start after first line to avoid replacing the uri */ off=begin-msg->buf; ret=-1; if ((lst=subst_run(se, begin, msg, &nmatches))==0) goto error; /* not found */ for (rpl=lst; rpl; rpl=rpl->next){ DBG(" %s: subst_f: replacing at offset %d [%.*s] with [%.*s]\n", exports.name, rpl->offset+off, rpl->size, rpl->offset+off+msg->buf, rpl->rpl.len, rpl->rpl.s); if ((l=del_lump(msg, rpl->offset+off, rpl->size, 0))==0) goto error; /* hack to avoid re-copying rpl, possible because both * replace_lst & lumps use pkg_malloc */ if (insert_new_lump_after(l, rpl->rpl.s, rpl->rpl.len, 0)==0){ LOG(L_ERR, "ERROR: %s: subst_f: could not insert new lump\n", exports.name); goto error; } /* hack continued: set rpl.s to 0 so that replace_lst_free will * not free it */ rpl->rpl.s=0; rpl->rpl.len=0; } ret=1; error: DBG("subst_f: lst was %p\n", lst); if (lst) replace_lst_free(lst); if (nmatches<0) LOG(L_ERR, "ERROR: %s: subst_run failed\n", exports.name); return ret; } /* sed-perl style re: s/regular expression/replacement/flags, like * subst but works on the message uri */ static int subst_uri_f(struct sip_msg* msg, char* subst, char* ignored) { char* tmp; int len; char c; struct subst_expr* se; str* result; se=(struct subst_expr*)subst; if (msg->new_uri.s){ len=msg->new_uri.len; tmp=msg->new_uri.s; }else{ tmp=msg->first_line.u.request.uri.s; len =msg->first_line.u.request.uri.len; }; /* ugly hack: 0 s[len], and restore it afterward * (our re functions require 0 term strings), we can do this * because we always alloc len+1 (new_uri) and for first_line, the * message will always be > uri.len */ c=tmp[len]; tmp[len]=0; result=subst_str(tmp, msg, se, 0); /* pkg malloc'ed result */ tmp[len]=c; if (result){ DBG("%s: subst_uri_f: match - old uri= [%.*s], new uri= [%.*s]\n", exports.name, len, tmp, (result->len)?result->len:0,(result->s)?result->s:""); if (msg->new_uri.s) pkg_free(msg->new_uri.s); msg->new_uri=*result; msg->parsed_uri_ok=0; /* reset "use cached parsed uri" flag */ ruri_mark_new(); pkg_free(result); /* free str* pointer */ return 1; /* success */ } return -1; /* false, no subst. made */ } /* sed-perl style re: s/regular expression/replacement/flags, like * subst but works on the user part of the uri */ static int subst_user_f(struct sip_msg* msg, char* subst, char* ignored) { int rval; str* result; struct subst_expr* se; struct action act; str user; char c; int nmatches; struct run_act_ctx ra_ctx; c=0; if (parse_sip_msg_uri(msg)<0){ return -1; /* error, bad uri */ } if (msg->parsed_uri.user.s==0){ /* no user in uri */ user.s=""; user.len=0; }else{ user=msg->parsed_uri.user; c=user.s[user.len]; user.s[user.len]=0; } se=(struct subst_expr*)subst; result=subst_str(user.s, msg, se, &nmatches);/* pkg malloc'ed result */ if (c) user.s[user.len]=c; if (result == NULL) { if (nmatches<0) LOG(L_ERR, "subst_user(): subst_str() failed\n"); return -1; } /* result->s[result->len] = '\0'; --subst_str returns 0-term strings */ memset(&act, 0, sizeof(act)); /* be on the safe side */ act.type = SET_USER_T; act.val[0].type = STRING_ST; act.val[0].u.string = result->s; init_run_actions_ctx(&ra_ctx); rval = do_action(&ra_ctx, &act, msg); if (result->s) pkg_free(result->s); /* SET_USER_T doesn't consume s */ pkg_free(result); return rval; } static inline int remove_hf(struct sip_msg* msg, char* p1, int by_re) { struct hdr_field *hf; struct lump* l; int cnt; str hfn; regex_t regexp; regmatch_t matches; char bkup; int no_match; if (by_re) { if (get_regex_fparam(®exp, msg, (fparam_t*)p1) < 0) { ERR("remove_hf: Error while obtaining parameter value\n"); return -1; } } else { if (get_str_fparam(&hfn, msg, (fparam_t*)p1) < 0) { ERR("remove_hf: Error while obtaining parameter value\n"); return -1; } } cnt=0; /* we need to be sure we have seen all HFs */ parse_headers(msg, HDR_EOH_F, 0); for (hf=msg->headers; hf; hf=hf->next) { if (by_re) { /* 0-term. it for regexp run; should always be a safe op. */ bkup = hf->name.s[hf->name.len]; hf->name.s[hf->name.len] = 0; no_match = regexec(®exp, hf->name.s, /*# matches */1, &matches, /*flags*/0); hf->name.s[hf->name.len] = bkup; if (no_match) continue; } else { if (hf->name.len!=hfn.len) continue; if (strncasecmp(hf->name.s, hfn.s, hf->name.len)!=0) continue; } l=del_lump(msg, hf->name.s-msg->buf, hf->len, 0); if (l==0) { ERR("no memory\n"); return -1; } cnt++; } return cnt==0 ? -1 : 1; } static int remove_hf_f(struct sip_msg* msg, char* p1, char* foo) { return remove_hf(msg, p1, /*use string comparison*/0); } static int remove_hf_re_f(struct sip_msg* msg, char* p1, char* foo) { return remove_hf(msg, p1, /*use regexp comparison*/1); } static int is_present_hf_f(struct sip_msg* msg, char* p1, char* foo) { struct hdr_field *hf; str hfn; if (get_str_fparam(&hfn, msg, (fparam_t*)p1) < 0) { ERR("is_present_hf: Error while obtaining parameter value\n"); return -1; } /* we need to be sure we have seen all HFs */ parse_headers(msg, HDR_EOH_F, 0); for (hf=msg->headers; hf; hf=hf->next) { if (hf->name.len!=hfn.len) continue; if (strncasecmp(hf->name.s, hfn.s, hf->name.len)!=0) continue; return 1; } return -1; } static int fixup_substre(void** param, int param_no) { struct subst_expr* se; str subst; DBG("%s module -- fixing %s\n", exports.name, (char*)(*param)); if (param_no!=1) return 0; subst.s=*param; subst.len=strlen(*param); se=subst_parser(&subst); if (se==0){ LOG(L_ERR, "ERROR: %s: bad subst. re %s\n", exports.name, (char*)*param); return E_BAD_RE; } /* free string */ pkg_free(*param); /* replace it with the compiled subst. re */ *param=se; return 0; } static int append_time_f(struct sip_msg* msg, char* p1, char *p2) { size_t len; char time_str[MAX_TIME]; time_t now; struct tm *bd_time; now=ser_time(0); bd_time=gmtime(&now); if (bd_time==NULL) { LOG(L_ERR, "ERROR: append_time: gmtime failed\n"); return -1; } len=strftime(time_str, MAX_TIME, TIME_FORMAT, bd_time); if (len>MAX_TIME-2 || len==0) { LOG(L_ERR, "ERROR: append_time: unexpected time length\n"); return -1; } time_str[len]='\r'; time_str[len+1]='\n'; if (add_lump_rpl(msg, time_str, len+2, LUMP_RPL_HDR)==0) { LOG(L_ERR, "ERROR: append_time: unable to add lump\n"); return -1; } return 1; } static int append_to_reply_f(struct sip_msg* msg, char* _str, char* dummy) { str str; if (eval_xlstr(msg, (void*) _str, &str) < 0) return -1; if ( add_lump_rpl( msg, str.s, str.len, LUMP_RPL_HDR)==0 ) { LOG(L_ERR,"ERROR:append_to_reply : unable to add lump_rl\n"); return -1; } return 1; } /* add str1 to end of header or str1.r-uri.str2 */ static int append_hf_helper(struct sip_msg* msg, struct xlstr *_str1, struct xlstr *_str2) { struct lump* anchor; char *s; str str1, str2; int len; if (eval_xlstr(msg, _str1, &str1) < 0) return -1; if (_str2) { if (eval_xlstr(msg, _str2, &str2) < 0) return -1; } if (parse_headers(msg, HDR_EOH_F, 0) == -1) { LOG(L_ERR, "append_hf(): Error while parsing message\n"); return -1; } anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0); if (anchor == 0) { LOG(L_ERR, "append_hf(): Can't get anchor\n"); return -1; } len=str1.len; if (_str2) len+= str2.len + REQ_LINE(msg).uri.len; s = (char*)pkg_malloc(len); if (!s) { LOG(L_ERR, "append_hf(): No memory left\n"); return -1; } memcpy(s, str1.s, str1.len); if (_str2) { memcpy(s+str1.len, REQ_LINE(msg).uri.s, REQ_LINE(msg).uri.len); memcpy(s+str1.len+REQ_LINE(msg).uri.len, str2.s, str2.len ); } if (insert_new_lump_before(anchor, s, len, 0) == 0) { LOG(L_ERR, "append_hf(): Can't insert lump\n"); pkg_free(s); return -1; } return 1; } static int append_hf(struct sip_msg *msg, char *str1, char *str2 ) { return append_hf_helper(msg, (struct xlstr *) str1, (struct xlstr *) 0); } static int append_urihf(struct sip_msg *msg, char *str1, char *str2 ) { return append_hf_helper(msg, (struct xlstr *) str1, (struct xlstr *) str2); } #define HNF_ALL 0x01 #define HNF_IDX 0x02 #define MAX_HF_VALUE_STACK 10 enum {hnoInsert, hnoAppend, hnoAssign, hnoRemove, hnoInclude, hnoExclude, hnoIsIncluded, hnoGetValue, hnoGetValueUri, hnoGetValueName, hnoRemove2, hnoAssign2, hnoGetValue2}; struct hname_data { int oper; int htype; str hname; int flags; int idx; str param; }; #define is_space(_p) ((_p) == '\t' || (_p) == '\n' || (_p) == '\r' || (_p) == ' ') #define eat_spaces(_p) \ while( is_space(*(_p)) ){\ (_p)++;} #define is_alphanum(_p) (((_p) >= 'a' && (_p) <= 'z') || ((_p) >= 'A' && (_p) <= 'Z') || ((_p) >= '0' && (_p) <= '9') || (_p) == '_' || (_p) == '-') #define eat_while_alphanum(_p) \ while ( is_alphanum(*(_p)) ) {\ (_p)++; } /* parse: hname [ ([] | [*] | [number]) ] [ "." param ] */ static int fixup_hname_param(char *hname, struct hname_data** h) { struct hdr_field hdr; char *savep, savec; *h = pkg_malloc(sizeof(**h)); if (!*h) return E_OUT_OF_MEM; memset(*h, 0, sizeof(**h)); memset(&hdr, 0, sizeof(hdr)); eat_spaces(hname); (*h)->hname.s = hname; savep = hname; eat_while_alphanum(hname); (*h)->hname.len = hname - (*h)->hname.s; savec = *hname; *hname = ':'; parse_hname2((*h)->hname.s, (*h)->hname.s+(*h)->hname.len+3, &hdr); *hname = savec; if (hdr.type == HDR_ERROR_T) goto err; (*h)->htype = hdr.type; eat_spaces(hname); savep = hname; if (*hname == '[') { hname++; eat_spaces(hname); savep = hname; (*h)->flags |= HNF_IDX; if (*hname == '*') { (*h)->flags |= HNF_ALL; hname++; } else if (*hname != ']') { char* c; (*h)->idx = strtol(hname, &c, 10); if (hname == c) goto err; hname = c; } eat_spaces(hname); savep = hname; if (*hname != ']') goto err; hname++; } eat_spaces(hname); savep = hname; if (*hname == '.') { hname++; eat_spaces(hname); savep = hname; (*h)->param.s = hname; eat_while_alphanum(hname); (*h)->param.len = hname-(*h)->param.s; if ((*h)->param.len == 0) goto err; } else { (*h)->param.s = hname; } savep = hname; if (*hname != '\0') goto err; (*h)->hname.s[(*h)->hname.len] = '\0'; (*h)->param.s[(*h)->param.len] = '\0'; return 0; err: pkg_free(*h); LOG(L_ERR, "ERROR: textops: cannot parse header near '%s'\n", savep); return E_CFG; } static int fixup_hname_str(void** param, int param_no) { if (param_no == 1) { struct hname_data* h; int res = fixup_hname_param(*param, &h); if (res < 0) return res; *param = h; } else if (param_no == 2) { return fixup_xlstr(param, param_no); } return 0; } static int find_next_hf(struct sip_msg* msg, struct hname_data* hname, struct hdr_field** hf) { if (!*hf) { if (parse_headers(msg, HDR_EOH_F, 0) == -1) { LOG(L_ERR, "ERROR: textops: find_next_hf: Error while parsing message\n"); return -1; } *hf = msg->headers; } else { *hf = (*hf)->next; } for (; *hf; *hf = (*hf)->next) { if (hname->htype == HDR_OTHER_T) { if ((*hf)->name.len==hname->hname.len && strncasecmp((*hf)->name.s, hname->hname.s, (*hf)->name.len)==0) return 1; } else if (hname->htype == (*hf)->type) { return 1; } } return 0; } static int find_next_value(char** start, char* end, str* val, str* lump_val) { int quoted = 0; lump_val->s = *start; while (*start < end && is_space(**start) ) (*start)++; val->s = *start; while ( *start < end && (**start != ',' || quoted) ) { if (**start == '\"' && (!quoted || (*start)[-1]!='\\') ) quoted = ~quoted; (*start)++; } val->len = *start - val->s; while (val->len > 0 && is_space(val->s[val->len-1])) val->len--; /* we cannot automatically strip quotes!!! an example why: "name" ;param="bar" if (val->len >= 2 && val->s[0] == '\"' && val->s[val->len-1] == '\"') { val->s++; val->len -= 2; } */ while (*start < end && **start != ',') (*start)++; if (*start < end) { (*start)++; } lump_val->len = *start - lump_val->s; return (*start < end); } static void adjust_lump_val_for_delete(struct hdr_field* hf, str* lump_val) { if ( lump_val->s+lump_val->len == hf->body.s+hf->body.len ) { if (lump_val->s > hf->body.s) { /* in case if is it last value in header save position of last delimiter to remove it with rightmost value */ lump_val->s--; lump_val->len++; } } } static int find_hf_value_idx(struct sip_msg* msg, struct hname_data* hname, struct hdr_field** hf, str* val, str* lump_val) { int res; char *p; if ( hname->flags & HNF_ALL || hname->idx == 0) return -1; *hf = 0; if (hname->idx > 0) { int idx; idx = hname->idx; do { res = find_next_hf(msg, hname, hf); if (res < 0) return -1; if (*hf) { if (val) { lump_val->len = 0; p = (*hf)->body.s; do { res = find_next_value(&p, (*hf)->body.s+(*hf)->body.len, val, lump_val); idx--; } while (res && idx); } else { idx--; } } } while (*hf && idx); } else if (hname->idx < 0) { /* search from the bottom */ struct hf_value_stack { str val, lump_val; struct hdr_field* hf; } stack[MAX_HF_VALUE_STACK]; int stack_pos, stack_num; if ( -hname->idx > MAX_HF_VALUE_STACK ) return -1; stack_pos = stack_num = 0; do { res = find_next_hf(msg, hname, hf); if (res < 0) return -1; if (*hf) { stack[stack_pos].lump_val.len = 0; p = (*hf)->body.s; do { stack[stack_pos].hf = *hf; if (val) res = find_next_value(&p, (*hf)->body.s+(*hf)->body.len, &stack[stack_pos].val, &stack[stack_pos].lump_val); else res = 0; stack_pos++; if (stack_pos >= MAX_HF_VALUE_STACK) stack_pos = 0; if (stack_num < MAX_HF_VALUE_STACK) stack_num++; } while (res); } } while (*hf); if (-hname->idx <= stack_num) { stack_pos += hname->idx; if (stack_pos < 0) stack_pos += MAX_HF_VALUE_STACK; *hf = stack[stack_pos].hf; if (val) { *val = stack[stack_pos].val; *lump_val = stack[stack_pos].lump_val; } } else { *hf = 0; } } else return -1; return *hf?1:0; } static int find_hf_value_param(struct hname_data* hname, str* param_area, str* value, str* lump_upd, str* lump_del) { int i, j, found; i = 0; while (1) { lump_del->s = param_area->s + i; for (; i < param_area->len && is_space(param_area->s[i]); i++); if (i < param_area->len && param_area->s[i] == ';') { /* found a param ? */ i++; for (; i < param_area->len && is_space(param_area->s[i]); i++); j = i; for (; i < param_area->len && !is_space(param_area->s[i]) && param_area->s[i]!='=' && param_area->s[i]!=';'; i++); found = hname->param.len == i-j && !strncasecmp(hname->param.s, param_area->s+j, i-j); lump_upd->s = param_area->s+i; value->s = param_area->s+i; value->len = 0; for (; i < param_area->len && is_space(param_area->s[i]); i++); if (i < param_area->len && param_area->s[i]=='=') { i++; for (; i < param_area->len && is_space(param_area->s[i]); i++); value->s = param_area->s+i; if (i < param_area->len) { if (param_area->s[i]=='\"') { i++; value->s++; for (; ilen; i++) { if (param_area->s[i]=='\"') { i++; break; } value->len++; } } else { for (; ilen && !is_space(param_area->s[i]) && param_area->s[i]!=';'; i++, value->len++); } } } if (found) { lump_del->len = param_area->s+i - lump_del->s; lump_upd->len = param_area->s+i - lump_upd->s; return 1; } } else { /* not found, return last correct position, should be end of param area */ lump_del->len = 0; return 0; } } } /* parse: something param_name=param_value something [ "," something param_name="param_value" ....] * 'something' is required by Authenticate */ static int find_hf_value2_param(struct hname_data* hname, str* param_area, str* value, str* lump_upd, str* lump_del, char* delim) { int i, j, k, found, comma_flag; i = 0; *delim = 0; lump_del->len = 0; while (i < param_area->len) { lump_del->s = param_area->s + i; while (ilen && is_space(param_area->s[i])) i++; comma_flag = i < param_area->len && param_area->s[i] == ','; if (comma_flag) i++; while (ilen && is_space(param_area->s[i])) i++; if (i < param_area->len && is_alphanum(param_area->s[i])) { /* found a param name ? */ j = i; if (!*delim) *delim = ' '; while (ilen && is_alphanum(param_area->s[i])) i++; k = i; while (ilen && is_space(param_area->s[i])) i++; lump_upd->s = param_area->s + i; if (i < param_area->len && param_area->s[i] == '=') { /* if equal then it's the param */ *delim = ','; i++; found = hname->param.len == k-j && !strncasecmp(hname->param.s, param_area->s+j, k-j); while (ilen && is_space(param_area->s[i])) i++; value->s = param_area->s+i; value->len = 0; if (i < param_area->len) { if (param_area->s[i]=='\"') { i++; value->s++; for (; ilen; i++) { if (param_area->s[i]=='\"') { i++; break; } value->len++; } } else { for (; ilen && !is_space(param_area->s[i]) && param_area->s[i]!=','; i++, value->len++); } } if (found) { lump_upd->len = param_area->s+i - lump_upd->s; lump_del->len = param_area->s+i - lump_del->s; while (ilen && is_space(param_area->s[i])) i++; if (!comma_flag && i < param_area->len && param_area->s[i]==',') { i++; lump_del->len = param_area->s+i - lump_del->s; } return 1; } } while (ilen && is_space(param_area->s[i])) i++; } else { while (ilen && !is_space(param_area->s[i]) && !param_area->s[i]!=',') i++; } } lump_del->s = param_area->s + i; return 0; } static int insert_header_lump(struct sip_msg* msg, char* msg_position, int lump_before, str* hname, str *val) { struct lump* anchor; char *s; int len; anchor = anchor_lump(msg, msg_position - msg->buf, 0, 0); if (anchor == 0) { LOG(L_ERR, "ERROR: textops: insert_header_lump(): Can't get anchor\n"); return -1; } len=hname->len+2+val->len+2; s = (char*)pkg_malloc(len); if (!s) { LOG(L_ERR, "ERROR: textops: insert_header_lump(): not enough memory\n"); return -1; } memcpy(s, hname->s, hname->len); s[hname->len] = ':'; s[hname->len+1] = ' '; memcpy(s+hname->len+2, val->s, val->len); s[hname->len+2+val->len] = '\r'; s[hname->len+2+val->len+1] = '\n'; if ( (lump_before?insert_new_lump_before(anchor, s, len, 0):insert_new_lump_after(anchor, s, len, 0)) == 0) { LOG(L_ERR, "ERROR: textops: insert_header_lump(): Can't insert lump\n"); pkg_free(s); return -1; } return 1; } static int insert_value_lump(struct sip_msg* msg, struct hdr_field* hf, char* msg_position, int lump_before, str *val) { struct lump* anchor; char *s; int len; anchor = anchor_lump(msg, msg_position - msg->buf, 0, 0); if (anchor == 0) { LOG(L_ERR, "ERROR: textops: insert_value_lump(): Can't get anchor\n"); return -1; } len=val->len+1; s = (char*)pkg_malloc(len); if (!s) { LOG(L_ERR, "ERROR: textops: insert_value_lump(): not enough memory\n"); return -1; } if (!hf) { memcpy(s, val->s, val->len); len--; } else if (msg_position == hf->body.s+hf->body.len) { s[0] = ','; memcpy(s+1, val->s, val->len); } else { memcpy(s, val->s, val->len); s[val->len] = ','; } if ( (lump_before?insert_new_lump_before(anchor, s, len, 0):insert_new_lump_after(anchor, s, len, 0)) == 0) { LOG(L_ERR, "ERROR: textops: insert_value_lump(): Can't insert lump\n"); pkg_free(s); return -1; } return 1; } static int delete_value_lump(struct sip_msg* msg, struct hdr_field* hf, str *val) { struct lump* l; /* TODO: check already existing lumps */ if (hf && val->s == hf->body.s && val->len == hf->body.len) /* check if remove whole haeder? */ l=del_lump(msg, hf->name.s-msg->buf, hf->len, 0); else l=del_lump(msg, val->s-msg->buf, val->len, 0); if (l==0) { LOG(L_ERR, "ERROR: textops: delete_value_lump: not enough memory\n"); return -1; } return 1; } static int incexc_hf_value_f(struct sip_msg* msg, char* _hname, char* _val) { struct hname_data* hname = (void*) _hname; struct hdr_field* hf, *lump_hf; str val, hval1, hval2; char *p; int res = eval_xlstr(msg, (void*) _val, &val); if (res < 0) return res; if (!val.len) return -1; hf = 0; lump_hf = 0; while (1) { if (find_next_hf(msg, hname, &hf) < 0) return -1; if (!hf) break; hval2.len = 0; p = hf->body.s; do { res = find_next_value(&p, hf->body.s+hf->body.len, &hval1, &hval2); if (hval1.len && val.len == hval1.len && strncasecmp(val.s, hval1.s, val.len) == 0) { switch (hname->oper) { case hnoIsIncluded: case hnoInclude: return 1; case hnoExclude: adjust_lump_val_for_delete(hf, &hval2); delete_value_lump(msg, hf, &hval2); default: break; } } } while (res); switch (hname->oper) { case hnoInclude: if (!lump_hf) { lump_hf = hf; } break; default: break; } } switch (hname->oper) { case hnoIsIncluded: return -1; case hnoInclude: if (lump_hf) return insert_value_lump(msg, lump_hf, lump_hf->body.s+lump_hf->body.len, 1, &val); else return insert_header_lump(msg, msg->unparsed, 1, &hname->hname, &val); default: return 1; } } #define INCEXC_HF_VALUE_FIXUP(_func,_oper) \ static int _func (void** param, int param_no) {\ char* p = *param; \ int res=fixup_hname_str(param, param_no); \ if (res < 0) return res; \ if (param_no == 1) {\ if ( ((struct hname_data*)*param)->flags & HNF_IDX || ((struct hname_data*)*param)->param.len ) { \ LOG(L_ERR, "ERROR: textops: neither index nor param may be specified in '%s'\n", p);\ return E_CFG;\ }\ ((struct hname_data*)*param)->oper = _oper;\ }\ return 0;\ } INCEXC_HF_VALUE_FIXUP(include_hf_value_fixup, hnoInclude) INCEXC_HF_VALUE_FIXUP(exclude_hf_value_fixup, hnoExclude) INCEXC_HF_VALUE_FIXUP(hf_value_exists_fixup, hnoIsIncluded) static void get_uri_and_skip_until_params(str *param_area, str *name, str *uri) { int i, quoted, uri_pos, uri_done; name->len = 0; uri->len = 0; uri_done = 0; name->s = param_area->s; for (i=0; ilen && param_area->s[i]!=';'; ) { /* [ *(token LSW)/quoted-string ] "<" addr-spec ">" | addr-spec */ /* skip name */ for (quoted=0, uri_pos=i; ilen; i++) { if (!quoted) { if (param_area->s[i] == '\"') { quoted = 1; uri_pos = -1; } else if (param_area->s[i] == '<' || param_area->s[i] == ';' || is_space(param_area->s[i])) break; } else if (param_area->s[i] == '\"' && param_area->s[i-1] != '\\') quoted = 0; } if (!name->len) name->len = param_area->s+i-name->s; if (uri_pos >= 0 && !uri_done) { uri->s = param_area->s+uri_pos; uri->len = param_area->s+i-uri->s; } /* skip uri */ while (ilen && is_space(param_area->s[i])) i++; if (ilen && param_area->s[i]=='<') { uri->s = param_area->s+i; uri->len = 0; for (quoted=0; ilen; i++) { if (!quoted) { if (param_area->s[i] == '\"') quoted = 1; else if (param_area->s[i] == '>') { uri->len = param_area->s+i-uri->s+1; uri_done = 1; break; } } else if (param_area->s[i] == '\"' && param_area->s[i-1] != '\\') quoted = 0; } } } param_area->s+= i; param_area->len-= i; if (uri->s == name->s) name->len = 0; } static int assign_hf_do_lumping(struct sip_msg* msg,struct hdr_field* hf, struct hname_data* hname, str* value, int upd_del_fl, str* lump_upd, str* lump_del, char delim) { int len, i; char *s; struct lump* anchor; if (upd_del_fl) { len = value?lump_upd->len:lump_del->len; if (len > 0) { if (!del_lump(msg, (value?lump_upd->s:lump_del->s)-msg->buf, len, 0)) { LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: not enough memory\n"); return -1; } } if (value && value->len) { anchor = anchor_lump(msg, lump_upd->s - msg->buf, 0, 0); if (anchor == 0) { LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: Can't get anchor\n"); return -1; } len = 1+value->len; s = pkg_malloc(len); if (!s) { LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: not enough memory\n"); return -1; } s[0]='='; memcpy(s+1, value->s, value->len); if ( (insert_new_lump_before(anchor, s, len, 0)) == 0) { LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: Can't insert lump\n"); pkg_free(s); return -1; } } } else { if (!value) return -1; anchor = anchor_lump(msg, lump_del->s - msg->buf, 0, 0); if (anchor == 0) { LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: Can't get anchor\n"); return -1; } len = 1+hname->param.len+(value->len?value->len+1:0); s = pkg_malloc(len); if (!s) { LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: not enough memory\n"); return -1; } if (delim) { s[0] = delim; i = 1; } else { i = 0; len--; } memcpy(s+i, hname->param.s, hname->param.len); if (value->len) { s[hname->param.len+i]='='; memcpy(s+i+hname->param.len+1, value->s, value->len); } if ( (insert_new_lump_before(anchor, s, len, 0)) == 0) { LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: Can't insert lump\n"); pkg_free(s); return -1; } } return 1; } static int assign_hf_process_params(struct sip_msg* msg, struct hdr_field* hf, struct hname_data* hname, str* value, str* value_area) { int r, r2, res=0; str param_area, lump_upd, lump_del, dummy_val, dummy_name, dummy_uri; param_area = *value_area; get_uri_and_skip_until_params(¶m_area, &dummy_name, &dummy_uri); do { r = find_hf_value_param(hname, ¶m_area, &dummy_val, &lump_upd, &lump_del); r2 = assign_hf_do_lumping(msg, hf, hname, value, r, &lump_upd, &lump_del, ';'); if (res == 0) res = r2; if (r && !value) { /* remove all parameters */ param_area.len -= lump_del.s+lump_del.len-param_area.s; param_area.s = lump_del.s+lump_del.len; } } while (!value && r); return res; } static int assign_hf_process2_params(struct sip_msg* msg, struct hdr_field* hf, struct hname_data* hname, str* value) { int r, r2, res = 0; str param_area, lump_upd, lump_del, dummy_val; char delim; param_area = hf->body; do { r = find_hf_value2_param(hname, ¶m_area, &dummy_val, &lump_upd, &lump_del, &delim); r2 = assign_hf_do_lumping(msg, hf, hname, value, r, &lump_upd, &lump_del, delim); if (res == 0) res = r2; if (r && !value) { /* remove all parameters */ param_area.len -= lump_del.s+lump_del.len-param_area.s; param_area.s = lump_del.s+lump_del.len; } } while (!value && r); return res; } static int insupddel_hf_value_f(struct sip_msg* msg, char* _hname, char* _val) { struct hname_data* hname = (void*) _hname; struct hdr_field* hf; str val, hval1, hval2; int res; if (_val) { res = eval_xlstr(msg, (void*) _val, &val); if (res < 0) return res; } switch (hname->oper) { case hnoAppend: if ((hname->flags & HNF_IDX) == 0) { if (parse_headers(msg, HDR_EOH_F, 0) == -1) { LOG(L_ERR, "ERROR: textops: Error while parsing message\n"); return -1; } return insert_header_lump(msg, msg->unparsed, 1, &hname->hname, &val); } else { res = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2); if (res < 0) return res; if (hf) { return insert_value_lump(msg, hf, hval2.s+hval2.len, res /* insert after, except it is last value in header */, &val); } else { return insert_header_lump(msg, msg->unparsed, 1, &hname->hname, &val); } } case hnoInsert: /* if !HNF_IDX is possible parse only until first hname header but not trivial for HDR_OTHER_T header, not implemented */ res = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2); if (res < 0) return res; if (hf && (hname->flags & HNF_IDX) == 0) { return insert_header_lump(msg, hf->name.s, 1, &hname->hname, &val); } else if (!hf && hname->idx == 1) { return insert_header_lump(msg, msg->unparsed, 1, &hname->hname, &val); } else if (hf) { return insert_value_lump(msg, hf, hval2.s, 1, &val); } else return -1; case hnoRemove: case hnoAssign: if (hname->flags & HNF_ALL) { struct hdr_field* hf = 0; int fl = -1; do { res = find_next_hf(msg, hname, &hf); if (res < 0) return res; if (hf) { if (!hname->param.len) { fl = 1; delete_value_lump(msg, hf, &hf->body); } else { char *p; hval2.len = 0; p = hf->body.s; do { res = find_next_value(&p, hf->body.s+hf->body.len, &hval1, &hval2); if (assign_hf_process_params(msg, hf, hname, _val?&val:0, &hval1) > 0) fl = 1; } while (res); } } } while (hf); return fl; } else { res = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2); if (res < 0) return res; if (hf) { if (!hname->param.len) { if (hname->oper == hnoRemove) { adjust_lump_val_for_delete(hf, &hval2); return delete_value_lump(msg, hf, &hval2); } else { res = delete_value_lump(msg, 0 /* delete only value part */, &hval1); if (res < 0) return res; if (val.len) { return insert_value_lump(msg, 0 /* do not add delims */, hval1.s, 1, &val); } return 1; } } else { return assign_hf_process_params(msg, hf, hname, _val?&val:0, &hval1); } } } break; case hnoRemove2: case hnoAssign2: if (hname->flags & HNF_ALL) { struct hdr_field* hf = 0; int fl = -1; do { res = find_next_hf(msg, hname, &hf); if (res < 0) return res; if (hf) { if (!hname->param.len) { /* the same as hnoRemove/hnoAssign */ fl = 1; delete_value_lump(msg, hf, &hf->body); } else { if (assign_hf_process2_params(msg, hf, hname, _val?&val:0) > 0) fl = 1; } } } while (hf); return fl; } else { res = find_hf_value_idx(msg, hname, &hf, 0, 0); if (res < 0) return res; if (hf) { if (!hname->param.len) { if (hname->oper == hnoRemove2) { return delete_value_lump(msg, hf, &hf->body); } else { res = delete_value_lump(msg, 0 /* delete only value part */, &hf->body); if (res < 0) return res; if (val.len) { return insert_value_lump(msg, 0 /* do not add delims */, hf->body.s, 1, &val); } return 1; } } else { return assign_hf_process2_params(msg, hf, hname, _val?&val:0); } } } break; } return -1; } static int append_hf_value_fixup(void** param, int param_no) { int res=fixup_hname_str(param, param_no); if (res < 0) return res; if (param_no == 1) { if ( ((struct hname_data*)*param)->flags & HNF_ALL ) { LOG(L_ERR, "ERROR: textops: asterisk not supported\n"); return E_CFG; } else if ( (((struct hname_data*)*param)->flags & HNF_IDX) == 0 || !((struct hname_data*)*param)->idx ) { ((struct hname_data*)*param)->idx = -1; } if (((struct hname_data*)*param)->idx < -MAX_HF_VALUE_STACK) { LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK); return E_CFG; } if ( ((struct hname_data*)*param)->param.len ) { LOG(L_ERR, "ERROR: textops: param not supported\n"); return E_CFG; } ((struct hname_data*)*param)->oper = hnoAppend; } return 0; } static int insert_hf_value_fixup(void** param, int param_no) { int res=fixup_hname_str(param, param_no); if (res < 0) return res; if (param_no == 1) { if ( ((struct hname_data*)*param)->flags & HNF_ALL ) { LOG(L_ERR, "ERROR: textops: asterisk not supported\n"); return E_CFG; } else if ( (((struct hname_data*)*param)->flags & HNF_IDX) == 0 || !((struct hname_data*)*param)->idx ) { ((struct hname_data*)*param)->idx = 1; } if (((struct hname_data*)*param)->idx < -MAX_HF_VALUE_STACK) { LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK); return E_CFG; } if ( ((struct hname_data*)*param)->param.len ) { LOG(L_ERR, "ERROR: textops: param not supported\n"); return E_CFG; } ((struct hname_data*)*param)->oper = hnoInsert; } return 0; } static int remove_hf_value_fixup(void** param, int param_no) { int res=fixup_hname_str(param, param_no); if (res < 0) return res; if (param_no == 1) { if ( (((struct hname_data*)*param)->flags & HNF_IDX) == 0 || !((struct hname_data*)*param)->idx ) { ((struct hname_data*)*param)->idx = 1; ((struct hname_data*)*param)->flags |= HNF_IDX; } if (((struct hname_data*)*param)->idx < -MAX_HF_VALUE_STACK) { LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK); return E_CFG; } ((struct hname_data*)*param)->oper = hnoRemove; } return 0; } static int assign_hf_value_fixup(void** param, int param_no) { int res=fixup_hname_str(param, param_no); if (res < 0) return res; if (param_no == 1) { if ( (((struct hname_data*)*param)->flags & HNF_ALL) && !((struct hname_data*)*param)->param.len) { LOG(L_ERR, "ERROR: textops: asterisk not supported without param\n"); return E_CFG; } else if ( (((struct hname_data*)*param)->flags & HNF_IDX) == 0 || !((struct hname_data*)*param)->idx ) { ((struct hname_data*)*param)->idx = 1; ((struct hname_data*)*param)->flags |= HNF_IDX; } if (((struct hname_data*)*param)->idx < -MAX_HF_VALUE_STACK) { LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK); return E_CFG; } ((struct hname_data*)*param)->oper = hnoAssign; } return 0; } static int remove_hf_value2_fixup(void** param, int param_no) { int res=remove_hf_value_fixup(param, param_no); if (res < 0) return res; if (param_no == 1) { ((struct hname_data*)*param)->oper = hnoRemove2; } return 0; } static int assign_hf_value2_fixup(void** param, int param_no) { int res=assign_hf_value_fixup(param, param_no); if (res < 0) return res; if (param_no == 1) { ((struct hname_data*)*param)->oper = hnoAssign2; } return 0; } static int sel_hf_value(str* res, select_t* s, struct sip_msg* msg) { /* dummy */ return 0; } #define _ALLOC_INC_SIZE 1024 static int sel_hf_value_name(str* res, select_t* s, struct sip_msg* msg) { struct hname_data* hname; struct hdr_field* hf; str val, hval1, hval2, huri, dummy_name; int r; if (!msg) { struct hdr_field hdr; char buf[50]; int i, n; if (s->params[1].type == SEL_PARAM_STR) { hname = pkg_malloc(sizeof(*hname)); if (!hname) return E_OUT_OF_MEM; memset(hname, 0, sizeof(*hname)); for (i=s->params[1].v.s.len-1; i>0; i--) { if (s->params[1].v.s.s[i]=='_') s->params[1].v.s.s[i]='-'; } i = snprintf(buf, sizeof(buf)-1, "%.*s: X\n", s->params[1].v.s.len, s->params[1].v.s.s); buf[i] = 0; hname->hname = s->params[1].v.s; parse_hname2(buf, buf+i, &hdr); if (hdr.type == HDR_ERROR_T) return E_CFG; hname->htype = hdr.type; s->params[1].v.p = hname; s->params[1].type = SEL_PARAM_PTR; } else { hname = s->params[1].v.p; } n = s->param_offset[select_level+1] - s->param_offset[select_level]; /* number of values before NESTED */ if (n > 2 && s->params[2].type == SEL_PARAM_INT) { hname->idx = s->params[2].v.i; hname->flags |= HNF_IDX; if (hname->idx < -MAX_HF_VALUE_STACK) { LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK); return E_CFG; } if (hname->idx == 0) hname->idx = 1; i = 3; } else { i = 2; hname->idx = 1; } if (n > i && s->params[i].type == SEL_PARAM_STR) { hname->param = s->params[i].v.s; for (i=hname->param.len-1; i>0; i--) { if (hname->param.s[i]=='_') hname->param.s[i]='-'; } } s->params[1].v.p = hname; s->params[1].type = SEL_PARAM_PTR; hname->oper = hnoGetValue; return 0; } res->len = 0; res->s = 0; hname = s->params[1].v.p; switch (hname->oper) { case hnoGetValueUri: if (hname->flags & HNF_ALL || (hname->flags & HNF_IDX) == 0) { char *buf = NULL; int buf_len = 0; hf = 0; do { r = find_next_hf(msg, hname, &hf); if (r < 0) break; if (hf) { char *p; str huri; hval2.len = 0; p = hf->body.s; do { r = find_next_value(&p, hf->body.s+hf->body.len, &hval1, &hval2); get_uri_and_skip_until_params(&hval1, &dummy_name, &huri); if (huri.len) { /* TODO: normalize uri, lowercase except quoted params, add/strip < > */ if (*huri.s == '<') { huri.s++; huri.len -= 2; } } if (res->len == 0) { *res = huri; /* first value, if is also last value then we don't need any buffer */ } else { if (buf) { if (res->len+huri.len+1 > buf_len) { buf_len = res->len+huri.len+1+_ALLOC_INC_SIZE; res->s = pkg_realloc(buf, buf_len); if (!res->s) { pkg_free(buf); LOG(L_ERR, "ERROR: textops: cannot realloc buffer\n"); res->len = 0; return E_OUT_OF_MEM; } buf = res->s; } } else { /* 2nd value */ buf_len = res->len+huri.len+1+_ALLOC_INC_SIZE; buf = pkg_malloc(buf_len); if (!buf) { LOG(L_ERR, "ERROR: testops: out of memory\n"); res->len = 0; return E_OUT_OF_MEM; } /* copy 1st value */ memcpy(buf, res->s, res->len); res->s = buf; } res->s[res->len] = ','; res->len++; if (huri.len) { memcpy(res->s+res->len, huri.s, huri.len); res->len += huri.len; } } } while (r); } } while (hf); if (buf) { res->s = get_static_buffer(res->len); if (!res->s) { pkg_free(buf); res->len = 0; LOG(L_ERR, "ERROR: testops: cannot allocate static buffer\n"); return E_OUT_OF_MEM; } memcpy(res->s, buf, res->len); pkg_free(buf); } } else { r = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2); if (r > 0) { get_uri_and_skip_until_params(&hval1, &dummy_name, res); if (res->len && *res->s == '<') { res->s++; /* strip < & > */ res->len-=2; } } } break; case hnoGetValueName: if ((hname->flags & HNF_ALL) == 0) { r = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2); if (r > 0) { get_uri_and_skip_until_params(&hval1, res, &dummy_name); if (res->len >= 2 && res->s[0] == '\"' && res->s[res->len-1]=='\"' ) { res->s++; /* strip quotes */ res->len-=2; } } } break; case hnoGetValue: if (hname->flags & HNF_ALL || (hname->flags & HNF_IDX) == 0) { char *buf = NULL; int buf_len = 0; hf = 0; do { r = find_next_hf(msg, hname, &hf); if (r < 0) break; if (hf) { char *p; hval2.len = 0; p = hf->body.s; do { r = find_next_value(&p, hf->body.s+hf->body.len, &hval1, &hval2); if (res->len == 0) { *res = hval1; /* first value, if is also last value then we don't need any buffer */ } else { if (buf) { if (res->len+hval1.len+1 > buf_len) { buf_len = res->len+hval1.len+1+_ALLOC_INC_SIZE; res->s = pkg_realloc(buf, buf_len); if (!res->s) { pkg_free(buf); LOG(L_ERR, "ERROR: textops: cannot realloc buffer\n"); res->len = 0; return E_OUT_OF_MEM; } buf = res->s; } } else { /* 2nd value */ buf_len = res->len+hval1.len+1+_ALLOC_INC_SIZE; buf = pkg_malloc(buf_len); if (!buf) { LOG(L_ERR, "ERROR: testops: out of memory\n"); res->len = 0; return E_OUT_OF_MEM; } /* copy 1st value */ memcpy(buf, res->s, res->len); res->s = buf; } res->s[res->len] = ','; res->len++; if (hval1.len) { memcpy(res->s+res->len, hval1.s, hval1.len); res->len += hval1.len; } } } while (r); } } while (hf); if (buf) { res->s = get_static_buffer(res->len); if (!res->s) { pkg_free(buf); res->len = 0; LOG(L_ERR, "ERROR: testops: cannot allocate static buffer\n"); return E_OUT_OF_MEM; } memcpy(res->s, buf, res->len); pkg_free(buf); } } else { r = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2); if (r > 0) { if (hname->param.len) { str d1, d2; get_uri_and_skip_until_params(&hval1, &dummy_name, &huri); if (find_hf_value_param(hname, &hval1, &val, &d1, &d2)) { *res = val; } } else { *res = hval1; } } } break; case hnoGetValue2: r = find_hf_value_idx(msg, hname, &hf, 0, 0); if (r > 0) { if (hname->param.len) { str d1, d2; char c; if (find_hf_value2_param(hname, &hf->body, &val, &d1, &d2, &c)) { *res = val; } } else { *res = hf->body; } } break; default: break; } return 0; } static int sel_hf_value_name_param_name(str* res, select_t* s, struct sip_msg* msg) { return sel_hf_value_name(res, s, msg); } static int sel_hf_value_name_param_name2(str* res, select_t* s, struct sip_msg* msg) { if (!msg) { /* eliminate "param" level */ int n; n = s->param_offset[select_level+1] - s->param_offset[select_level]; s->params[n-2] = s->params[n-1]; } return sel_hf_value_name(res, s, msg); } static int sel_hf_value_name_uri(str* res, select_t* s, struct sip_msg* msg) { int r; r = sel_hf_value_name(res, s, msg); if (!msg && r==0) { ((struct hname_data*) s->params[1].v.p)->oper = hnoGetValueUri; } return r; } static int sel_hf_value_name_name(str* res, select_t* s, struct sip_msg* msg) { int r; r = sel_hf_value_name(res, s, msg); if (!msg && r==0) { ((struct hname_data*) s->params[1].v.p)->oper = hnoGetValueName; } return r; } static int sel_hf_value_exists(str* res, select_t* s, struct sip_msg* msg) { /* dummy */ return 0; } static int sel_hf_value_exists_param(str* res, select_t* s, struct sip_msg* msg) { static char ret_val[] = "01"; struct xlstr xlstr; int r; if (!msg) { r = sel_hf_value_name(res, s, msg); if (r == 0) ((struct hname_data*) s->params[1].v.p)->oper = hnoIsIncluded; return r; } xlstr.s = s->params[2].v.s; xlstr.xlfmt = 0; r = incexc_hf_value_f(msg, s->params[1].v.p, (void*) &xlstr); res->s = &ret_val[r > 0]; res->len = 1; return 0; } static int sel_hf_value2(str* res, select_t* s, struct sip_msg* msg) { /* dummy */ return 0; } static int sel_hf_value2_name(str* res, select_t* s, struct sip_msg* msg) { int r; r = sel_hf_value_name(res, s, msg); if (!msg && r==0) { ((struct hname_data*) s->params[1].v.p)->oper = hnoGetValue2; } return r; } static int sel_hf_value2_name_param_name(str* res, select_t* s, struct sip_msg* msg) { return sel_hf_value2_name(res, s, msg); } SELECT_F(select_any_nameaddr) SELECT_F(select_any_uri) SELECT_F(select_anyheader_params) select_row_t sel_declaration[] = { { NULL, SEL_PARAM_STR, STR_STATIC_INIT("hf_value"), sel_hf_value, SEL_PARAM_EXPECTED}, { sel_hf_value, SEL_PARAM_STR, STR_NULL, sel_hf_value_name, CONSUME_NEXT_INT | OPTIONAL | FIXUP_CALL}, { sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("param"), sel_hf_value_name_param_name2, CONSUME_NEXT_STR | FIXUP_CALL}, { sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("p"), sel_hf_value_name_param_name2, CONSUME_NEXT_STR | FIXUP_CALL}, { sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("uri"), sel_hf_value_name_uri, FIXUP_CALL}, { sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("name"), sel_hf_value_name_name, FIXUP_CALL}, { sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("nameaddr"), select_any_nameaddr, NESTED | CONSUME_NEXT_STR}, /* it duplicates param,p,name,... */ { sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("params"), select_anyheader_params, NESTED}, { sel_hf_value_name_uri, SEL_PARAM_INT, STR_NULL, select_any_uri, NESTED}, { sel_hf_value_name, SEL_PARAM_STR, STR_NULL, sel_hf_value_name_param_name, FIXUP_CALL}, { NULL, SEL_PARAM_STR, STR_STATIC_INIT("hf_value_exists"), sel_hf_value_exists, CONSUME_NEXT_STR | SEL_PARAM_EXPECTED}, { sel_hf_value_exists, SEL_PARAM_STR, STR_NULL, sel_hf_value_exists_param, FIXUP_CALL}, { NULL, SEL_PARAM_STR, STR_STATIC_INIT("hf_value2"), sel_hf_value2, SEL_PARAM_EXPECTED}, { sel_hf_value2, SEL_PARAM_STR, STR_NULL, sel_hf_value2_name, CONSUME_NEXT_INT | OPTIONAL | FIXUP_CALL}, { sel_hf_value2_name, SEL_PARAM_STR, STR_STATIC_INIT("params"), select_anyheader_params, NESTED}, { sel_hf_value2_name, SEL_PARAM_STR, STR_NULL, sel_hf_value2_name_param_name, FIXUP_CALL}, { sel_hf_value2_name_param_name, SEL_PARAM_STR, STR_STATIC_INIT("nameaddr"), select_any_nameaddr, NESTED}, { sel_hf_value2_name_param_name, SEL_PARAM_STR, STR_STATIC_INIT("uri"), select_any_uri, NESTED}, { NULL, SEL_PARAM_INT, STR_NULL, NULL, 0} }; kamailio-4.0.4/obsolete/textops/doc/0000755000000000000000000000000012223032460016064 5ustar rootrootkamailio-4.0.4/obsolete/textops/doc/functions.xml0000644000000000000000000004742112223032460020626 0ustar rootroot
Functions
<function>search_append(re, txt)</function> Searches for the first match of re and appends txt after it. Meaning of the parameters is as follows: re - Regular expression. txt - String to be appended. Xl_lib formatting supported. <function>search_append</function> usage ... search_append("[Ss]er", " blabla"); ...
<function>replace(re, txt)</function> Replaces the first occurrence of re with txt. Meaning of the parameters is as follows: re - Regular expression. txt - String. Xl_lib formatting supported. <function>replace</function> usage ... replace("ser", "Sip Express Router"); ...
<function>subst('/re/repl/flags')</function> Replaces re with repl (sed or perl like). Meaning of the parameters is as follows: '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). <function>subst</function> usage ... # replace the uri in to: with the message uri (just an example) if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1\u\2/ig') ) {}; ...
<function>subst_uri('/re/repl/flags')</function> Runs the re substitution on the message uri (like subst but works only on the uri) Meaning of the parameters is as follows: '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). <function>subst</function> usage ... # adds 3463 prefix to numeric uris, and save the original uri (\0 match) # as a parameter: orig_uri (just an example) if (subst_uri('/^sip:([0-9]+)@(.*)$/sip:3463\1@\2;orig_uri=\0/i')){$ ...
<function>subst_user('/re/repl/flags')</function> Runs the re substitution on the message uri (like subst_uri but works only on the user portion of the uri) Meaning of the parameters is as follows: '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). <function>subst</function> usage ... # adds 3463 prefix to uris ending with 3642 (just an example) if (subst_user('/3642$/36423463/')){$ ...
<function>append_to_reply(txt)</function> Append txt to the reply. Meaning of the parameters is as follows: txt - String. Xl_lib formatting supported. <function>append_to_reply</function> usage ... append_to_reply("Foo: bar\r\n"); ...
<function>append_hf(hf)</function> Appends txt after the last header field. Meaning of the parameters is as follows: hf - Header field to be appended. Xl_lib formatting supported. <function>append_hf</function> usage ... append_hf("P-hint: VOICEMAIL\r\n"); ...
<function>append_urihf(prefix, suffix)</function> Append header field name with original Request-URI in middle. Meaning of the parameters is as follows: prefix - string (usually at least header field name). Xl_lib formatting supported. suffix - string (usually at least line terminator). Xl_lib formatting supported. <function>append_urihf</function> usage ... append_urihf("CC-Diversion: ", "\r\n"); ...
<function>is_present_hf(hf_name)</function> Return true if a header field is present in message. Takes header field names "as is" and doesn't distinguish compact names. Meaning of the parameters is as follows: hf_name - Header field name. <function>is_present_hf</function> usage ... if (is_present_hf("From")) log(1, "From HF Present"); ...
<function>append_time()</function> Append "Date" header containing the current date and time to the reply generated by SER. <function>is_present_hf</function> usage ... if (method == "REGISTER" ) { # Many user agents can use the Date header field # in 200 OK replies to REGISTER to synchronize # internal clock append_time(); }; ...
<function>remove_hf(hf_name)</function> Remove from the message all the headers with the specified name. Meaning of the parameters is as follows: hf_name - Header field name to be removed. <function>remove_hf</function> usage ... remove_hf("Subject") # strip all headers whose name is "Subject". ...
<function>remove_hf_re(reg_exp)</function> Remove from the message all the headers whose names match a given regular expression. Meaning of the parameters is as follows: reg_exp - Regular expression that is matched against header name fields. <function>remove_hf_re</function> usage ... remove_hf_re("Subject|P-.*|X-.*") # strip all headers whose names match "Subject", contain "P-" or "X-". ...
<function>append_hf_value(hf, xl_value)</function> Append new header value after an existing header, if no index acquired append at the end of list. Note that a header may consist of comma delimited list of values. Meaning of the parameters is as follows: hf - Header field to be appended. Format: HFNAME [ [IDX] ]. If index is not specified new header is inserted at the end of message. xl_value - Value to be added, xl_lib formatting supported. <function>append_hf_value</function> usage ... append_hf_value("foo", "gogo;stamp=%Ts") # add new header append_hf_value("foo[1]", "gogo") # add new value behind first value append_hf_value("foo[-1]", "%@Bar") # try add value to the last header, if not exists add new header ...
<function>insert_hf_value(hf, xl_value)</function> Insert new header value before an existing header, if no index acquired insert before first hf header. Note that a header may consist of comma delimited list of values. To insert value behing last value use appenf_hf_value. Meaning of the parameters is as follows: hf - Header field to be appended. Format: HFNAME [ [IDX] ]. If index is not specified new header is inserted at the top of message. xl_value - Value to be added, xl_lib formatting supported. <function>insert_hf_value</function> usage ... insert_hf_value("foo[2]", "gogo") insert_hf_value("foo", "%$an_avp") # add new header at the top of list insert_hf_value("foo[1]", "gogo") # try add to the first header ...
<function>remove_hf_value(hf_par)</function> Remove the header value from existing header, Note that a header may consist of comma delimited list of values. Meaning of the parameters is as follows: hf_par - Header field/param to be removed. Format: HFNAME [ [IDX] ] [. PARAM ] If asterisk is specified as index then all values are affected. <function>remove_hf_value</function> usage ... remove_hf_value("foo") # remove foo[1] remove_hf_value("foo[*]") # remove all foo's headers remove_hf_value("foo[-1]") # last foo remove_hf_value("foo.bar") # delete parameter remove_hf_value("foo[*].bar") # for each foo delete bar parameters ...
<function>remove_hf_value2(hf_par)</function> Remove specified header or parameter. It is expected header in Authorization format (comma delimiters are not treated as multi-value delimiters). Meaning of the parameters is as follows: hf_par - Header/param to be removed. Format: HFNAME [ [IDX] ] [. PARAM ] If asterisk is specified as index then all values are affected. <function>remove_hf_value2</function> usage ... remove_hf_value2("foo") # remove foo[1] remove_hf_value2("foo[*]") # remove all foo's headers, the same as remove_hf_header("foo[*]"); remove_hf_value2("foo[-1]") # last foo remove_hf_value2("foo.bar") # delete parameter remove_hf_value2("foo[*].bar") # for each foo delete bar parameters ...
<function>assign_hf_value(hf, xl_value)</function> Assign value to specified header value / param. Meaning of the parameters is as follows: hf_para - Header field value / param to be appended. Format: HFNAME [ [IDX] ] [. PARAM] If asterisk is specified as index then all values are affected. xl_value - Value to be assigned, xl_lib formatting supported. If value is empty then no equal sign apears in param. <function>assign_hf_value</function> usage ... assign_hf_value("foo", "gogo") # foo[1] assign_hf_value("foo[-1]", "gogo") # foo[last_foo] assign_hf_value("foo.bar", "") assign_hf_value("foo[3].bar", "") assign_hf_value("foo[*]", "") # remove all foo's, empty value remains assign_hf_value("foo[*].bar", "") # set empty value (ex. lr) ...
<function>assign_hf_value2(hf, xl_value)</function> Assign value to specified header. It is expected header in Authorization format (comma delimiters are not treated as multi-value delimiters). Meaning of the parameters is as follows: hf_para - Header field value / param to be appended. Format: HFNAME [ [IDX] ] [. PARAM] If asterisk is specified as index then all values are affected. xl_value - Value to be assigned, xl_lib formatting supported. If value is empty then no equal sign apears in param. <function>assign_hf_value2</function> usage ... assign_hf_value2("Authorization.integrity-protected", "\"yes\"") assign_hf_value2("foo[-1]", "gogo") # foo[last_foo] assign_hf_value2("foo[*].bar", "") # set empty value (ex. lr) ...
<function>include_hf_value(hf, xl_value)</function> Add value in set if not exists, eg. "Supported: path,100rel". Meaning of the parameters is as follows: hf - Header field name to be affected. value - xl_lib formatting supported. <function>include_hf_value</function> usage ... include_hf_value("Supported", "path"); ...
<function>exclude_hf_value(hf, xl_value)</function> Remove value from set if exists, eg. "Supported: path,100rel". Meaning of the parameters is as follows: hf - Header field name to be affected. value - xl_lib formatting supported. <function>exclude_hf_value</function> usage ... exclude_hf_value("Supported", "100rel"); ...
<function>hf_value_exists(hf, xl_value)</function> Check if value exists in set. Alternate select @hf_value_exists.HF.VALUE may be used. It returns one or zero. Meaning of the parameters is as follows: hf - Header field name to be affected. Underscores are treated as dashes. value - xl_lib formatting supported. <function>hf_value_exists</function> usage ... if (hf_value_exists("Supported", "100rel")) { } if (@hf_value_exists.supported.path == "1") { } ...
<function>@hf_value selects</function> Get value of required header-value or param. Note that functions called 'value2' works with Authorization-like headers where comma is not treated as value delimiter. Formats: @hf_value.HFNAME[IDX] # idx value, negative value counts from bottom @hf_value.HFNAME.PARAM_NAME @hf_value.HFNAME[IDX].PARAM_NAME @hf_value.HFNAME.p.PARAM_NAME # or .param., useful if requred called "uri", "p", "param" @hf_value.HFNAME[IDX].p.PARAM_NAME # dtto @hf_value.HFNAME[IDX].uri # (< & > excluded) @hf_value.HFNAME[*] # return comma delimited list of all values (combines headers) @hf_value.HFNAME # the same as above [*] but may be parsed by cfg.y @hf_value.HFNAME[*].uri # return comma delimited list of uris (< & > excluded) @hf_value.HFNAME.uri # the same as above [*] but may be parsed by cfg.y @hf_value.HFNAME[IDX].name # returns name part, quotes excluded @hf_value.HFNAME.name # returns name part of the first value @hf_value2.HFNAME # returns value of first header @hf_value2.HFNAME[IDX] # returns value of idx's header @hf_value2.HFNAME.PARAM_NAME @hf_value2.HFNAME[IDX].PARAM_NAME @hf_value.HFNAME[IDX].uri # return URI, quotes excluded @hf_value.HFNAME.p.uri # returns param named uri, not URI itself @hf_value.HFNAME.p.name # returns param named name, not name itself @hf_value.HFNAME[IDX].uri.name # any sel_any_uri nested features may be used @hf_value.HFNAME[IDX].nameaddr.name # select_any_nameaddr Meaning of the parameters is as follows: HFNAME - Header field name. Underscores are treated as dashes. IDX - Value index, negative value counts from bottom PARAM_NAME - name of parameter <function>@hf_value select</function> usage ... $a = @hf_value.my_header[1].my_param; xplog("L_ERR", "%@hf_value.via[-1], %@hf_value.from.tag\n"); $b = @hf_value.p_associated_uri; xplog("L_ERR", "Route uris: '%@hf_value.route[*].uri'\n"); $rr = @hf_value.route.uri; $prt = @hf_value2.authorization.integrity_protected; ...
kamailio-4.0.4/obsolete/textops/doc/textops.xml0000644000000000000000000000245012223032460020315 0ustar rootroot
Andrei Pelinescu-Onciul FhG FOKUS
pelinescu-onciul@fokus.fraunhofer.de
2003 FhG FOKUS
Textops Module
Overview This is mostly an example module. It implements text based operation (search, replace, append a.s.o). Many functions support xl_lib formating using xprint module.
Known Limitations search ignores folded lines. For example, search("(From|f):.*@foo.bar") doesn't match the following From header field: From: medabeda <sip:medameda@foo.bar>;tag=1234
kamailio-4.0.4/obsolete/textops/doc/Makefile0000644000000000000000000000013012223032460017516 0ustar rootrootdocs = textops.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/textops/doc/params.xml0000644000000000000000000000050112223032460020065 0ustar rootroot
Parameters
kamailio-4.0.4/obsolete/textops/README0000644000000000000000000003303712223032460016205 0ustar rootroot1. Textops Module Andrei Pelinescu-Onciul FhG FOKUS Copyright © 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.1.1. Known Limitations 1.2. Functions 1.2.1. search(re) 1.2.2. search_append(re, txt) 1.2.3. replace(re, txt) 1.2.4. subst('/re/repl/flags') 1.2.5. subst_uri('/re/repl/flags') 1.2.6. subst_user('/re/repl/flags') 1.2.7. append_to_reply(txt) 1.2.8. append_hf(hf) 1.2.9. append_urihf(prefix, suffix) 1.2.10. is_present_hf(hf_name) 1.2.11. append_time() 1.2.12. remove_hf(hf_name) 1.2.13. remove_hf_re(reg_exp) 1.2.14. append_hf_value(hf, xl_value) 1.2.15. insert_hf_value(hf, xl_value) 1.2.16. remove_hf_value(hf_par) 1.2.17. remove_hf_value2(hf_par) 1.2.18. assign_hf_value(hf, xl_value) 1.2.19. assign_hf_value2(hf, xl_value) 1.2.20. include_hf_value(hf, xl_value) 1.2.21. exclude_hf_value(hf, xl_value) 1.2.22. hf_value_exists(hf, xl_value) 1.2.23. @hf_value selects 1.1. Overview This is mostly an example module. It implements text based operation (search, replace, append a.s.o). Many functions support xl_lib formating using xprint module. 1.1.1. Known Limitations search ignores folded lines. For example, search("(From|f):.*@foo.bar") doesn't match the following From header field: From: medabeda ;tag=1234 1.2. Functions 1.2.1. search(re) Searches for the re in the message. Meaning of the parameters is as follows: * re - Regular expression. Example 1. search usage ... if ( search("[Ss][Ee][Rr]" ) { /*....*/ }; ... 1.2.2. search_append(re, txt) Searches for the first match of re and appends txt after it. Meaning of the parameters is as follows: * re - Regular expression. * txt - String to be appended. Xl_lib formatting supported. Example 2. search_append usage ... search_append("[Ss]er", " blabla"); ... 1.2.3. replace(re, txt) Replaces the first occurrence of re with txt. Meaning of the parameters is as follows: * re - Regular expression. * txt - String. Xl_lib formatting supported. Example 3. replace usage ... replace("ser", "Sip Express Router"); ... 1.2.4. subst('/re/repl/flags') Replaces re with repl (sed or perl like). Meaning of the parameters is as follows: * '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). Example 4. subst usage ... # replace the uri in to: with the message uri (just an example) if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1\u\2/ig') ) {}; ... 1.2.5. subst_uri('/re/repl/flags') Runs the re substitution on the message uri (like subst but works only on the uri) Meaning of the parameters is as follows: * '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). Example 5. subst usage ... # adds 3463 prefix to numeric uris, and save the original uri (\0 match) # as a parameter: orig_uri (just an example) if (subst_uri('/^sip:([0-9]+)@(.*)$/sip:3463\1@\2;orig_uri=\0/i')){$ ... 1.2.6. subst_user('/re/repl/flags') Runs the re substitution on the message uri (like subst_uri but works only on the user portion of the uri) Meaning of the parameters is as follows: * '/re/repl/flags' - sed like regular expression. flags can be a combination of i (case insensitive), g (global) or s (match newline don't treat it as end of line). Example 6. subst usage ... # adds 3463 prefix to uris ending with 3642 (just an example) if (subst_user('/3642$/36423463/')){$ ... 1.2.7. append_to_reply(txt) Append txt to the reply. Meaning of the parameters is as follows: * txt - String. Xl_lib formatting supported. Example 7. append_to_reply usage ... append_to_reply("Foo: bar\r\n"); ... 1.2.8. append_hf(hf) Appends txt after the last header field. Meaning of the parameters is as follows: * hf - Header field to be appended. Xl_lib formatting supported. Example 8. append_hf usage ... append_hf("P-hint: VOICEMAIL\r\n"); ... 1.2.9. append_urihf(prefix, suffix) Append header field name with original Request-URI in middle. Meaning of the parameters is as follows: * prefix - string (usually at least header field name). Xl_lib formatting supported. * suffix - string (usually at least line terminator). Xl_lib formatting supported. Example 9. append_urihf usage ... append_urihf("CC-Diversion: ", "\r\n"); ... 1.2.10. is_present_hf(hf_name) Return true if a header field is present in message. Note Takes header field names "as is" and doesn't distinguish compact names. Meaning of the parameters is as follows: * hf_name - Header field name. Example 10. is_present_hf usage ... if (is_present_hf("From")) log(1, "From HF Present"); ... 1.2.11. append_time() Append "Date" header containing the current date and time to the reply generated by SER. Example 11. is_present_hf usage ... if (method == "REGISTER" ) { # Many user agents can use the Date header field # in 200 OK replies to REGISTER to synchronize # internal clock append_time(); }; ... 1.2.12. remove_hf(hf_name) Remove from the message all the headers with the specified name. Meaning of the parameters is as follows: * hf_name - Header field name to be removed. Example 12. remove_hf usage ... remove_hf("Subject") # strip all headers whose name is "Subject". ... 1.2.13. remove_hf_re(reg_exp) Remove from the message all the headers whose names match a given regular expression. Meaning of the parameters is as follows: * reg_exp - Regular expression that is matched against header name fields. Example 13. remove_hf_re usage ... remove_hf_re("Subject|P-.*|X-.*") # strip all headers whose names match "Subject", contain "P-" or "X-". ... 1.2.14. append_hf_value(hf, xl_value) Append new header value after an existing header, if no index acquired append at the end of list. Note that a header may consist of comma delimited list of values. Meaning of the parameters is as follows: * hf - Header field to be appended. Format: HFNAME [ [IDX] ]. If index is not specified new header is inserted at the end of message. * xl_value - Value to be added, xl_lib formatting supported. Example 14. append_hf_value usage ... append_hf_value("foo", "gogo;stamp=%Ts") # add new header append_hf_value("foo[1]", "gogo") # add new value behind first value append_hf_value("foo[-1]", "%@Bar") # try add value to the last header, if not e xists add new header ... 1.2.15. insert_hf_value(hf, xl_value) Insert new header value before an existing header, if no index acquired insert before first hf header. Note that a header may consist of comma delimited list of values. To insert value behing last value use appenf_hf_value. Meaning of the parameters is as follows: * hf - Header field to be appended. Format: HFNAME [ [IDX] ]. If index is not specified new header is inserted at the top of message. * xl_value - Value to be added, xl_lib formatting supported. Example 15. insert_hf_value usage ... insert_hf_value("foo[2]", "gogo") insert_hf_value("foo", "%$an_avp") # add new header at the top of list insert_hf_value("foo[1]", "gogo") # try add to the first header ... 1.2.16. remove_hf_value(hf_par) Remove the header value from existing header, Note that a header may consist of comma delimited list of values. Meaning of the parameters is as follows: * hf_par - Header field/param to be removed. Format: HFNAME [ [IDX] ] [. PARAM ] If asterisk is specified as index then all values are affected. Example 16. remove_hf_value usage ... remove_hf_value("foo") # remove foo[1] remove_hf_value("foo[*]") # remove all foo's headers remove_hf_value("foo[-1]") # last foo remove_hf_value("foo.bar") # delete parameter remove_hf_value("foo[*].bar") # for each foo delete bar parameters ... 1.2.17. remove_hf_value2(hf_par) Remove specified header or parameter. It is expected header in Authorization format (comma delimiters are not treated as multi-value delimiters). Meaning of the parameters is as follows: * hf_par - Header/param to be removed. Format: HFNAME [ [IDX] ] [. PARAM ] If asterisk is specified as index then all values are affected. Example 17. remove_hf_value2 usage ... remove_hf_value2("foo") # remove foo[1] remove_hf_value2("foo[*]") # remove all foo's headers, the same as remove_hf_he ader("foo[*]"); remove_hf_value2("foo[-1]") # last foo remove_hf_value2("foo.bar") # delete parameter remove_hf_value2("foo[*].bar") # for each foo delete bar parameters ... 1.2.18. assign_hf_value(hf, xl_value) Assign value to specified header value / param. Meaning of the parameters is as follows: * hf_para - Header field value / param to be appended. Format: HFNAME [ [IDX] ] [. PARAM] If asterisk is specified as index then all values are affected. * xl_value - Value to be assigned, xl_lib formatting supported. If value is empty then no equal sign apears in param. Example 18. assign_hf_value usage ... assign_hf_value("foo", "gogo") # foo[1] assign_hf_value("foo[-1]", "gogo") # foo[last_foo] assign_hf_value("foo.bar", "") assign_hf_value("foo[3].bar", "") assign_hf_value("foo[*]", "") # remove all foo's, empty value remains assign_hf_value("foo[*].bar", "") # set empty value (ex. lr) ... 1.2.19. assign_hf_value2(hf, xl_value) Assign value to specified header. It is expected header in Authorization format (comma delimiters are not treated as multi-value delimiters). Meaning of the parameters is as follows: * hf_para - Header field value / param to be appended. Format: HFNAME [ [IDX] ] [. PARAM] If asterisk is specified as index then all values are affected. * xl_value - Value to be assigned, xl_lib formatting supported. If value is empty then no equal sign apears in param. Example 19. assign_hf_value2 usage ... assign_hf_value2("Authorization.integrity-protected", "\"yes\"") assign_hf_value2("foo[-1]", "gogo") # foo[last_foo] assign_hf_value2("foo[*].bar", "") # set empty value (ex. lr) ... 1.2.20. include_hf_value(hf, xl_value) Add value in set if not exists, eg. "Supported: path,100rel". Meaning of the parameters is as follows: * hf - Header field name to be affected. * value - xl_lib formatting supported. Example 20. include_hf_value usage ... include_hf_value("Supported", "path"); ... 1.2.21. exclude_hf_value(hf, xl_value) Remove value from set if exists, eg. "Supported: path,100rel". Meaning of the parameters is as follows: * hf - Header field name to be affected. * value - xl_lib formatting supported. Example 21. exclude_hf_value usage ... exclude_hf_value("Supported", "100rel"); ... 1.2.22. hf_value_exists(hf, xl_value) Check if value exists in set. Alternate select @hf_value_exists.HF.VALUE may be used. It returns one or zero. Meaning of the parameters is as follows: * hf - Header field name to be affected. Underscores are treated as dashes. * value - xl_lib formatting supported. Example 22. hf_value_exists usage ... if (hf_value_exists("Supported", "100rel")) { } if (@hf_value_exists.supported.path == "1") { } ... 1.2.23. @hf_value selects Get value of required header-value or param. Note that functions called 'value2' works with Authorization-like headers where comma is not treated as value delimiter. Formats: @hf_value.HFNAME[IDX] # idx value, negative value counts from bottom @hf_value.HFNAME.PARAM_NAME @hf_value.HFNAME[IDX].PARAM_NAME @hf_value.HFNAME.p.PARAM_NAME # or .param., useful if requred called "uri", "p", "param" @hf_value.HFNAME[IDX].p.PARAM_NAME # dtto @hf_value.HFNAME[IDX].uri # (< & > excluded) @hf_value.HFNAME[*] # return comma delimited list of all values (combines headers) @hf_value.HFNAME # the same as above [*] but may be parsed by cfg.y @hf_value.HFNAME[*].uri # return comma delimited list of uris (< & > excluded) @hf_value.HFNAME.uri # the same as above [*] but may be parsed by cfg.y @hf_value.HFNAME[IDX].name # returns name part, quotes excluded @hf_value.HFNAME.name # returns name part of the first value @hf_value2.HFNAME # returns value of first header @hf_value2.HFNAME[IDX] # returns value of idx's header @hf_value2.HFNAME.PARAM_NAME @hf_value2.HFNAME[IDX].PARAM_NAME @hf_value.HFNAME[IDX].uri # return URI, quotes excluded @hf_value.HFNAME.p.uri # returns param named uri, not URI itself @hf_value.HFNAME.p.name # returns param named name, not name itself @hf_value.HFNAME[IDX].uri.name # any sel_any_uri nested features may be used @hf_value.HFNAME[IDX].nameaddr.name # select_any_nameaddr Meaning of the parameters is as follows: * HFNAME - Header field name. Underscores are treated as dashes. * IDX - Value index, negative value counts from bottom * PARAM_NAME - name of parameter Example 23. @hf_value select usage ... $a = @hf_value.my_header[1].my_param; xplog("L_ERR", "%@hf_value.via[-1], %@hf_value.from.tag\n"); $b = @hf_value.p_associated_uri; xplog("L_ERR", "Route uris: '%@hf_value.route[*].uri'\n"); $rr = @hf_value.route.uri; $prt = @hf_value2.authorization.integrity_protected; ... kamailio-4.0.4/obsolete/textops/Makefile0000644000000000000000000000036412223032460016762 0ustar rootroot# $Id$ # # print example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=textops.so LIBS= DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/xcap/0000755000000000000000000000000012223032460014544 5ustar rootrootkamailio-4.0.4/obsolete/xcap/doc/0000755000000000000000000000000012223032460015311 5ustar rootrootkamailio-4.0.4/obsolete/xcap/doc/functions.xml0000644000000000000000000000325212223032460020045 0ustar rootroot
Functions set_xcap_root(string xcap_root) Overrides XCAP root settings for one config script call. The parameter has to be string (AVP not allowed now). xcap_query This function does the XCAP query. It is no accessible from script, it can be called only internaly. It is by default called from XCAP library. fill_xcap_params This function fills internal data structure with XCAP query parameters according to XCAP module settings. It can not be called from script. (Prepared for authentication to XCAP server, ...) XCAP module functions usage This example shows config file overridding XCAP root settings.
kamailio-4.0.4/obsolete/xcap/doc/xcap.xml0000644000000000000000000000353212223032460016771 0ustar rootroot
XCAP module Vaclav Kubart Iptel/Tekelec vaclav.kubart@iptel.org Module for doing XCAP queries. This module covers functions called internaly to access XCAP server. These functions were separated into standalone module to allow simple replacing XCAP queries with queries into database or local filesystem or whatever else. Next reason was to protect other modules from linking with libcurl (implements HTTP) or other such stuff.
Dependencies Libraries libxml2 (development version) - external library for parsing XML documents libcurl (development version) - external library for HTTP queries libcds (internal) libpresence (internal) libxcap (internal) - parsing XCAP documents, ...
Incompatibility This module is not working with TLS module. Fro more information see section "Known problems" in presence handbook.
kamailio-4.0.4/obsolete/xcap/doc/Makefile0000644000000000000000000000012512223032460016747 0ustar rootrootdocs = xcap.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/xcap/doc/params.xml0000644000000000000000000000071312223032460017317 0ustar rootroot
Parameters xcap_root Default settings of XCAP root. It can be overridden by set_xcap_root call in config script.
kamailio-4.0.4/obsolete/xcap/README0000644000000000000000000000443412223032460015431 0ustar rootroot1. XCAP module Vaclav Kubart Iptel/Tekelec __________________________________________________________________ 1.1. Dependencies 1.2. Incompatibility 1.3. Parameters 1.4. Functions Module for doing XCAP queries. This module covers functions called internaly to access XCAP server. These functions were separated into standalone module to allow simple replacing XCAP queries with queries into database or local filesystem or whatever else. Next reason was to protect other modules from linking with libcurl (implements HTTP) or other such stuff. 1.1. Dependencies Libraries * libxml2 (development version) - external library for parsing XML documents * libcurl (development version) - external library for HTTP queries * libcds (internal) * libpresence (internal) * libxcap (internal) - parsing XCAP documents, ... 1.2. Incompatibility This module is not working with TLS module. Fro more information see section "Known problems" in presence handbook. 1.3. Parameters xcap_root Default settings of XCAP root. It can be overridden by set_xcap_root call in config script. 1.4. Functions set_xcap_root(string xcap_root) Overrides XCAP root settings for one config script call. The parameter has to be string (AVP not allowed now). xcap_query This function does the XCAP query. It is no accessible from script, it can be called only internaly. It is by default called from XCAP library. fill_xcap_params This function fills internal data structure with XCAP query parameters according to XCAP module settings. It can not be called from script. (Prepared for authentication to XCAP server, ...) Example 1. XCAP module functions usage This example shows config file overridding XCAP root settings. ... modparam("xcap", "xcap_root", "http://xcap/xcap") ... route { ... if (lookup_domain("To")) { if ($t.did == "domain1") { # change XCAP root for domain1 set_xcap_root("http://xcap/xcap-for-domain1/"); } # else leave default XCAP root and file names } ... kamailio-4.0.4/obsolete/xcap/xcap_mod.h0000644000000000000000000000031512223032460016506 0ustar rootroot#ifndef __XCAP_MOD_H #define __XCAP_MOD_H #include #include "../../parser/msg_parser.h" typedef int (*fill_xcap_params_func)(struct sip_msg *m, xcap_query_params_t *params); #endif kamailio-4.0.4/obsolete/xcap/xcap_params.h0000644000000000000000000000052012223032460017210 0ustar rootroot#ifndef __XCAP_PARAMS_H #define __XCAP_PARAMS_H #include #include "str.h" int fill_xcap_params_impl(struct sip_msg *m, xcap_query_params_t *params); int set_xcap_root(struct sip_msg* m, char *value, char* _x); int set_xcap_filename(struct sip_msg* m, char *value, char* _x); extern str default_xcap_root; #endif kamailio-4.0.4/obsolete/xcap/xcap_mod.c0000644000000000000000000001137012223032460016504 0ustar rootroot#include "../../sr_module.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include #include #include #include #include #include #include #include #include "xcap_params.h" MODULE_VERSION int xcap_mod_init(void); void xcap_mod_destroy(void); int xcap_child_init(int _rank); int xcap_query_impl(const char *uri, xcap_query_params_t *params, char **buf, int *bsize); /** Exported functions */ static cmd_export_t cmds[]={ {"xcap_query", (cmd_function)xcap_query_impl, 0, 0, -1}, {"fill_xcap_params", (cmd_function)fill_xcap_params_impl, 0, 0, -1}, {"set_xcap_root", set_xcap_root, 1, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"set_xcap_filename", set_xcap_filename, 1, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {0, 0, 0, 0, 0} }; /** Exported parameters */ static param_export_t params[]={ {"xcap_root", PARAM_STR, &default_xcap_root }, {0, 0, 0} }; struct module_exports exports = { "xcap", cmds, /* Exported functions */ 0, /* RPC methods */ params, /* Exported parameters */ xcap_mod_init, /* module initialization function */ 0, /* response function*/ xcap_mod_destroy, /* pa_destroy, / * destroy function */ 0, /* oncancel function */ xcap_child_init /* per-child init function */ }; int xcap_mod_init(void) { INFO("xcap module initialization\n"); /* ??? if other module uses this libraries it might be a problem ??? */ DEBUG_LOG(" ... libxml\n"); xmlInitParser(); DEBUG_LOG(" ... libcurl\n"); curl_global_init(CURL_GLOBAL_ALL); return 0; } int xcap_child_init(int _rank) { return 0; } void xcap_mod_destroy(void) { /*int i, cnt; char *s;*/ DEBUG_LOG("xcap module cleanup\n"); /* ??? if other module uses this libraries it might be a problem ??? */ /* xmlCleanupParser(); curl_global_cleanup();*/ DEBUG_LOG("xcap module cleanup finished\n"); } /* --------------------------------------------------------- */ static size_t write_data_func(void *ptr, size_t size, size_t nmemb, void *stream) { int s = size * nmemb; /* TRACE_LOG("%d bytes writen\n", s);*/ if (s != 0) { if (dstr_append((dstring_t*)stream, ptr, s) != 0) { ERROR_LOG("can't append %d bytes into data buffer\n", s); return 0; } } return s; } /* helper functions for XCAP queries */ int xcap_query_impl(const char *uri, xcap_query_params_t *params, char **buf, int *bsize) { CURLcode res = -1; static CURL *handle = NULL; dstring_t data; char *auth = NULL; int i; long auth_methods; if (!uri) { ERR("BUG: no uri given\n"); return -1; } if (!buf) { ERR("BUG: no buf given\n"); return -1; } i = 0; if (params) { i += params->auth_user.len; i += params->auth_pass.len; } if (i > 0) { /* do authentication */ auth = (char *)cds_malloc_pkg(i + 2); if (!auth) return -1; sprintf(auth, "%.*s:%.*s", FMT_STR(params->auth_user), FMT_STR(params->auth_pass)); } auth_methods = CURLAUTH_BASIC | CURLAUTH_DIGEST; dstr_init(&data, 512); if (!handle) handle = curl_easy_init(); if (handle) { curl_easy_setopt(handle, CURLOPT_URL, uri); /* TRACE_LOG("uri: %s\n", uri ? uri : ""); */ /* do not store data into a file - store them in memory */ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_data_func); curl_easy_setopt(handle, CURLOPT_WRITEDATA, &data); #ifdef CURLOPT_MUTE /* be quiet */ curl_easy_setopt(handle, CURLOPT_MUTE, 1); #endif /* CURLOPT_MUTE */ /* non-2xx => error */ curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1); /* auth */ curl_easy_setopt(handle, CURLOPT_HTTPAUTH, auth_methods); /* TODO possibility of selection */ curl_easy_setopt(handle, CURLOPT_NETRC, CURL_NETRC_IGNORED); curl_easy_setopt(handle, CURLOPT_USERPWD, auth); /* SSL */ if (params) { if (params->enable_unverified_ssl_peer) { curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0); } } /* follow redirects (needed for apache mod_speling - case insesitive names) */ curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); /* curl_easy_setopt(handle, CURLOPT_TCP_NODELAY, 1); curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, 10);*/ /* Accept headers */ res = curl_easy_perform(handle); /* curl_easy_cleanup(handle); */ /* FIXME: experimental */ } else ERROR_LOG("can't initialize curl handle\n"); if (res == 0) { *bsize = dstr_get_data_length(&data); if (*bsize) { *buf = (char*)cds_malloc(*bsize); if (!*buf) { ERROR_LOG("can't allocate %d bytes\n", *bsize); res = -1; *bsize = 0; } else dstr_get_data(&data, *buf); } } else DBG("curl error: %d\n", res); /* see curl/curl.h for possible values*/ dstr_destroy(&data); if (auth) cds_free_pkg(auth); return res; } kamailio-4.0.4/obsolete/xcap/ChangeLog0000644000000000000000000000037512223032460016323 0ustar rootroot2006-06-26 * added documentation 2006-04-10 * reduced trace logs 2006-04-07 * using global variables instead of AVPs for modifying parameters like XCAP_ROOT 2006-04-06 * all XCAP queries done from XCAP module 2006-04-05 * created XCAP module kamailio-4.0.4/obsolete/xcap/xcap_params.c0000644000000000000000000000467412223032460017221 0ustar rootroot#include "../../sr_module.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "xcap_params.h" #include #include #include #include /* global variables holding parameters (AVPs not used due to problems with them if * new transactions (sending messages, ...) in processing */ /*static struct sip_msg *msg = NULL;*/ /* if parameters stored and msg is different, the store is reinitialized before */ static unsigned int msgid = 0; static char xcap_root[512] = ""; static char xcap_filename[512] = ""; str default_xcap_root = {s: "", len: 0 }; /* clears all stored parameters */ static void clear_params() { *xcap_root = 0; /* TODO: SSL, auth params, ... */ } static void check_params(struct sip_msg *m) { /* clears all stored parameters if for other message */ if (m) { if ((m->id == msgid)) return; /* don't clean params */ } clear_params(); if (m) msgid = m->id; else msgid = 0; } static void set_param_value(struct sip_msg *m, char *dst, int dst_size, const char *value) { check_params(m); strncpy(dst, value, dst_size - 1); dst[dst_size - 1] = 0; } #if 0 /* AVPs are not used due to problems with them !!! */ static int get_xcap_root(str *dst) { avp_t *avp; int_str name, val; str avp_xcap_root = STR_STATIC_INIT("xcap_root"); /* if (!dst) return -1; */ name.s = avp_xcap_root; avp = search_first_avp(AVP_NAME_STR, name, &val, 0); if (avp) { /* don't use default - use value from AVP */ DBG("redefined xcap_root = %.*s\n", FMT_STR(val.s)); *dst = val.s; } else { *dst = default_xcap_root; } if (is_str_empty(dst)) return -1; return 0; } #endif int fill_xcap_params_impl(struct sip_msg *m, xcap_query_params_t *params) { int res = 0; int use_default = 1; if (!params) return -1; check_params(m); use_default = 1; if (*xcap_root) use_default = 0; if (use_default) params->xcap_root = default_xcap_root; else { params->xcap_root.s = xcap_root; params->xcap_root.len = strlen(xcap_root); } /* ERR("**** XCAP PARAMS: ****\n"); ERR("XCAP ROOT: %.*s\n", FMT_STR(params->xcap_root)); */ /*ERR"(XCAP FILE: %.*s\n", FMT_STR(params->xcap_file); */ return res; } int set_xcap_root(struct sip_msg* m, char* value, char* _x) { set_param_value(m, xcap_root, sizeof(xcap_root), value); return 1; } int set_xcap_filename(struct sip_msg* m, char* value, char* _x) { set_param_value(m, xcap_filename, sizeof(xcap_filename), value); return 1; } kamailio-4.0.4/obsolete/xcap/Makefile0000644000000000000000000000123312223032460016203 0ustar rootroot# $Id$ # # Registrar Presence User Agent # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME = xcap.so # if using libcds, the directive SER must be defined ! # and root ser directory must be in include directories DEFS +=-DSER INCLUDES += -I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include -I../../lib -I../.. LIBS += -L$(LOCALBASE)/lib -L/usr/pkg/lib -lxml2 -lcurl SERLIBPATH=../../lib SER_LIBS=$(SERLIBPATH)/cds/ser_cds # not used: $(SERLIBPATH)/presence/ser_presence $(SERLIBPATH)/xcap/ser_xcap DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/pdt/0000755000000000000000000000000012223032460014400 5ustar rootrootkamailio-4.0.4/obsolete/pdt/pdtree.c0000644000000000000000000001341212223032460016030 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG FOKUS * Copyright (C) 2005 Voice Sistem SRL (Voice-System.RO) * * This file is part of SIP Express Router. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * History: * ------- * 2005-01-25 first tree version (ramona) */ #include #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "pdtree.h" pdt_tree_t* pdt_init_tree() { pdt_tree_t *pt = NULL; pt = (pdt_tree_t*)pkg_malloc(sizeof(pdt_tree_t)); if(pt==NULL) { LOG(L_ERR, "pdt_init_tree:ERROR: no more pkg memory\n"); return NULL; } memset(pt, 0, sizeof(pdt_tree_t)); pt->head = (pdt_node_t*)pkg_malloc(PDT_NODE_SIZE*sizeof(pdt_node_t)); if(pt->head == NULL) { pkg_free(pt); LOG(L_ERR, "pdt_init_tree:ERROR: no more pkg mem\n"); return NULL; } memset(pt->head, 0, PDT_NODE_SIZE*sizeof(pdt_node_t)); return pt; } int pdt_add_to_tree(pdt_tree_t *pt, str *sp, str *sd) { int l; pdt_node_t *itn, *itn0; if(pt==NULL || sp==NULL || sp->s==NULL || sd==NULL || sd->s==NULL) { LOG(L_ERR, "pdt_add_to_tree:ERROR: bad parameters\n"); return -1; } if(sp->len>=PDT_MAX_DEPTH) { LOG(L_ERR, "pdt_add_to_tree:ERROR: max prefix len exceeded\n"); return -1; } l = 0; itn0 = pt->head; itn = itn0[(sp->s[l]-'0')%PDT_NODE_SIZE].child; while(l < sp->len-1) { if(sp->s[l] < '0' || sp->s[l] > '9') { LOG(L_ERR, "pdt_add_to_tree:ERROR: invalid char %d in prefix [%c (0x%x)]\n", l, sp->s[l], sp->s[l]); return -1; } if(itn == NULL) { itn = (pdt_node_t*)pkg_malloc(PDT_NODE_SIZE*sizeof(pdt_node_t)); if(itn == NULL) { LOG(L_ERR, "pdt_add_to_tree: no more pkg mem\n"); return -1; } memset(itn, 0, PDT_NODE_SIZE*sizeof(pdt_node_t)); itn0[(sp->s[l]-'0')%PDT_NODE_SIZE].child = itn; } l++; itn0 = itn; itn = itn0[(sp->s[l]-'0')%PDT_NODE_SIZE].child; } if(itn0[(sp->s[l]-'0')%PDT_NODE_SIZE].domain.s!=NULL) { LOG(L_ERR, "pdt_add_to_tree:ERROR: prefix alredy allocated\n"); return -1; } itn0[(sp->s[l]-'0')%PDT_NODE_SIZE].domain.s = (char*)pkg_malloc((sd->len+1)*sizeof(char)); if(itn0[(sp->s[l]-'0')%PDT_NODE_SIZE].domain.s==NULL) { LOG(L_ERR, "pdt_add_to_tree:ERROR: no more pkg mem!\n"); return -1; } strncpy(itn0[(sp->s[l]-'0')%PDT_NODE_SIZE].domain.s, sd->s, sd->len); itn0[(sp->s[l]-'0')%PDT_NODE_SIZE].domain.len = sd->len; itn0[(sp->s[l]-'0')%PDT_NODE_SIZE].domain.s[sd->len] = '\0'; return 0; } int pdt_remove_from_tree(pdt_tree_t *pt, str *sp) { int l; pdt_node_t *itn; if(pt==NULL || sp==NULL || sp->s==NULL || sp->s<=0) { LOG(L_ERR, "pdt_remove_from_tree:ERROR: bad parameters\n"); return -1; } l = 1; itn = pt->head; while(itn!=NULL && l < sp->len && l < PDT_MAX_DEPTH) { itn = itn[(sp->s[l-1]-'0')%PDT_NODE_SIZE].child; l++; } if(itn!=NULL && l==sp->len && itn[(sp->s[l-1]-'0')%PDT_NODE_SIZE].domain.s!=NULL) { DBG("pdt_remove_from_tree: deleting <%.*s>\n", itn[(sp->s[l-1]-'0')%PDT_NODE_SIZE].domain.len, itn[(sp->s[l-1]-'0')%PDT_NODE_SIZE].domain.s); pkg_free(itn[(sp->s[l-1]-'0')%PDT_NODE_SIZE].domain.s); itn[(sp->s[l-1]-'0')%PDT_NODE_SIZE].domain.s = NULL; itn[(sp->s[l-1]-'0')%PDT_NODE_SIZE].domain.len = 0; } /* todo: should free the node if no child and prefix inside */ return 0; } str* pdt_get_domain(pdt_tree_t *pt, str *sp, int *plen) { int l, len; pdt_node_t *itn; str *domain; if(pt==NULL || sp==NULL || sp->s==NULL) { LOG(L_ERR, "pdt_get_domain:ERROR: bad parameters\n"); return NULL; } l = len = 0; itn = pt->head; domain = NULL; while(itn!=NULL && l < sp->len && l < PDT_MAX_DEPTH) { if(itn[(sp->s[l]-'0')%PDT_NODE_SIZE].domain.s!=NULL) { domain = &itn[(sp->s[l]-'0')%PDT_NODE_SIZE].domain; len = l+1; } itn = itn[(sp->s[l]-'0')%PDT_NODE_SIZE].child; l++; } if(plen!=NULL) *plen = len; return domain; } void pdt_free_node(pdt_node_t *pn) { int i; if(pn==NULL) return; for(i=0; ihead); pkg_free(pt); pt = NULL; return; } int pdt_print_node(pdt_node_t *pn, char *code, int len) { int i; if(pn==NULL || code==NULL || len>=PDT_MAX_DEPTH) return 0; for(i=0; ihead, code_buf, len); } kamailio-4.0.4/obsolete/pdt/doc/0000755000000000000000000000000012223032460015145 5ustar rootrootkamailio-4.0.4/obsolete/pdt/doc/pdt.sql0000644000000000000000000000034012223032460016452 0ustar rootroot-- SQL script for PDT module DROP DATABASE IF EXISTS pdt; CREATE DATABASE pdt; USE pdt; -- create table CREATE TABLE prefix_domain ( prefix VARCHAR(32) NOT NULL PRIMARY KEY, domain VARCHAR(255) NOT NULL DEFAULT "" ); kamailio-4.0.4/obsolete/pdt/doc/admin.sql0000644000000000000000000000051512223032460016757 0ustar rootroot-- SQL script for PDT module USE pdt; -- create table CREATE TABLE admin( name VARCHAR(20) NOT NULL PRIMARY KEY, passwd VARCHAR(20) NOT NULL DEFAULT "" ); CREATE TABLE authentication ( username VARCHAR(50) NOT NULL, domain VARCHAR(50) NOT NULL, passwd_h VARCHAR(32) NOT NULL ); INSERT INTO admin VALUES ("admin", "admin"); kamailio-4.0.4/obsolete/pdt/doc/pdt_fifo_delete.sh0000755000000000000000000000012312223032460020614 0ustar rootrootcat /tmp/ser_reply & cat > /tmp/ser_fifo << EOF :pdt_delete:ser_reply 123.com EOF kamailio-4.0.4/obsolete/pdt/doc/pdt_fifo_add.sh0000755000000000000000000000012312223032460020102 0ustar rootrootcat /tmp/ser_reply & cat > /tmp/ser_fifo << EOF :pdt_add:ser_reply 55 123.com EOF kamailio-4.0.4/obsolete/pdt/doc/web/0000755000000000000000000000000012223032460015722 5ustar rootrootkamailio-4.0.4/obsolete/pdt/doc/web/index.php0000644000000000000000000000207712223032460017550 0ustar rootroot

Prefix Domain Translation -- User Interface


User
Password
Domain Name
Domain Port
kamailio-4.0.4/obsolete/pdt/doc/web/request.php0000644000000000000000000000575412223032460020136 0ustar rootroot

Prefix Domain Translation -- UserInterface


No password supplied"; exit; } $link = mysql_connect($host, $user, $pass) or die("Could not connect to mysql"); mysql_select_db($database) or die("Could not select database"); $query = "SELECT * FROM ".$table." WHERE name=\"".$admin."\" and passwd=\"".$passwd."\""; $result = mysql_query($query) or die("Query failed: ".mysql_error()); $num_rows = mysql_num_rows($result); if($num_rows>0) $authorized="1"; else echo "

Authentication failed. No right to register a new domain.

"; mysql_free_result($result); mysql_close($link); } $response_file = "rf".session_id(); $reply = "/tmp/".$response_file; @system("mkfifo -m 666 ".$reply); $new_line ="\n"; $fifo_command = ":get_domainprefix:"; $fifo_command = $fifo_command.$response_file.$new_line; $fifo_command = $fifo_command.$domain_name; if($domain_port) $fifo_command = $fifo_command.":".$domain_port; $fifo_command = $fifo_command.$new_line; $fifo_command = $fifo_command.$authorized.$new_line.$new_line; $fp = fopen($input_file, "w"); if(!$fp) { echo "Cannot open fifo
"; exit; } if( fwrite($fp, $fifo_command) == -1) { @unlink($reply); @fclose($fp); echo "fifo writing error
"; exit; } fclose($fp); $fr = fopen($reply, "r"); if(!$fr) { @unlink($reply); echo "Cannot open reply file"; exit; } $count = 1000; $str = fread($fr, $count); if(!$str) { @fclose($fr); @unlink($reply); echo "response fifo reading error"; exit; } $domain_code = ""; list($return_code, $description) = explode("|", $str); if(!strcmp("$return_code","400 ")) { echo "

ERROR: Cannot read from fifo. Try again.

"; exit; } list($garbage1, $garbage2, $domain_code) = explode("=", $str); list($domain_code, $garbage3) = explode("\n", $domain_code); fclose($fr); @unlink("/tmp/".$response_file); if(!strcmp("$return_code","204 ")) { $domain_code = "registration failed"; } if(!strcmp("$return_code","203 ")) { $domain_code = "not registered"; } ?>
Domain Name
Domain Code
kamailio-4.0.4/obsolete/pdt/doc/functions.xml0000644000000000000000000000241112223032460017675 0ustar rootroot
Functions
<function>prefix2domain(mode)</function> Build a new URI if it is necessary. Returns 1 when the translation was made or there was nothing to translate (user part of the URI is empty, it does not match the prefix parameter or there is no domain associated with a possible prefix from user part). Returns -1 in error cases. The "mode" parameter specifies whether to strip or not the prefix from user part. If the parameter is missing or it has the value "0", then the prefix is removed along with the leading prefix. If the value is "1", only the leading prefix is removed. If the values is "2" the user part of the URI is not changed. <function>prefix2domain</function> usage ... prefix2domain(); ... prefix2domain("2"); ...
kamailio-4.0.4/obsolete/pdt/doc/fifo.sh0000755000000000000000000000011212223032460016421 0ustar rootrootcat > /tmp/ser_fifo << EOF :get_domaincode:response_fifo iptel.org 1 EOF kamailio-4.0.4/obsolete/pdt/doc/pdt.xml0000644000000000000000000000543412223032460016464 0ustar rootroot
Elena-Ramona Modroiu Asipto
ramona@asipto.com
2003 FhG FOKUS
PDT module
Overview This module translates a numerical prefix into a domain and updates accordingly the request URI. The module looks up at the Request-URI part of a message and if the user part begins with an established prefix it will update the URI. Updating the uri consists of: remove the prefix from the user part of the uri and keep the rest as the user part of the new uri. The host and port are changed with the domain matched for the leading prefix. <prefix><userid><:password>@<mydomain.com> ... and the result will be: <userid><:password>@<domain[:port]>... prefix-domain translation prefix=123 domain[123]=alpha.org sip:12391001@mydomain.com => sip:91001@alpha.org The prefix could be prefixed by other digits. These digits will not be used to look up the domain (the classic example, 00 used for international calls, then follows the country prefix). For more information on this, see 'prefix' parameter. A sample config file and the MySQL script to create the database needed by PDT are located in './doc/'. Sample shell scripts to manage prefix-domain pairs are also located in './doc/' (pdt_fifo_add.sh, pdt_fifo_delete.sh, pdt_fifo_list.sh).
Dependencies
SER Modules The following modules must be loaded before this module: A SER database module (e.g., mysql, dbtext).
kamailio-4.0.4/obsolete/pdt/doc/pdt_fifo_list.sh0000755000000000000000000000011512223032460020326 0ustar rootrootcat /tmp/ser_reply & cat > /tmp/ser_fifo << EOF :pdt_list:ser_reply 1 . EOF kamailio-4.0.4/obsolete/pdt/doc/fifo.xml0000644000000000000000000000230112223032460016606 0ustar rootroot
FIFO Interface The modules uses only the cache to look up domains. If you want to add or delete a new prefix-domain pair you have to use FIFO commands. All changes made via FIFO are applied to database. The database is loaded only at SER start up time. There are three FIFO commands to use with PDT. pdt_add - add a new prefix-domain pair pdt_delete - remove a prefix-domain pair pdt_list - list the prefixes and the domains Example shell scripts for these commands are placed in './doc/' (pdt_fifo_add.sh, pdt_fifo_delete.sh, pdt_fifo_list.sh). More about, in the comments before the implementation of the functions, inside the 'pdt.c' file.
kamailio-4.0.4/obsolete/pdt/doc/pdt.cfg0000644000000000000000000000446512223032460016426 0ustar rootroot# # $Id$ # # simple quick-start config script # # ----------- global configuration parameters ------------------------ debug=9 # debug level (cmd line: -dddddddddd) fork=no log_stderror=yes# (cmd line: -E) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 fifo="/tmp/ser_fifo" listen=193.175.135.170 # ------------------ module loading ---------------------------------- # Uncomment this if you want to use SQL database loadmodule "modules/mysql/mysql.so" loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" loadmodule "modules/rr/rr.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/pdt/pdt.so" # Uncomment this if you want digest authentication # mysql.so must be loaded ! #loadmodule "/usr/lib/ser/modules/auth.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- modparam("usrloc", "db_mode", 0) # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line #modparam("usrloc", "db_mode", 2) # -- auth params -- # Uncomment if you are using auth module # #modparam("auth", "secret", "alsdkhglaksdhfkloiwr") #modparam("auth", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) # #modparam("auth", "password_column", "password") modparam("pdt", "db_url", "mysql://root@localhost/pdt") modparam("pdt", "db_table", "domains") modparam("pdt", "prefix", "2") modparam("pdt", "start_range", 2000) modparam("pdt", "hsize_2pow", 2) # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwars==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (len_gt( max_len )) { sl_send_reply("513", "Message too big"); break; }; loose_route(); prefix2domain(); record_route(); # forward to current uri now if (!t_relay()) { sl_reply_error(); }; } kamailio-4.0.4/obsolete/pdt/doc/Makefile0000644000000000000000000000012412223032460016602 0ustar rootrootdocs = pdt.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/pdt/doc/params.xml0000644000000000000000000001005212223032460017150 0ustar rootroot
Parameters
<varname>db_url</varname> (string) SQL URL of database--username, password, host, port and database (ex: mysql://username:password@hostname.com/database) Default value is 'mysql://root@127.0.0.1/pdt'. Set <varname>db_url</varname> parameter ... modparam("pdt", "db_url", "mysql://user:xxx@127.0.0.1/ser") ...
<varname>db_table</varname> (string) Table name. Default value is "prefix_domain". Set <varname>db_table</varname> parameter ... modparam("pdt", "db_table", "pdt") ...
<varname>prefix_column</varname> (string) Name of 'prefix' column. Default value is "prefix". Set <varname>prefix_column</varname> parameter ... modparam("pdt", "prefix_column", "code") ...
<varname>domain_column</varname> (string) Name of 'domain' column. Default value is "domain". Set <varname>domain_column</varname> parameter ... modparam("pdt", "domain_column", "hostname") ...
<varname>prefix</varname> (string) Default leading prefix who denotes what URI needs to be translated - if it is NULL the module will not check the Request-URI; against it and the PDT prefix is considered starting from the first digit. Otherwise, the module will check first if the Request-URI starts with it and will skip it to look up the domain. Default value is NULL. Set <varname>prefix</varname> parameter ... modparam("pdt", "prefix", "00") ...
<varname>hsize_2pow</varname> (integer) Number of the hash entries = 2^hash_size. Default value is 4. Set <varname>hsize_2pow</varname> parameter ... modparam("pdt", "hsize_2pow", 4) ...
<varname>sync_time</varname> (integer) Time in seconds to synchronize the cache of each process with the changes made through FIFO. Any prefix-domain change made through FIFO is guaranteed to have efect after this period of time past. Default value is 600. Set <varname>sync_time</varname> parameter ... modparam("pdt", "sync_time", 300) ...
<varname>clean_time</varname> (integer) Time in seconds to clean the changes inserted via FIFO. The changes will be removed from FIFO diff list only when all SER processes applied these changes. Default value is 900. Set <varname>clean_time</varname> parameter ... modparam("pdt", "clean_time", 600) ...
kamailio-4.0.4/obsolete/pdt/domains.c0000644000000000000000000002275212223032460016206 0ustar rootroot/** * $Id$ * * Copyright (C) 2001-2003 FhG FOKUS * Copyright (C) 2008 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * History * ------- * 2003-04-07: a structure for both hashes introduced (ramona) * 2005-01-26: double hash removed (ramona) * FIFO operations are kept as a diff list (ramona) * domain hash kept in share memory along with FIFO ops (ramona) * */ #include #include #include "../../sr_module.h" #include "../../parser/parse_fline.h" #include "../../lib/srdb2/db.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "domains.h" pd_op_t* new_pd_op(pd_t *cell, int id, int op) { pd_op_t *pdo; if(cell==NULL) { LOG(L_ERR, "PDT:new_pd_op: bad parameters\n"); return NULL; } pdo = (pd_op_t*)shm_malloc(sizeof(pd_op_t)); if(pdo==NULL) { LOG(L_ERR, "PDT:new_pd_op: no more shm\n"); return NULL; } memset(pdo, 0, sizeof(pd_op_t)); pdo->cell = cell; pdo->id = id; pdo->op = op; return pdo; } void free_pd_op(pd_op_t *pdo) { if(pdo==NULL) return; free_cell(pdo->cell); shm_free(pdo); pdo = NULL; return; } pd_t* new_cell(str* p, str *d) { pd_t* cell = NULL; if(p==NULL || p->s==NULL || d==NULL || d->s==NULL) { LOG(L_ERR, "PDT:new_cell: bad parameters\n"); return NULL; } /* the cell is in share memory */ cell = (pd_t*)shm_malloc(sizeof(pd_t)); /* if there is no space return just NULL */ if(cell==NULL) { LOG(L_ERR, "PDT:new_cell: no more shm memory.\n"); return NULL; } memset(cell, 0, sizeof(pd_t)); cell->prefix.s = (char*)shm_malloc((1+p->len)*sizeof(char)); if(cell->prefix.s==NULL) { shm_free(cell); LOG(L_ERR, "PDT:new_cell: no more shm memory\n"); return NULL; } strncpy(cell->prefix.s, p->s, p->len); cell->prefix.len = p->len; cell->prefix.s[p->len] = '\0'; cell->domain.s = (char*)shm_malloc((1+d->len)*sizeof(char)); if(cell->domain.s==NULL) { shm_free(cell->prefix.s); shm_free(cell); LOG(L_ERR, "PDT:new_cell: no more shm memory!\n"); return NULL; } strncpy(cell->domain.s, d->s, d->len); cell->domain.len = d->len; cell->domain.s[d->len] = '\0'; cell->dhash = pdt_compute_hash(cell->domain.s); /* return the newly allocated in share memory cell */ return cell; } void free_cell(pd_t* cell) { if(cell==NULL) return; if(cell->prefix.s) shm_free(cell->prefix.s); if(cell->domain.s) shm_free(cell->domain.s); shm_free(cell); } /* returns a pointer to a hashtable */ pd_entry_t* init_hash(unsigned int hash_size) { int i, j; pd_entry_t *hash = NULL; /* space for the hash is allocated in share memory */ hash = (pd_entry_t*)shm_malloc(hash_size*sizeof(pd_entry_t)); if(hash == NULL) { LOG(L_ERR, "PDT:init_hash: no more shm\n"); return NULL; } memset(hash, 0, hash_size*sizeof(pd_entry_t)); /* create mutex semaphores for each entry of the hash */ for(i=0; iMAX_HSIZE_TWO_POW || hs_two_pow<0) hash_size = MAX_HASH_SIZE; else hash_size = 1<diff_lock) == 0) { shm_free(hash); LOG(L_ERR, "PDT:pdt_init_hash: cannot init the diff lock\n"); return NULL; } if( (hash->dhash = init_hash(hash_size)) == NULL ) { lock_destroy(&hash->diff_lock); shm_free(hash); LOG(L_ERR, "PDT:pdt_init_hash: no more shm!\n"); return NULL; } hash->hash_size = hash_size; return hash; } void free_hash(pd_entry_t* hash, unsigned int hash_size) { int i; pd_t *tmp, *it; if(hash==NULL || hash_size<=0) return; for(i=0; in; free_cell(it); it = tmp; } lock_destroy(&hash[i].lock); } shm_free(hash); } void pdt_free_hash(pdt_hash_t* hash) { free_hash(hash->dhash, hash->hash_size); lock_destroy(&hash->diff_lock); /* todo: destroy diff list */ shm_free(hash); } int pdt_add_to_hash(pdt_hash_t *hash, str *sp, str *sd) { int hash_entry=0; unsigned int dhash; pd_t *it, *tmp; pd_t *cell; if(hash==NULL || sp==NULL || sd==NULL) { LOG(L_ERR, "PDT:pdt_add_to_hash: bad parameters\n"); return -1; } dhash = pdt_compute_hash(sd->s); hash_entry = get_hash_entry(dhash, hash->hash_size); lock_get(&hash->dhash[hash_entry].lock); it = hash->dhash[hash_entry].e; tmp = NULL; while(it!=NULL && it->dhash < dhash) { tmp = it; it = it->n; } /* we need a new entry for this cell */ cell = new_cell(sp, sd); if(cell == NULL) { lock_release(&hash->dhash[hash_entry].lock); return -1; } if(tmp) tmp->n=cell; else hash->dhash[hash_entry].e = cell; cell->p=tmp; cell->n=it; if(it) it->p=cell; lock_release(&hash->dhash[hash_entry].lock); return 0; } int pdt_remove_from_hash(pdt_hash_t *hash, str *sd) { int hash_entry=0; unsigned int dhash; pd_t *it, *tmp; if(sd==NULL) return 0; if(hash==NULL) { LOG(L_ERR, "PDT:pdt_remove_from_hash: bad parameters\n"); return -1; } /* find the list where the cell must be */ dhash = pdt_compute_hash(sd->s); hash_entry = get_hash_entry(dhash, hash->hash_size); lock_get(&hash->dhash[hash_entry].lock); /* first element of the list */ it = hash->dhash[hash_entry].e; /* find the cell in the list */ /* a double linked list in the hash is kept alphabetically * or numerical ordered */ tmp = NULL; while(it!=NULL) { if( it->dhash==dhash && it->domain.len==sd->len && strncasecmp(it->domain.s, sd->s, sd->len)==0) break; tmp = it; it = it->n; } if(it!=NULL) { if(tmp!=NULL) tmp->n = it->n; else hash->dhash[hash_entry].e = it->n; if(it->n) it->n->p = it->p; free_cell(it); } lock_release(&hash->dhash[hash_entry].lock); return 0; } str* pdt_get_prefix(pdt_hash_t *ph, str* sd) { int hash_entry; unsigned int dhash; pd_t* it; if(ph==NULL || ph->dhash==NULL || ph->hash_size>MAX_HASH_SIZE) { LOG(L_ERR, "PDT:pdt_get_prefix: bad parameters\n"); return NULL; } dhash = pdt_compute_hash(sd->s); hash_entry = get_hash_entry(dhash, ph->hash_size); lock_get(&ph->dhash[hash_entry].lock); it = ph->dhash[hash_entry].e; while(it!=NULL && it->dhash<=dhash) { if(it->dhash==dhash && it->domain.len==sd->len && strncasecmp(it->domain.s, sd->s, sd->len)==0) { lock_release(&ph->dhash[hash_entry].lock); return &it->prefix; } it = it->n; } lock_release(&ph->dhash[hash_entry].lock); return NULL; } int pdt_check_pd(pdt_hash_t *ph, str *sp, str *sd) { int i; unsigned int dhash; pd_t* it; if(ph==NULL || sp==NULL || sd==NULL) { LOG(L_ERR, "PDT:pdt_check_pd: bad parameters\n"); return -1; } dhash = pdt_compute_hash(sd->s); for(i=0; ihash_size; i++) { lock_get(&ph->dhash[i].lock); it = ph->dhash[i].e; while(it != NULL) { if((it->domain.len==sd->len && strncasecmp(it->domain.s, sd->s, sd->len)==0) || (it->prefix.len==sp->len && strncasecmp(it->prefix.s, sp->s, sp->len)==0)) { lock_release(&ph->dhash[i].lock); return 1; } it = it->n; } lock_release(&ph->dhash[i].lock); } return 0; } void pdt_print_hash(pdt_hash_t* hash) { int i, count; pd_t *it; if(hash==NULL) { DBG("PDT:pdt_print_hash: empty...\n"); return; } for(i=0; ihash_size; i++) { lock_get(&hash->dhash[i].lock); it = hash->dhash[i].e; DBG("PDT:pdt_print_hash: entry<%d>:\n", i); count = 0; while(it!=NULL) { DBG("PDT:pdt_print_hash: |Domain: %.*s |Code: %.*s | DHash:%u \n", it->domain.len, it->domain.s, it->prefix.len, it->prefix.s, it->dhash); it = it->n; count++; } lock_release(&hash->dhash[i].lock); DBG("PDT:pdt_print_hash: ---- has %d records\n\n", count); } } /* be sure s!=NULL */ /* compute a hash value for a string, knowing also the hash dimension */ unsigned int pdt_compute_hash(char* s) { #define h_inc h+=v^(v>>3); char* p; register unsigned v; register unsigned h; int len; len = strlen(s); h=0; for(p=s; p<=(s+len-4); p+=4) { v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; h_inc; } v=0; for(;p<(s+len); p++) { v<<=8; v+=*p; } h_inc; return h; } kamailio-4.0.4/obsolete/pdt/README0000644000000000000000000001272512223032460015267 0ustar rootroot1. PDT module Elena-Ramona Modroiu Asipto Copyright © 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. Dependencies 1.2.1. SER Modules 1.3. Parameters 1.3.1. db_url (string) 1.3.2. db_table (string) 1.3.3. prefix_column (string) 1.3.4. domain_column (string) 1.3.5. prefix (string) 1.3.6. hsize_2pow (integer) 1.3.7. sync_time (integer) 1.3.8. clean_time (integer) 1.4. Functions 1.4.1. prefix2domain(mode) 1.5. FIFO Interface 1.1. Overview This module translates a numerical prefix into a domain and updates accordingly the request URI. The module looks up at the Request-URI part of a message and if the user part begins with an established prefix it will update the URI. Updating the uri consists of: remove the prefix from the user part of the uri and keep the rest as the user part of the new uri. The host and port are changed with the domain matched for the leading prefix. <:password>@ ... and the result will be: <:password>@... Example 1. prefix-domain translation prefix=123 domain[123]=alpha.org sip:12391001@mydomain.com => sip:91001@alpha.org The prefix could be prefixed by other digits. These digits will not be used to look up the domain (the classic example, 00 used for international calls, then follows the country prefix). For more information on this, see 'prefix' parameter. * A sample config file and the MySQL script to create the database needed by PDT are located in './doc/'. * Sample shell scripts to manage prefix-domain pairs are also located in './doc/' (pdt_fifo_add.sh, pdt_fifo_delete.sh, pdt_fifo_list.sh). 1.2. Dependencies 1.2.1. SER Modules The following modules must be loaded before this module: * A SER database module (e.g., mysql, dbtext). 1.3. Parameters 1.3.1. db_url (string) SQL URL of database--username, password, host, port and database (ex: mysql://username:password@hostname.com/database) Default value is 'mysql://root@127.0.0.1/pdt'. Example 2. Set db_url parameter ... modparam("pdt", "db_url", "mysql://user:xxx@127.0.0.1/ser") ... 1.3.2. db_table (string) Table name. Default value is "prefix_domain". Example 3. Set db_table parameter ... modparam("pdt", "db_table", "pdt") ... 1.3.3. prefix_column (string) Name of 'prefix' column. Default value is "prefix". Example 4. Set prefix_column parameter ... modparam("pdt", "prefix_column", "code") ... 1.3.4. domain_column (string) Name of 'domain' column. Default value is "domain". Example 5. Set domain_column parameter ... modparam("pdt", "domain_column", "hostname") ... 1.3.5. prefix (string) Default leading prefix who denotes what URI needs to be translated - if it is NULL the module will not check the Request-URI; against it and the PDT prefix is considered starting from the first digit. Otherwise, the module will check first if the Request-URI starts with it and will skip it to look up the domain. Default value is NULL. Example 6. Set prefix parameter ... modparam("pdt", "prefix", "00") ... 1.3.6. hsize_2pow (integer) Number of the hash entries = 2^hash_size. Default value is 4. Example 7. Set hsize_2pow parameter ... modparam("pdt", "hsize_2pow", 4) ... 1.3.7. sync_time (integer) Time in seconds to synchronize the cache of each process with the changes made through FIFO. Any prefix-domain change made through FIFO is guaranteed to have efect after this period of time past. Default value is 600. Example 8. Set sync_time parameter ... modparam("pdt", "sync_time", 300) ... 1.3.8. clean_time (integer) Time in seconds to clean the changes inserted via FIFO. The changes will be removed from FIFO diff list only when all SER processes applied these changes. Default value is 900. Example 9. Set clean_time parameter ... modparam("pdt", "clean_time", 600) ... 1.4. Functions 1.4.1. prefix2domain(mode) Build a new URI if it is necessary. Returns 1 when the translation was made or there was nothing to translate (user part of the URI is empty, it does not match the prefix parameter or there is no domain associated with a possible prefix from user part). Returns -1 in error cases. The "mode" parameter specifies whether to strip or not the prefix from user part. If the parameter is missing or it has the value "0", then the prefix is removed along with the leading prefix. If the value is "1", only the leading prefix is removed. If the values is "2" the user part of the URI is not changed. Example 10. prefix2domain usage ... prefix2domain(); ... prefix2domain("2"); ... 1.5. FIFO Interface The modules uses only the cache to look up domains. If you want to add or delete a new prefix-domain pair you have to use FIFO commands. All changes made via FIFO are applied to database. The database is loaded only at SER start up time. There are three FIFO commands to use with PDT. * pdt_add - add a new prefix-domain pair * pdt_delete - remove a prefix-domain pair * pdt_list - list the prefixes and the domains Example shell scripts for these commands are placed in './doc/' (pdt_fifo_add.sh, pdt_fifo_delete.sh, pdt_fifo_list.sh). More about, in the comments before the implementation of the functions, inside the 'pdt.c' file. kamailio-4.0.4/obsolete/pdt/domains.h0000644000000000000000000000440212223032460016203 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG FOKUS * Copyright (C) 2008 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _DOMAINS_H_ #define _DOMAINS_H_ #include "../../str.h" #define MAX_HSIZE_TWO_POW 20 #define MAX_HASH_SIZE 1< #include #include #include "../../sr_module.h" #include "../../lib/srdb2/db.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "../../parser/parse_uri.h" #include "../../timer.h" #include "../../ut.h" #include "../../rpc.h" #include "../../action.h" #include "domains.h" #include "pdtree.h" MODULE_VERSION #define NR_KEYS 2 int hs_two_pow = 2; /** structure containing prefix-domain pairs */ pdt_hash_t *_dhash = NULL; pdt_tree_t *_ptree = NULL; time_t last_sync; /** database connection */ static db_ctx_t* ctx = NULL; static db_cmd_t* db_load = NULL; static db_cmd_t* db_insert = NULL; static db_cmd_t* db_delete = NULL; static db_cmd_t* db_del_domain = NULL; /** parameters */ static char *db_url = DEFAULT_DB_URL; char *db_table = "pdt"; char *prefix_column = "prefix"; char *domain_column = "domain"; /** pstn prefix */ str prefix = STR_STATIC_INIT(""); int sync_time = 600; int clean_time = 900; static int w_prefix2domain(struct sip_msg* msg, char* str1, char* str2); static int w_prefix2domain_1(struct sip_msg* msg, char* mode, char* str2); static int mod_init(void); static void mod_destroy(void); static int child_init(int r); static int prefix2domain(struct sip_msg*, int mode); int update_new_uri(struct sip_msg *msg, int plen, str *d, int mode); int pdt_load_db(); int pdt_sync_cache(); void pdt_clean_cache(unsigned int ticks, void *param); static rpc_export_t pdt_rpc[]; static cmd_export_t cmds[]={ {"prefix2domain", w_prefix2domain, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {"prefix2domain", w_prefix2domain_1, 1, 0, REQUEST_ROUTE|FAILURE_ROUTE}, {0, 0, 0, 0, 0} }; static param_export_t params[]={ {"db_url", PARAM_STRING, &db_url}, {"db_table", PARAM_STRING, &db_table}, {"prefix_column", PARAM_STRING, &prefix_column}, {"domain_column", PARAM_STRING, &domain_column}, {"prefix", PARAM_STR, &prefix}, {"hsize_2pow", PARAM_INT, &hs_two_pow}, {"sync_time", PARAM_INT, &sync_time}, {"clean_time", PARAM_INT, &clean_time}, {0, 0, 0} }; struct module_exports exports = { "pdt", cmds, pdt_rpc, /* RPC methods */ params, mod_init, /* module initialization function */ 0, /* response function */ mod_destroy, /* destroy function */ 0, /* oncancel function */ child_init /* per child init function */ }; static void pdt_db_close(void) { if (db_load) db_cmd_free(db_load); db_load = NULL; if (db_insert) db_cmd_free(db_insert); db_insert = NULL; if (db_delete) db_cmd_free(db_delete); db_delete = NULL; if (db_del_domain) db_cmd_free(db_del_domain); db_del_domain = NULL; if (ctx) { db_disconnect(ctx); db_ctx_free(ctx); ctx = NULL; } } static int pdt_db_init(void) { db_fld_t fields[] = { {.name = prefix_column, .type = DB_STR}, {.name = domain_column, .type = DB_STR}, {.name = 0} }; db_fld_t del_dom_param[] = { {.name = domain_column, .type = DB_STR}, {.name = 0} }; ctx = db_ctx("pdt"); if (!ctx) goto error; if (db_add_db(ctx, db_url) < 0) goto error; if (db_connect(ctx) < 0) goto error; db_load = db_cmd(DB_GET, ctx, db_table, fields, NULL, NULL); if (!db_load) goto error; db_insert = db_cmd(DB_PUT, ctx, db_table, NULL, NULL, fields); if (!db_insert) goto error; db_delete = db_cmd(DB_DEL, ctx, db_table, NULL, fields, NULL); if (!db_delete) goto error; db_del_domain = db_cmd(DB_DEL, ctx, db_table, NULL, del_dom_param, NULL); if (!db_del_domain) goto error; return 0; error: pdt_db_close(); ERR("pdt: Error while initializing database layer\n"); return -1; } /* * init module function */ static int mod_init(void) { DBG("PDT: initializing...\n"); if(hs_two_pow<0) { LOG(L_ERR, "PDT:mod_init: hash_size_two_pow must be" " positive and less than %d\n", MAX_HSIZE_TWO_POW); return -1; } prefix.len = strlen(prefix.s); if (pdt_db_init() < 0) return -1; /* init the hash and tree in share memory */ if( (_dhash = pdt_init_hash(hs_two_pow)) == NULL) { LOG(L_ERR, "PDT:mod_init: domain hash could not be allocated\n"); goto error1; } if( (_ptree = pdt_init_tree()) == NULL) { LOG(L_ERR, "PDT:mod_init: prefix tree could not be allocated\n"); goto error2; } /* loading all information from database */ if(pdt_load_db()!=0) { LOG(L_ERR, "PDT:mod_init: cannot load info from database\n"); goto error3; } pdt_db_close(); pdt_print_tree(_ptree); DBG("PDT:mod_init: -------------------\n"); pdt_print_hash(_dhash); last_sync = time(NULL); register_timer(pdt_clean_cache, 0, clean_time); /* success code */ return 0; error3: if(_ptree!=NULL) { pdt_free_tree(_ptree); _ptree = 0; } error2: if(_dhash!=NULL) { pdt_free_hash(_dhash); _dhash = 0; } error1: pdt_db_close(); return -1; } /* each child get a new connection to the database */ static int child_init(int rank) { DBG("PDT:child_init #%d / pid <%d>\n", rank, getpid()); if(rank>0) { if(_dhash==NULL) { LOG(L_ERR,"PDT:child_init #%d: ERROR no domain hash\n", rank); return -1; } lock_get(&_dhash->diff_lock); _dhash->workers++; lock_release(&_dhash->diff_lock); } else { if(_ptree!=NULL) { pdt_free_tree(_ptree); _ptree = 0; } } if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN) return 0; /* do nothing for the main or tcp_main processes */ if (pdt_db_init() < 0) return -1; if(sync_time<=0) sync_time = 300; sync_time += rank%60; DBG("PDT:child_init #%d: Database connection opened successfully\n",rank); return 0; } static void mod_destroy(void) { DBG("PDT: mod_destroy : Cleaning up\n"); if (_dhash!=NULL) pdt_free_hash(_dhash); if (_ptree!=NULL) pdt_free_tree(_ptree); pdt_db_close(); } static int w_prefix2domain(struct sip_msg* msg, char* str1, char* str2) { return prefix2domain(msg, 0); } static int w_prefix2domain_1(struct sip_msg* msg, char* mode, char* str2) { if(mode!=NULL && *mode=='1') return prefix2domain(msg, 1); else if(mode!=NULL && *mode=='2') return prefix2domain(msg, 2); else return prefix2domain(msg, 0); } /* change the r-uri if it is a PSTN format */ static int prefix2domain(struct sip_msg* msg, int mode) { str p; str *d; time_t crt_time; int plen; if(msg==NULL) { LOG(L_ERR,"PDT:prefix2domain: weird error\n"); return -1; } /* parse the uri, if not yet */ if(msg->parsed_uri_ok==0) if(parse_sip_msg_uri(msg)<0) { LOG(L_ERR,"PDT:prefix2domain: ERROR while parsing the R-URI\n"); return -1; } /* if the user part begin with the prefix for PSTN users, extract the code*/ if (msg->parsed_uri.user.len<=0) { DBG("PDT:prefix2domain: user part of the message is empty\n"); return 1; } if(prefix.len>0 && prefix.len < msg->parsed_uri.user.len && strncasecmp(prefix.s, msg->parsed_uri.user.s, prefix.len)!=0) { DBG("PDT:prefix2domain: PSTN prefix did not matched\n"); return 1; } p.s = msg->parsed_uri.user.s + prefix.len; p.len = msg->parsed_uri.user.len - prefix.len; /* check if need for sync */ crt_time = time(NULL); if(last_sync + sync_time < crt_time) { last_sync = crt_time; if(pdt_sync_cache()) { LOG(L_ERR, "PDT:prefix2domain: cannot update the cache\n"); return -1; } } /* find the domain that corresponds to this prefix */ plen = 0; if((d=pdt_get_domain(_ptree, &p, &plen))==NULL) { LOG(L_INFO, "PDT:prefix2domain: no prefix found in [%.*s]\n", p.len, p.s); return -1; } /* update the new uri */ if(update_new_uri(msg, plen, d, mode)<0) { LOG(L_ERR, "PDT:prefix2domain: new_uri cannot be updated\n"); return -1; } return 1; } /* change the uri according to translation of the prefix */ int update_new_uri(struct sip_msg *msg, int plen, str *d, int mode) { struct action act; struct run_act_ctx ra_ctx; if(msg==NULL || d==NULL) { LOG(L_ERR, "PDT:update_new_uri: bad parameters\n"); return -1; } memset(&act, 0, sizeof(act)); if(mode==0 || (mode==1 && prefix.len>0)) { act.type = STRIP_T; act.val[0].type = NUMBER_ST; if(mode==0) act.val[0].u.number = plen + prefix.len; else act.val[0].u.number = prefix.len; act.next = 0; init_run_actions_ctx(&ra_ctx); if (do_action(&ra_ctx, &act, msg) < 0) { LOG(L_ERR, "PDT:update_new_uri:Error removing prefix\n"); return -1; } } act.type = SET_HOSTPORT_T; act.val[0].type = STRING_ST; act.val[0].u.string = d->s; act.next = 0; init_run_actions_ctx(&ra_ctx); if (do_action(&ra_ctx, &act, msg) < 0) { LOG(L_ERR, "PDT:update_new_uri:Error changing domain\n"); return -1; } DBG("PDT: update_new_uri: len=%d uri=%.*s\n", msg->new_uri.len, msg->new_uri.len, msg->new_uri.s); return 0; } int pdt_load_db() { db_res_t* res = NULL; db_rec_t* rec; if (db_exec(&res, db_load) < 0) { ERR("pdt: Error while loading data from database\n"); return -1; } if (res == NULL) return 0; for(rec = db_first(res); rec; rec = db_next(res)) { if (rec->fld[0].flags & DB_NULL || rec->fld[1].flags & DB_NULL) { INFO("pdt: Record with NULL value(s) found in database, skipping\n"); continue; } if (pdt_check_pd(_dhash, &rec->fld[0].v.lstr, &rec->fld[1].v.lstr) != 0) { ERR("pdt: Prefix [%.*s] or domain <%.*s> duplicated\n", STR_FMT(&rec->fld[0].v.lstr), STR_FMT(&rec->fld[1].v.lstr)); goto error; } if (pdt_add_to_tree(_ptree, &rec->fld[0].v.lstr, &rec->fld[1].v.lstr) != 0) { ERR("pdt: Error adding info in tree\n"); goto error; } if(pdt_add_to_hash(_dhash, &rec->fld[0].v.lstr, &rec->fld[1].v.lstr) != 0) { ERR("pdt: Error adding info in hash\n"); goto error; } } db_res_free(res); return 0; error: if (res) db_res_free(res); return -1; } int pdt_sync_cache() { pd_op_t *ito; DBG("PDT:pdt_sync_cache: ...\n"); if(_dhash==NULL || _ptree==NULL) { LOG(L_ERR, "PDT:pdt_sync_cache: strange situation\n"); return -1; } lock_get(&_dhash->diff_lock); if(_ptree->idsync >= _dhash->max_id) goto done; ito = _dhash->diff; while(ito!=NULL && _ptree->idsync >= ito->id) ito = ito->n; while(ito!=NULL) { DBG("PDT:pdt_sync_cache: sync op[%d]=%d...\n", ito->id, ito->op); switch(ito->op) { case PDT_ADD: if(pdt_add_to_tree(_ptree, &ito->cell->prefix, &ito->cell->domain)!=0) { LOG(L_ERR, "PDT:pdt_sync_cache: Error to insert in tree\n"); goto error; } break; case PDT_DELETE: if(pdt_remove_from_tree(_ptree, &ito->cell->prefix)!=0) { LOG(L_ERR, "PDT:pdt_sync_cache: Error to remove from tree\n"); goto error; } break; default: LOG(L_ERR, "PDT:pdt_sync_cache: unknown operation\n"); } _ptree->idsync = ito->id; ito->count++; ito = ito->n; } done: lock_release(&_dhash->diff_lock); return 0; error: lock_release(&_dhash->diff_lock); return -1; } void pdt_clean_cache(unsigned int ticks, void *param) { pd_op_t *ito, *tmp; /* DBG("PDT:pdt_clean_cache: ...\n"); */ if(_dhash==NULL) { LOG(L_ERR, "PDT:pdt_clean_cache: strange situation\n"); return; } lock_get(&_dhash->diff_lock); ito = _dhash->diff; while(ito!=NULL) { if(ito->count >= _dhash->workers) { DBG("PDT:pdt_clean_cache: cleaning op[%d]=%d...\n", ito->id, ito->op); free_cell(ito->cell); if(ito->p!=NULL) (ito->p)->n = ito->n; else _dhash->diff = ito->n; if(ito->n!=NULL) (ito->n)->p = ito->p; tmp = ito; ito = ito->n; shm_free(tmp); } else ito = ito->n; } lock_release(&_dhash->diff_lock); return; } static const char* rpc_add_doc[2] = { "Add new prefix/domain translation rule.", 0 }; static void rpc_add(rpc_t* rpc, void* c) { pd_t* cell; pd_op_t *ito, *tmp; str sd, sp; char* t; if(_dhash==NULL) { LOG(L_ERR, "PDT:pdt_fifo_add: strange situation\n"); rpc->fault(c, 500, "Server Error"); return; } /* Use 's' to make sure strings are zero terminated */ if (rpc->scan(c, "ss", &sp.s, &sd.s) < 2) { rpc->fault(c, 400, "Invalid Parameter Value"); return; } sp.len = strlen(sp.s); sd.len = strlen(sd.s); t = sp.s; while(t!=NULL && *t!='\0') { if(*t < '0' || *t > '9') { LOG(L_ERR, "PDT:pdt_fifo_add: bad prefix [%s]\n", sp.s); rpc->fault(c, 400, "Bad Prefix"); return; } t++; } if(pdt_check_pd(_dhash, &sp, &sd)!=0) { LOG(L_ERR, "PDT:pdt_fifo_add: prefix or domain exists\n"); rpc->fault(c, 400, "Prefix Or Domain Exists"); return; } db_insert->vals[0].v.lstr = sp; db_insert->vals[1].v.lstr = sd; DBG("PDT:pdt_fifo_add: [%.*s] <%.*s>\n", STR_FMT(&sp), STR_FMT(&sd)); /* insert a new domain into database */ if (db_exec(NULL, db_insert) < 0) { LOG(L_ERR, "PDT:pdt_fifo_add: error storing new prefix/domain\n"); rpc->fault(c, 430, "Cannot Store Prefix/domain"); return; } /* insert the new domain into hashtables, too */ cell = new_cell(&sp, &sd); if(cell==NULL) { LOG(L_ERR, "PDT:pdt_fifo_add: no more shm\n"); rpc->fault(c, 431, "Out Of Shared Memory"); goto error1; } tmp = new_pd_op(cell, 0, PDT_ADD); if(tmp==NULL) { LOG(L_ERR, "PDT:pdt_fifo_add: no more shm!\n"); rpc->fault(c, 431, "Out Of Shared Memory"); goto error2; } lock_get(&_dhash->diff_lock); if(pdt_add_to_hash(_dhash, &sp, &sd)!=0) { LOG(L_ERR, "PDT:pdt_fifo_add: could not add to cache\n"); rpc->fault(c, 431, "Could Not Add To Cache"); goto error3; } _dhash->max_id++; tmp->id = _dhash->max_id; if(_dhash->diff==NULL) { _dhash->diff = tmp; goto done; } ito = _dhash->diff; while(ito->n!=NULL) ito = ito->n; ito->n = tmp; tmp->p = ito; done: DBG("PDT:pdt_fifo_add: op[%d]=%d...\n", tmp->id, tmp->op); lock_release(&_dhash->diff_lock); return; error3: lock_release(&_dhash->diff_lock); free_pd_op(tmp); error2: free_cell(cell); error1: db_delete->vals[0].v.lstr = sp; db_delete->vals[1].v.lstr = sd; if (db_exec(NULL, db_delete) < 0) { LOG(L_ERR,"PDT:pdt_fifo_add: database/cache are inconsistent\n"); } } static const char* rpc_delete_doc[2] = { "Delete prefix/domain translation rule.", 0 }; static void rpc_delete(rpc_t* rpc, void* c) { str sd; unsigned int dhash; int hash_entry; pd_t *it; pd_op_t *ito, *tmp; if(_dhash==NULL) { LOG(L_ERR, "PDT:pdt_fifo_delete: strange situation\n"); rpc->fault(c, 500, "Server Error"); return; } /* Use s to make sure the string is zero terminated */ if (rpc->scan(c, "s", &sd.s) < 1) { rpc->fault(c, 400, "Parameter Missing"); return; } sd.len = strlen(sd.s); if(*sd.s=='\0') { LOG(L_INFO, "PDT:pdt_fifo_delete: empty domain\n"); rpc->fault(c, 400, "Empty Parameter"); return; } dhash = pdt_compute_hash(sd.s); hash_entry = get_hash_entry(dhash, _dhash->hash_size); lock_get(&_dhash->diff_lock); lock_get(&_dhash->dhash[hash_entry].lock); it = _dhash->dhash[hash_entry].e; while(it!=NULL && it->dhash<=dhash) { if(it->dhash==dhash && it->domain.len==sd.len && strncasecmp(it->domain.s, sd.s, sd.len)==0) break; it = it->n; } if(it!=NULL) { if(it->p!=NULL) (it->p)->n = it->n; else _dhash->dhash[hash_entry].e = it->n; if(it->n) (it->n)->p = it->p; } lock_release(&_dhash->dhash[hash_entry].lock); if(it!=NULL) { tmp = new_pd_op(it, 0, PDT_DELETE); if(tmp==NULL) { LOG(L_ERR, "PDT:pdt_fifo_delete: no more shm!\n"); rpc->fault(c, 431, "No Shared Memory Left"); lock_release(&_dhash->diff_lock); return; } _dhash->max_id++; tmp->id = _dhash->max_id; if(_dhash->diff==NULL) { _dhash->diff = tmp; DBG("PDT:pdt_fifo_delete: op[%d]=%d...\n", tmp->id, tmp->op); goto done; } ito = _dhash->diff; while(ito->n!=NULL) ito = ito->n; ito->n = tmp; tmp->p = ito; DBG("PDT:pdt_fifo_delete: op[%d]=%d...\n", tmp->id, tmp->op); dhash = 1; } else { dhash = 0; } done: lock_release(&_dhash->diff_lock); if(dhash==0) { DBG("PDT:pdt_fifo_delete: prefix for domain [%s] not found\n", sd.s); rpc->fault(c, 404, "Domain Not Found"); } else { db_del_domain->match[0].v.lstr = sd; if (db_exec(NULL, db_del_domain) < 0) { LOG(L_ERR,"PDT:pdt_fifo_delete: database/cache are inconsistent\n"); rpc->fault(c, 502, "Database And Cache Are Inconsistent"); } } } static const char* rpc_list_doc[2] = { "List existin prefix/domain translation rules", 0 }; /** * Fifo command example: * * --- * :pdt_list:[response_file]\n * prefix\n * domain\n * \n * -- * * - '.' (dot) means NULL value for [prefix] and [domain] * - if both [prefix] and [domain] are NULL, all prefix-domain pairs are listed * - the comparison operation is 'START WITH' -- if domain is 'a' then * all domains starting with 'a' are listed */ static void rpc_list(rpc_t* rpc, void* c) { str sd, sp; pd_t *it; int i; char* buf1, *buf2, *t; if(_dhash==NULL) { LOG(L_ERR, "PDT:pdt_fifo_list: strange situation\n"); rpc->fault(c, 500, "Server Error"); return; } if (rpc->scan(c, "ss", &sp.s, &sd.s) < 2) { rpc->fault(c, 400, "Invalid parameter value"); return; } sp.len = strlen(sp.s); sd.len = strlen(sd.s); t = sp.s; if(*t!='\0' && *t!='.') { while(t!=NULL && *t!='\0') { if(*t < '0' || *t > '9') { LOG(L_ERR, "PDT:pdt_fifo_add: bad prefix [%s]\n", sp.s); rpc->fault(c, 400, "Bad Prefix"); return; } t++; } } else { sp.s = NULL; sp.len = 0; } if(*sd.s=='\0' || *sd.s=='.') { sd.s = NULL; sd.len = 0; } lock_get(&_dhash->diff_lock); for(i=0; i<_dhash->hash_size; i++) { lock_get(&_dhash->dhash[i].lock); it = _dhash->dhash[i].e; for (it = _dhash->dhash[i].e; it; it = it->n) { if((sp.s==NULL && sd.s==NULL) || (sp.s!=NULL && it->prefix.len>=sp.len && strncmp(it->prefix.s, sp.s, sp.len)==0) || (sd.s!=NULL && it->domain.len>=sd.len && strncasecmp(it->domain.s, sd.s, sd.len)==0)) { buf1 = pkg_malloc(it->prefix.len + 1); if (!buf1) continue; memcpy(buf1, it->prefix.s, it->prefix.len); buf1[it->prefix.len] = '\0'; buf2 = pkg_malloc(it->domain.len + 1); if (!buf2) { pkg_free(buf1); continue; } memcpy(buf2, it->domain.s, it->domain.len); buf2[it->domain.len] = '\0'; rpc->add(c, "ss", buf1, buf2); pkg_free(buf1); pkg_free(buf2); } } lock_release(&_dhash->dhash[i].lock); } lock_release(&_dhash->diff_lock); } static rpc_export_t pdt_rpc[] = { {"pdt.add", rpc_add, rpc_add_doc, 0}, {"pdt.delete", rpc_delete, rpc_delete_doc, 0}, {"pdt.list", rpc_list, rpc_list_doc, RET_ARRAY}, {0, 0, 0, 0} }; kamailio-4.0.4/obsolete/pdt/ser-pdt.cfg0000644000000000000000000000054612223032460016444 0ustar rootroot# # Minimalistic SER configuration file that can be used to # test the pdt module. # debug = 4 fork = no children = 1 log_stderror = yes listen=127.0.0.1 loadpath "./modules" loadmodule "mysql" loadmodule "sl" loadmodule "pdt" modparam("pdt", "db_url", "mysql://ser:heslo@localhost/ser") route { prefix2domain(); forward(uri:host, uri:port); break; } kamailio-4.0.4/obsolete/pdt/Makefile0000644000000000000000000000045012223032460016037 0ustar rootroot# $Id$ # # print example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=pdt.so LIBS= DEFS+=-DSER_MOD_INTERFACE SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/pdt/pdtree.h0000644000000000000000000000317412223032460016041 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG FOKUS * Copyright (C) 2005 Voice Sistem SRL (Voice-System.RO) * * This file is part of SIP Express Router. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * For any questions about this software and its license, please contact * Voice Sistem at following e-mail address: * office@voice-sistem.ro * * History: * ------- * 2005-01-25 first tree version (ramona) */ #ifndef _PDTREE_H_ #define _PDTREE_H_ #include "../../str.h" typedef struct _pdt_node { str domain; struct _pdt_node *child; } pdt_node_t; #define PDT_MAX_DEPTH 32 #define PDT_NODE_SIZE 10 typedef struct _pdt_tree { pdt_node_t *head; int idsync; } pdt_tree_t; int pdt_add_to_tree(pdt_tree_t *pt, str *code, str *domain); int pdt_remove_from_tree(pdt_tree_t *pt, str *code); str* pdt_get_domain(pdt_tree_t *pt, str *code, int *plen); pdt_tree_t* pdt_init_tree(); void pdt_free_tree(pdt_tree_t *pt); int pdt_print_tree(pdt_tree_t *pt); #endif kamailio-4.0.4/obsolete/fifo/0000755000000000000000000000000012223032460014534 5ustar rootrootkamailio-4.0.4/obsolete/fifo/fifo_server.h0000644000000000000000000000301212223032460017212 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _FIFO_SERVER_H #define _FIFO_SERVER_H #include #define CMD_SEPARATOR ':' extern char* fifo; extern char* fifo_dir; extern char* fifo_user; extern char* fifo_group; extern int fifo_mode; extern int fifo_reply_retries; extern int fifo_reply_wait; /* Initialize FIFO server data structures */ int init_fifo_server(void); /* Create the FIFO server process */ int start_fifo_server(void); /* memory deallocation */ void destroy_fifo(void); #endif kamailio-4.0.4/obsolete/fifo/README0000644000000000000000000000004412223032460015412 0ustar rootrootThis module is considered obsolete. kamailio-4.0.4/obsolete/fifo/fifo_server.c0000644000000000000000000011274712223032460017225 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Fifo server is a very powerful tool used to access easily * ser's internals via textual interface, similarly to * how internals of many operating systems are accessible * via the proc file system. This might be used for * making ser do things for you (such as initiating new * transaction from webpages) or inspect server's health. * * FIFO server allows new functionality to be registered * with it -- thats what register_fifo_cmd is good for. * Remember, the initialization must take place before * forking; best in init_module functions. When a function * is registered, it can be always evoked by sending its * name prefixed by colon to the FIFO. * * There are few commands already implemented in core. * These are 'uptime' for looking at how long the server * is alive and 'print' for debugging purposes. * * Every command sent to FIFO must be sent atomically to * avoid intermixing with other commands and MUST be * terminated by empty line so that the server is to able * to find its end if it does not understand the command. * * File test/transaction.fifo illustrates example of use * of t_uac command (part of TM module). * * History: * -------- * 2003-03-29 destroy pkg mem introduced (jiri) * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-01-29 new built-in fifo commands: arg and pwd (jiri) * 2003-10-07 fifo security fixes: permissions, always delete old fifo, * reply fifo checks -- added fifo_check (andrei) * 2003-10-13 added fifo_dir for reply fifos (andrei) * 2003-10-30 DB interface exported via FIFO (bogdan) * 2004-03-09 open_fifo_server split into init_ and start_ (andrei) * 2004-04-29 added chown(sock_user, sock_group) (andrei) * 2004-06-06 updated to the new DB interface & init_db_fifo (andrei) * 2004-09-19 fifo is deleted on exit (destroy_fifo) (andrei) * 2005-03-02 meminfo fifo cmd added (andrei) */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_TCP #include #endif #include "../../dprint.h" #include "../../ut.h" #include "../../error.h" #include "../../config.h" #include "../../globals.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../sr_module.h" #include "../../pt.h" #include "../../rpc.h" #include "../../cfg/cfg_struct.h" #include "fifo_server.h" #define MAX_FIFO_COMMAND 128 /* Maximum length of a FIFO server command */ #define MAX_CONSUME_BUFFER 1024 /* Buffer dimensions for FIFO server */ #define MAX_LINE_BUFFER 2048 /* Maximum parameter line length */ #define DEFAULT_REPLY_RETRIES 4 /* Default number of reply write attempts */ #define DEFAULT_REPLY_WAIT 80000 /* How long we should wait for the client, in micro seconds */ #define DEFAULT_FIFO_DIR "/tmp/" /* Where reply pipes may be opened */ enum text_flags { CHUNK_SEEN = (1 << 0), CHUNK_POSITIONAL = (1 << 1), /* Positinal parameter, should be followed by \n */ CHUNK_MEMBER_NAME = (1 << 2), /* Struct member name, should be followed by : */ CHUNK_MEMBER_VALUE = (1 << 3) /* Struct member value, should be followed by , if * there is another member name and \n if not */ }; /* * Generit text chunk. Flags attribute contains arbitrary flags */ struct text_chunk { unsigned char flags; str s; struct text_chunk* next; }; /* * This is the parameter if rpc_struct_add */ struct rpc_struct_out { struct rpc_context* ctx; struct text_chunk* line; }; struct rpc_struct { struct rpc_context* ctx; struct text_chunk* names; /* Names of elements */ struct text_chunk* values; /* Element values as strings */ struct rpc_struct* next; }; /* * Context structure containing state of processing */ typedef struct rpc_context { char* method; /* Request method name */ char* reply_file; /* Full path and name to the reply FIFO file */ int reply_sent; /* This flag ensures that we do not send a reply twice */ int code; /* Reply code */ char* reason; /* Reason phrase */ struct text_chunk* body; /* First line to be appended as reply body */ struct text_chunk* last; /* Last body line */ struct text_chunk* strs; /* Strings to be collected at the end of processing */ struct rpc_struct* structs; /* Structures to be collected at the end of processing */ } rpc_ctx_t; /* * Module parameters */ char* fifo = 0; /* FIFO name */ char* fifo_dir = DEFAULT_FIFO_DIR; /* dir where reply fifos are allowed */ char* fifo_user = 0; /* Username of FIFO file owner */ char* fifo_group = 0; /* Group name of FIFO file */ int fifo_reply_retries = DEFAULT_REPLY_RETRIES; int fifo_reply_wait = DEFAULT_REPLY_WAIT; int fifo_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; /* Default FIFO file mode (rw-rw----) */ static pid_t fifo_pid; /* PID of the FIFO server process */ static int fifo_uid = -1; /* User ID of owner of FIFO file */ static int fifo_gid = -1; /* Group ID of FIFO file */ static int fifo_read = 0; /* File descriptor for reading from FIFO file */ static int fifo_write = 0; /* File descriptor for writing to FIFO reply file */ static int line_no = 0; /* This is where we keep track of line numbers in the request */ static FILE* fifo_stream = 0; /* Convenience stream structure for reading from FIFO file */ static rpc_t func_param; /* Pointers to implementation of RPC funtions */ static rpc_ctx_t context; /* Global context for processing of requests received * through FIFO file */ static int rpc_send (rpc_ctx_t* ctx); /* Send the reply to the client */ static void rpc_fault (rpc_ctx_t* ctx, int code, char* fmt, ...); /* Signal a failure to the client */ static int rpc_add (rpc_ctx_t* ctx, char* fmt, ...); /* Add a new piece of data to the result */ static int rpc_scan (rpc_ctx_t* ctx, char* fmt, ...); /* Retrieve request parameters */ static int rpc_printf (rpc_ctx_t* ctx, char* fmt, ...); /* Add printf-like formated data to the result set */ static int rpc_struct_add (struct text_chunk* s, char* fmt, ...); /* Create a new structure */ static int rpc_struct_scan (struct rpc_struct* s, char* fmt, ...); /* Scan attributes of a structure */ static int rpc_struct_printf(struct text_chunk* s, char* name, char* fmt, ...); /* * Escape string in buffer 'r' of length len. Write * the escaped string in buffer dst. The destination * buffer must exist and must be twice as big as the * input buffer. * * Parameter all controls the set of characters to be * escaped. If set to 1 then all characters, including * structure delimiters, will be escaped. If set to * 0 then only line delimiters, tab and zero will be * escaped. */ static void escape(str* dst, char* r, int len, int all) { int i; char* w; if (!len) { dst->len = 0; return; } w = dst->s; for(i = 0; i < len; i++) { switch(r[i]) { case '\n': *w++ = '\\'; *w++ = 'n'; break; case '\r': *w++ = '\\'; *w++ = 'r'; break; case '\t': *w++ = '\\'; *w++ = 't'; break; case '\\': *w++ = '\\'; *w++ = '\\'; break; case '\0': *w++ = '\\'; *w++ = '0'; break; case ':': if (all) { *w++ = '\\'; *w++ = 'o'; } else *w++ = r[i]; break; case ',': if (all) { *w++ = '\\'; *w++ = 'c'; } else *w++ = r[i]; break; default: *w++ = r[i]; break; } } dst->len = w - dst->s; } /* * Unescape the string in buffer 'r' of length len. * The resulting string will be stored in buffer dst * which must exist and must be at least as big as * the source buffer. The function will update dst->len * to the length of the resulting string. * * Return value 0 indicates success, -1 indicates * formatting error. */ static int unescape(str* dst, char* r, int len) { char* w; int i; if (!len) { dst->len = 0; return 0; } w = dst->s; for(i = 0; i < len; i++) { switch(*r) { case '\\': r++; i++; switch(*r++) { case '\\': *w++ = '\\'; break; case 'n': *w++ = '\n'; break; case 'r': *w++ = '\r'; break; case 't': *w++ = '\t'; break; case '0': *w++ = '\0'; break; case 'c': *w++ = ':'; break; /* Structure delimiter */ case 'o': *w++ = ','; break; /* Structure delimiter */ default: return -1; } break; default: *w++ = *r++; break; } } dst->len = w - dst->s; return 0; } /* * Create a new text chunk, the input text will * be escaped. */ struct text_chunk* new_chunk_escape(str* src, int escape_all) { struct text_chunk* l; if (!src) return 0; l = pkg_malloc(sizeof(struct text_chunk)); if (!l) { ERR("No Memory Left\n"); return 0; } l->s.s = pkg_malloc(src->len * 2 + 1); if (!l->s.s) { ERR("No Memory Left\n"); pkg_free(l); return 0; } l->next = 0; l->flags = 0; escape(&l->s, src->s, src->len, escape_all); l->s.s[l->s.len] = '\0'; return l; } /* * Create a new text chunk, the input text * will not be escaped. The function returns * 0 on an error */ struct text_chunk* new_chunk(str* src) { struct text_chunk* l; if (!src) return 0; l = pkg_malloc(sizeof(struct text_chunk)); if (!l) { ERR("No Memory Left\n"); return 0; } l->s.s = pkg_malloc(src->len + 1); if (!l->s.s) { ERR("No Memory Left\n"); pkg_free(l); return 0; } l->next = 0; l->flags = 0; memcpy(l->s.s, src->s, src->len); l->s.len = src->len; l->s.s[l->s.len] = '\0'; return l; } /* * Create a new text chunk, the input text * will be unescaped first. */ struct text_chunk* new_chunk_unescape(str* src) { struct text_chunk* l; if (!src) return 0; l = pkg_malloc(sizeof(struct text_chunk)); if (!l) { ERR("No Memory Left\n"); return 0; } l->s.s = pkg_malloc(src->len + 1); if (!l->s.s) { ERR("No Memory Left\n"); pkg_free(l); return 0; } l->next = 0; l->flags = 0; if (unescape(&l->s, src->s, src->len) < 0) { pkg_free(l->s.s); pkg_free(l); return 0; } l->s.s[l->s.len] = '\0'; return l; } static void free_chunk(struct text_chunk* c) { if (c && c->s.s) pkg_free(c->s.s); if (c) pkg_free(c); } static void free_struct(struct rpc_struct* s) { struct text_chunk* c; if (!s) return; while(s->names) { c = s->names; s->names = s->names->next; free_chunk(c); } while(s->values) { c = s->values; s->values = s->values->next; free_chunk(c); } pkg_free(s); } /* * Parse a structure */ static struct rpc_struct* new_struct(rpc_ctx_t* ctx, str* line) { char* comma, *colon; struct rpc_struct* s; str left, right = STR_NULL, name, value; struct text_chunk* n, *v; if (!line->len) { rpc_fault(ctx, 400, "Line %d Empty - Structure Expected", line_no); return 0; } s = (struct rpc_struct*)pkg_malloc(sizeof(struct rpc_struct)); if (!s) { rpc_fault(ctx, 500, "Internal Server Error (No Memory Left)"); return 0; } memset(s, 0, sizeof(struct rpc_struct)); s->ctx = ctx; left = *line; do { comma = q_memchr(left.s, ',', left.len); if (comma) { right.s = comma + 1; right.len = left.len - (comma - left.s) - 1; left.len = comma - left.s; } /* Split the record to name and value */ colon = q_memchr(left.s, ':', left.len); if (!colon) { rpc_fault(ctx, 400, "Colon missing in struct on line %d", line_no); goto err;; } name.s = left.s; name.len = colon - name.s; value.s = colon + 1; value.len = left.len - (colon - left.s) - 1; /* Create name chunk */ n = new_chunk_unescape(&name); if (!n) { rpc_fault(ctx, 400, "Error while processing struct member '%.*s' on line %d", name.len, ZSW(name.s), line_no); goto err; } n->next = s->names; s->names = n; /* Create value chunk */ v = new_chunk_unescape(&value); if (!v) { rpc_fault(ctx, 400, "Error while processing struct membeer '%.*s' on line %d", name.len, ZSW(name.s), line_no); goto err; } v->next = s->values; s->values = v; left = right; } while(comma); return s; err: if (s) free_struct(s); return 0; } /* * Read a line from FIFO file and store the data in buffer 'b' * and the legnth of the line in variable 'read' * * Returns -1 on error, 0 on success */ static int read_line(char *b, int max, FILE *stream, int *read) { int len, retry_cnt; retry_cnt = 0; retry: if (fgets(b, max, stream) == NULL) { ERR("fifo_server fgets failed: %s\n", strerror(errno)); /* on Linux, fgets sometimes returns ESPIPE -- give * it few more chances */ if (errno == ESPIPE) { retry_cnt++; if (retry_cnt<4) goto retry; } /* interrupted by signal or ... */ if ((errno == EINTR) || (errno == EAGAIN)) goto retry; kill(0, SIGTERM); } /* if we did not read whole line, our buffer is too small * and we cannot process the request; consume the remainder of * request */ len = strlen(b); if (len && !(b[len - 1] == '\n' || b[len - 1] == '\r')) { ERR("Request line too long\n"); return -1; } /* trim from right */ while(len) { if(b[len - 1] == '\n' || b[len - 1] == '\r' || b[len - 1] == ' ' || b[len - 1] == '\t') { len--; b[len] = 0; } else break; } *read = len; line_no++; return 0; } /* * Remove directory path from filename and replace it * with the path configured through a module parameter. * * The result is allocated using pkg_malloc and thus * has to be freed using pkg_free */ static char *trim_filename(char * file) { int prefix_len, fn_len; char *new_fn; /* we only allow files in "/tmp" -- any directory * changes are not welcome */ if (strchr(file, '.') || strchr(file, '/') || strchr(file, '\\')) { ERR("Forbidden filename: %s\n" , file); return 0; } prefix_len = strlen(fifo_dir); fn_len = strlen(file); new_fn = pkg_malloc(prefix_len + fn_len + 1); if (new_fn == 0) { ERR("No memory left\n"); return 0; } memcpy(new_fn, fifo_dir, prefix_len); memcpy(new_fn + prefix_len, file, fn_len); new_fn[prefix_len + fn_len] = 0; return new_fn; } /* * Consume pending data in FIFO file */ static void consume_request(FILE *stream) { int len; static char buffer[MAX_CONSUME_BUFFER]; while(read_line(buffer, MAX_CONSUME_BUFFER, stream, &len) < 0); } /* reply fifo security checks: * checks if fd is a fifo, is not hardlinked and it's not a softlink * opened file descriptor + file name (for soft link check) * returns 0 if ok, <0 if not */ static int fifo_check(int fd, char* fname) { struct stat fst; struct stat lst; if (fstat(fd, &fst) < 0) { ERR("fstat failed: %s\n", strerror(errno)); return -1; } /* check if fifo */ if (!S_ISFIFO(fst.st_mode)){ ERR("%s is not a fifo\n", fname); return -1; } /* check if hard-linked */ if (fst.st_nlink > 1) { ERR("%s is hard-linked %d times\n", fname, (unsigned)fst.st_nlink); return -1; } /* lstat to check for soft links */ if (lstat(fname, &lst) < 0) { ERR("lstat failed: %s\n", strerror(errno)); return -1; } if (S_ISLNK(lst.st_mode)) { ERR("%s is a soft link\n", fname); return -1; } /* if this is not a symbolic link, check to see if the inode didn't * change to avoid possible sym.link, rm sym.link & replace w/ fifo race */ if ((lst.st_dev != fst.st_dev) || (lst.st_ino != fst.st_ino)) { ERR("inode/dev number differ : %d %d (%s)\n", (int)fst.st_ino, (int)lst.st_ino, fname); return -1; } /* success */ return 0; } /* * Open the FIFO reply file */ static FILE *open_reply_pipe(char *pipe_name) { int fifofd; FILE *file_handle; int flags; int retries = fifo_reply_retries; if (!pipe_name || *pipe_name == 0) { DBG("No file to write to about missing cmd\n"); return 0; } tryagain: /* open non-blocking to make sure that a broken client will not * block the FIFO server forever */ fifofd = open(pipe_name, O_WRONLY | O_NONBLOCK); if (fifofd == -1) { /* retry several times if client is not yet ready for getting * feedback via a reply pipe */ if (errno == ENXIO) { /* give up on the client - we can't afford server blocking */ if (retries == 0) { ERR("No client at %s\n", pipe_name); return 0; } /* don't be noisy on the very first try */ if (retries != fifo_reply_retries) { DBG("Retry countdown: %d\n", retries); } sleep_us(fifo_reply_wait); retries--; goto tryagain; } /* some other opening error */ ERR("Open error (%s): %s\n", pipe_name, strerror(errno)); return 0; } /* security checks: is this really a fifo?, is * it hardlinked? is it a soft link? */ if (fifo_check(fifofd, pipe_name) < 0) goto error; /* we want server blocking for big writes */ if ((flags = fcntl(fifofd, F_GETFL, 0)) < 0) { ERR("(%s): getfl failed: %s\n", pipe_name, strerror(errno)); goto error; } flags &= ~O_NONBLOCK; if (fcntl(fifofd, F_SETFL, flags) < 0) { ERR("(%s): setfl cntl failed: %s\n", pipe_name, strerror(errno)); goto error; } /* create an I/O stream */ file_handle = fdopen( fifofd, "w"); if (file_handle == NULL) { ERR("Open error (%s): %s\n", pipe_name, strerror(errno)); goto error; } return file_handle; error: close(fifofd); return 0; } /* * Man FIFO server routine running in the FIFO * server process, processes requests received * through the FIFO file repeatedly, never returns */ static void fifo_server(FILE *fifo_stream) { rpc_export_t* exp; static char buf[MAX_FIFO_COMMAND]; int line_len; char *file_sep; struct text_chunk* p; struct rpc_struct* s; file_sep = 0; context.method = 0; context.reply_file = 0; context.body = 0; /* register a diagnostic FIFO command */ while(1) { context.code = 200; context.reason = "OK"; context.reply_sent = 0; context.last = 0; line_no = 0; /* commands must look this way '::[filename]' */ if (read_line(buf, MAX_FIFO_COMMAND, fifo_stream, &line_len) < 0) { /* line breaking must have failed -- consume the rest * and proceed to a new request */ ERR("Command expected\n"); goto consume; } if (line_len == 0) { INFO("Empty command received\n"); continue; } if (line_len < 3) { ERR("Command must have at least 3 chars\n"); goto consume; } if (*buf != CMD_SEPARATOR) { ERR("Command must begin with %c: %.*s\n", CMD_SEPARATOR, line_len, buf); goto consume; } context.method = buf + 1; file_sep = strchr(context.method, CMD_SEPARATOR); if (file_sep == NULL) { ERR("File separator missing\n"); goto consume; } if (file_sep == context.method) { ERR("Empty command\n"); goto consume; } if (*(file_sep + 1) == 0) context.reply_file = NULL; else { context.reply_file = file_sep + 1; context.reply_file = trim_filename(context.reply_file); if (context.reply_file == 0) { ERR("Trimming filename\n"); goto consume; } } /* make command zero-terminated */ *file_sep = 0; /* update the local config */ cfg_update(); exp = find_rpc_export(context.method, 0); if (!exp || !exp->function) { DBG("Command %s not found\n", context.method); rpc_fault(&context, 500, "Command '%s' not found", context.method); goto consume; } exp->function(&func_param, &context); consume: if (!context.reply_sent) { rpc_send(&context); } if (context.reply_file) { pkg_free(context.reply_file); context.reply_file = 0; } /* Collect garbage (unescaped strings and structures) */ while(context.strs) { p = context.strs; context.strs = context.strs->next; free_chunk(p); } while(context.structs) { s = context.structs; context.structs = context.structs->next; free_struct(s); } consume_request(fifo_stream); DBG("Command consumed\n"); } } /* * Initialze the FIFO server, translated usernames to uid, * Make sure that we can create and open the FIFO file and * make it secure. This function must be executed from mod_init. * This ensures that it has sufficient privileges. */ int init_fifo_server(void) { struct stat filestat; int n; long opt; if (fifo == NULL) { DBG("No fifo will be opened\n"); /* everything is ok, we just do not want to start */ return 1; } if (strlen(fifo) == 0) { DBG("Fifo disabled\n"); return 1; } /* fix sock/fifo uid/gid */ if (fifo_user) { if (user2uid(&fifo_uid, 0, fifo_user) < 0) { ERR("Bad socket user name/uid number %s\n", fifo_user); return -1; } } if (fifo_group) { if (group2gid(&fifo_gid, fifo_group) < 0) { ERR("Bad group name/gid number: -u %s\n", fifo_group); return -1; } } DBG("Opening fifo...\n"); n = stat(fifo, &filestat); if (n == 0) { /* FIFO exist, delete it (safer) */ if (unlink(fifo) < 0) { ERR("Cannot delete old fifo (%s):" " %s\n", fifo, strerror(errno)); return -1; } } else if (n < 0 && errno != ENOENT) { ERR("FIFO stat failed: %s\n", strerror(errno)); } /* create FIFO ... */ if ((mkfifo(fifo, fifo_mode) < 0)) { ERR("Can't create FIFO: " "%s (mode=%d)\n", strerror(errno), fifo_mode); return -1; } DBG("FIFO created @ %s\n", fifo ); if ((chmod(fifo, fifo_mode) < 0)) { ERR("Can't chmod FIFO: %s (mode=%d)\n", strerror(errno), fifo_mode); return -1; } if ((fifo_uid != -1) || (fifo_gid != -1)) { if (chown(fifo, fifo_uid, fifo_gid) < 0) { ERR("Failed to change the owner/group for %s to %d.%d; %s[%d]\n", fifo, fifo_uid, fifo_gid, strerror(errno), errno); return -1; } } DBG("fifo %s opened, mode=%d\n", fifo, fifo_mode); fifo_read = open(fifo, O_RDONLY | O_NONBLOCK, 0); if (fifo_read < 0) { ERR("fifo_read did not open: %s\n", strerror(errno)); return -1; } fifo_stream = fdopen(fifo_read, "r"); if (fifo_stream == NULL) { ERR("fdopen failed: %s\n", strerror(errno)); return -1; } /* make sure the read fifo will not close */ fifo_write = open(fifo, O_WRONLY | O_NONBLOCK, 0); if (fifo_write < 0) { ERR("fifo_write did not open: %s\n", strerror(errno)); return -1; } /* set read fifo blocking mode */ if ((opt = fcntl(fifo_read, F_GETFL)) == -1) { ERR("fcntl(F_GETFL) failed: %s [%d]\n", strerror(errno), errno); return -1; } if (fcntl(fifo_read, F_SETFL, opt & (~O_NONBLOCK)) == -1) { ERR("fcntl(F_SETFL) failed: %s [%d]\n", strerror(errno), errno); return -1; } func_param.send = (rpc_send_f)rpc_send; func_param.fault = (rpc_fault_f)rpc_fault; func_param.add = (rpc_add_f)rpc_add; func_param.scan = (rpc_scan_f)rpc_scan; func_param.printf = (rpc_printf_f)rpc_printf; func_param.struct_add = (rpc_struct_add_f)rpc_struct_add; func_param.struct_scan = (rpc_struct_scan_f)rpc_struct_scan; func_param.struct_printf = (rpc_struct_printf_f)rpc_struct_printf; return 0; } /* * Create a new process that will be processing requests received * through the FIFO file. This must be executed from child_init * with rank PROC_MAIN. */ int start_fifo_server(void) { if (fifo_stream == 0) return 1; /* no error, we just don't start it */ fifo_pid = fork_process(PROC_FIFO,"fifo server",1); if (fifo_pid < 0) { ERR("Failed to fork: %s\n", strerror(errno)); return -1; } if (fifo_pid == 0) { /* child == FIFO server */ is_main = 0; INFO("fifo process starting: %d\n", getpid()); /* a real server doesn't die if writing to reply fifo fails */ signal(SIGPIPE, SIG_IGN); INFO("fifo server up at %s...\n", fifo); /* initialize the config framework */ if (cfg_child_init()) return -1; fifo_server(fifo_stream); /* never returns */ } /* dad process */ snprintf(pt[process_no].desc, MAX_PT_DESC, "fifo server @ %s", fifo); return 1; } /* * Close and unlink the FIFO file */ void destroy_fifo(void) { if (!fifo_stream) return; fclose(fifo_stream); fifo_stream = 0; /* if FIFO was created, delete it */ if (fifo && strlen(fifo)) { if (unlink(fifo) < 0) { WARN("Cannot delete fifo (%s):" " %s\n", fifo, strerror(errno)); } } } #define REASON_BUF_LEN 1024 /* * An error occurred, signal it to the client */ static void rpc_fault(rpc_ctx_t* ctx, int code, char* fmt, ...) { static char buf[REASON_BUF_LEN]; va_list ap; ctx->code = code; va_start(ap, fmt); vsnprintf(buf, REASON_BUF_LEN, fmt, ap); va_end(ap); ctx->reason = buf; } static inline int safe_write(FILE* f, char* fmt, ...) { va_list ap; if (!*fmt) return 0; va_start(ap, fmt); retry: /* First line containing code and reason phrase */ if (vfprintf(f, fmt, ap) <= 0) { ERR("Write error (%s): %s\n", fifo, strerror(errno)); if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK)) { goto retry; } va_end(ap); return -1; } va_end(ap); return 0; } /* * Send a reply, either positive or negative, to the client */ static int rpc_send(rpc_ctx_t* ctx) { struct text_chunk* p; FILE *f; /* Send the reply only once */ if (ctx->reply_sent) return 1; else ctx->reply_sent = 1; /* Open the reply file */ f = open_reply_pipe(ctx->reply_file); if (f == 0) { ERR("No reply pipe %s\n", ctx->reply_file); return -1; } /* First line containing code and reason phrase */ safe_write(f, "%d %s\n", ctx->code, ctx->reason); /* Send the body */ while(ctx->body) { p = ctx->body; ctx->body = ctx->body->next; if (p->s.len) safe_write(f, "%.*s", p->s.len, ZSW(p->s.s)); if (p->flags & CHUNK_POSITIONAL) { safe_write(f, "\n"); } else if (p->flags & CHUNK_MEMBER_NAME) { safe_write(f, ":"); } else if (p->flags & CHUNK_MEMBER_VALUE) { if (p->next && p->next->flags & CHUNK_MEMBER_NAME) { safe_write(f, ","); } else { safe_write(f, "\n"); } } free_chunk(p); } fclose(f); return 0; } /* * Add a chunk to reply */ static void append_chunk(rpc_ctx_t* ctx, struct text_chunk* l) { if (!ctx->last) { ctx->body = l; ctx->last = l; } else { ctx->last->next = l; ctx->last = l; } } /* * Convert a value to data chunk and add it to reply */ static int print_value(rpc_ctx_t* ctx, char fmt, va_list* ap) { struct text_chunk* l; str str_val; str* sp; char buf[256]; switch(fmt) { case 'd': case 't': str_val.s = int2str(va_arg(*ap, int), &str_val.len); l = new_chunk(&str_val); if (!l) { rpc_fault(ctx, 500, "Internal server error while processing line %d", line_no); goto err; } break; case 'f': str_val.s = buf; str_val.len = snprintf(buf, 256, "%f", va_arg(*ap, double)); if (str_val.len < 0) { rpc_fault(ctx, 400, "Error While Converting double"); ERR("Error while converting double\n"); goto err; } l = new_chunk(&str_val); if (!l) { rpc_fault(ctx, 500, "Internal Server Error, line %d", line_no); goto err; } break; case 'b': str_val.len = 1; str_val.s = ((va_arg(*ap, int) == 0) ? "0" : "1"); l = new_chunk(&str_val); if (!l) { rpc_fault(ctx, 500, "Internal Server Error, line %d", line_no); goto err; } break; case 's': str_val.s = va_arg(*ap, char*); str_val.len = strlen(str_val.s); l = new_chunk_escape(&str_val, 0); if (!l) { rpc_fault(ctx, 500, "Internal Server Error, line %d", line_no); goto err; } break; case 'S': sp = va_arg(*ap, str*); l = new_chunk_escape(sp, 0); if (!l) { rpc_fault(ctx, 500, "Internal Server Error, line %d", line_no); goto err; } break; default: rpc_fault(ctx, 500, "Bug In SER (Invalid formatting character %c)", fmt); ERR("Invalid formatting character\n"); goto err; } l->flags |= CHUNK_POSITIONAL; append_chunk(ctx, l); return 0; err: return -1; } static int rpc_add(rpc_ctx_t* ctx, char* fmt, ...) { void** void_ptr; va_list ap; str s = {"", 0}; struct text_chunk* l; va_start(ap, fmt); while(*fmt) { if (*fmt == '{') { void_ptr = va_arg(ap, void**); l = new_chunk(&s); if (!l) { rpc_fault(ctx, 500, "Internal Server Error"); goto err; } append_chunk(ctx, l); *void_ptr = l; } else { if (print_value(ctx, *fmt, &ap) < 0) goto err; } fmt++; } va_end(ap); return 0; err: va_end(ap); return -1; } #define RPC_BUF_SIZE 1024 static int rpc_struct_printf(struct text_chunk* c, char* name, char* fmt, ...) { int n, buf_size; char* buf; va_list ap; str s, nm; struct text_chunk* l, *m; buf = (char*)pkg_malloc(RPC_BUF_SIZE); if (!buf) { rpc_fault(&context, 500, "Internal Server Error (No memory left)"); ERR("No memory left\n"); return -1; } buf_size = RPC_BUF_SIZE; while (1) { /* Try to print in the allocated space. */ va_start(ap, fmt); n = vsnprintf(buf, buf_size, fmt, ap); va_end(ap); /* If that worked, return the string. */ if (n > -1 && n < buf_size) { nm.s = name; nm.len = strlen(name); m = new_chunk_escape(&nm, 1); /* Escape all characters, including : and , */ if (!m) { rpc_fault(&context, 500, "Internal Server Error"); goto err; } s.s = buf; s.len = n; l = new_chunk_escape(&s, 1); if (!l) { rpc_fault(&context, 500, "Internal Server Error"); free_chunk(m); ERR("Error while creating text_chunk structure"); goto err; } l->flags |= CHUNK_MEMBER_VALUE; l->next = c->next; c->next = l; if (c == context.last) context.last = l; m->flags |= CHUNK_MEMBER_NAME; m->next = c->next; c->next = m; if (c == context.last) context.last = m; return 0; } /* Else try again with more space. */ if (n > -1) { /* glibc 2.1 */ buf_size = n + 1; /* precisely what is needed */ } else { /* glibc 2.0 */ buf_size *= 2; /* twice the old size */ } if ((buf = pkg_realloc(buf, buf_size)) == 0) { rpc_fault(&context, 500, "Internal Server Error (No memory left)"); ERR("No memory left\n"); goto err; } } return 0; err: if (buf) pkg_free(buf); return -1; } static int rpc_printf(rpc_ctx_t* ctx, char* fmt, ...) { int n, buf_size; char* buf; va_list ap; str s; struct text_chunk* l; buf = (char*)pkg_malloc(RPC_BUF_SIZE); if (!buf) { rpc_fault(ctx, 500, "Internal Server Error (No memory left)"); ERR("No memory left\n"); return -1; } buf_size = RPC_BUF_SIZE; while (1) { /* Try to print in the allocated space. */ va_start(ap, fmt); n = vsnprintf(buf, buf_size, fmt, ap); va_end(ap); /* If that worked, return the string. */ if (n > -1 && n < buf_size) { s.s = buf; s.len = n; l = new_chunk_escape(&s, 0); if (!l) { rpc_fault(ctx, 500, "Internal Server Error"); ERR("Error while creating text_chunk structure"); goto err; } append_chunk(ctx, l); pkg_free(buf); return 0; } /* Else try again with more space. */ if (n > -1) { /* glibc 2.1 */ buf_size = n + 1; /* precisely what is needed */ } else { /* glibc 2.0 */ buf_size *= 2; /* twice the old size */ } if ((buf = pkg_realloc(buf, buf_size)) == 0) { rpc_fault(ctx, 500, "Internal Server Error (No memory left)"); ERR("No memory left\n"); goto err; } } return 0; err: if (buf) pkg_free(buf); return -1; } static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...) { static char buf[MAX_LINE_BUFFER]; struct text_chunk* l; struct rpc_struct* s; int* int_ptr; char** char_ptr; str* str_ptr; double* double_ptr; void** void_ptr; int read; str line; va_list ap; va_start(ap, fmt); line.s = buf; read = 0; while(*fmt) { if (read_line(line.s, MAX_LINE_BUFFER, fifo_stream, &line.len) < 0) { va_end(ap); return read; } switch(*fmt) { case 'b': /* Bool */ case 't': /* Date and time */ case 'd': /* Integer */ if (!line.len) { rpc_fault(ctx, 400, "Invalid parameter value on line %d", line_no); goto error; } int_ptr = va_arg(ap, int*); *int_ptr = strtol(line.s, 0, 0); break; case 'f': /* double */ if (!line.len) { rpc_fault(ctx, 400, "Invalid parameter value on line %d", line_no); goto error; } double_ptr = va_arg(ap, double*); *double_ptr = strtod(line.s, 0); break; case 's': /* zero terminated string */ case 'S': /* str structure */ l = new_chunk_unescape(&line); if (!l) { rpc_fault(ctx, 500, "Internal Server Error"); ERR("Not enough memory\n"); goto error; } /* Make sure it gets released at the end */ l->next = ctx->strs; ctx->strs = l; if (*fmt == 's') { char_ptr = va_arg(ap, char**); *char_ptr = l->s.s; } else { str_ptr = va_arg(ap, str*); *str_ptr = l->s; } break; case '{': void_ptr = va_arg(ap, void**); s = new_struct(ctx, &line); if (!s) goto error; s->next = ctx->structs; ctx->structs = s; *void_ptr = s; break; default: ERR("Invalid parameter type in formatting string: %c\n", *fmt); rpc_fault(ctx, 500, "Server Internal Error (Invalid Formatting Character '%c')", *fmt); goto error; } fmt++; read++; } va_end(ap); return read; error: va_end(ap); return -read; } static int rpc_struct_add(struct text_chunk* s, char* fmt, ...) { static char buf[MAX_LINE_BUFFER]; str st, *sp; va_list ap; struct text_chunk* m, *c; va_start(ap, fmt); while(*fmt) { /* Member name escaped */ st.s = va_arg(ap, char*); st.len = strlen(st.s); m = new_chunk_escape(&st, 1); /* Escape all characters, including : and , */ if (!m) { rpc_fault(&context, 500, "Internal Server Error"); goto err; } m->flags |= CHUNK_MEMBER_NAME; switch(*fmt) { case 'd': case 't': st.s = int2str(va_arg(ap, int), &st.len); c = new_chunk(&st); break; case 'f': st.s = buf; st.len = snprintf(buf, 256, "%f", va_arg(ap, double)); if (st.len < 0) { rpc_fault(&context, 400, "Error While Converting double"); ERR("Error while converting double\n"); goto err; } c = new_chunk(&st); break; case 'b': st.len = 1; st.s = ((va_arg(ap, int) == 0) ? "0" : "1"); c = new_chunk(&st); break; case 's': st.s = va_arg(ap, char*); st.len = strlen(st.s); c = new_chunk_escape(&st, 1); break; case 'S': sp = va_arg(ap, str*); c = new_chunk_escape(sp, 1); break; default: rpc_fault(&context, 500, "Bug In SER (Invalid formatting character %c)", *fmt); ERR("Invalid formatting character\n"); goto err; } if (!c) { rpc_fault(&context, 500, "Internal Server Error"); goto err; } c->flags |= CHUNK_MEMBER_VALUE; c->next = s->next; s->next = c; if (s == context.last) context.last = c; m->next = s->next; s->next = m; if (s == context.last) context.last = m; fmt++; } va_end(ap); return 0; err: if (m) free_chunk(m); va_end(ap); return -1; } static int find_member(struct text_chunk** value, struct rpc_struct* s, str* member_name) { struct text_chunk* n, *v; n = s->names; v = s->values; while(n) { if (member_name->len == n->s.len && !strncasecmp(member_name->s, n->s.s, n->s.len)) { if (n->flags & CHUNK_SEEN) goto skip; else { *value = v; n->flags |= CHUNK_SEEN; return 0; } } skip: n = n->next; v = v->next; } return 1; } static int rpc_struct_scan(struct rpc_struct* s, char* fmt, ...) { struct text_chunk* val; va_list ap; int* int_ptr; double* double_ptr; char** char_ptr; str* str_ptr; str member_name; int ret, read; read = 0; va_start(ap, fmt); while(*fmt) { member_name.s = va_arg(ap, char*); member_name.len = strlen(member_name.s); ret = find_member(&val, s, &member_name); if (ret > 0) { va_end(ap); return read; } switch(*fmt) { case 'b': /* Bool */ case 't': /* Date and time */ case 'd': /* Integer */ int_ptr = va_arg(ap, int*); if (!val->s.len) { rpc_fault(s->ctx, 400, "Invalid Parameter Value"); goto error; } /* String in text_chunk is always zero terminated */ *int_ptr = strtol(val->s.s, 0, 0); break; case 'f': /* double */ double_ptr = va_arg(ap, double*); if (!val->s.len) { rpc_fault(s->ctx, 400, "Invalid Parameter Value"); goto error; } /* String in text_chunk is always zero terminated */ *double_ptr = strtod(val->s.s, 0); break; case 's': /* zero terminated string */ char_ptr = va_arg(ap, char**); /* String in text_chunk is always zero terminated */ *char_ptr = val->s.s; break; case 'S': /* str structure */ str_ptr = va_arg(ap, str*); str_ptr->len = strlen(str_ptr->s); *str_ptr = val->s; break; default: rpc_fault(s->ctx, 500, "Invalid character in formatting string '%c'", *fmt); ERR("Invalid parameter type in formatting string: %c\n", *fmt); goto error; } fmt++; read++; } va_end(ap); return read; error: va_end(ap); return -read; } kamailio-4.0.4/obsolete/fifo/fifo.c0000644000000000000000000000643412223032460015632 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../../str.h" #include "../../sr_module.h" #include "../../error.h" #include "../../mem/mem.h" #include "../../parser/msg_parser.h" #include "../../ut.h" #include "../../dprint.h" #include "../../pt.h" #include "../../cfg/cfg_struct.h" #include "fifo_server.h" #include "fifo.h" MODULE_VERSION static int mod_init(void); static int child_init(int rank); static void mod_destroy(void); /* * Exported functions */ static cmd_export_t cmds[] = { {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"fifo_file", PARAM_STRING, &fifo }, {"fifo_dir", PARAM_STRING, &fifo_dir }, {"user", PARAM_STRING, &fifo_user }, {"mode", PARAM_INT, &fifo_mode }, {"group", PARAM_STRING, &fifo_group }, {"reply_retries", PARAM_INT, &fifo_reply_retries }, {"reply_wait", PARAM_INT, &fifo_reply_wait }, {0, 0, 0} }; struct module_exports exports = { "fifo", cmds, /* Exported commands */ 0, /* Exported RPC methods */ params, /* Exported parameters */ mod_init, /* module initialization function */ 0, /* response function*/ mod_destroy, /* destroy function */ 0, /* oncancel function */ child_init /* per-child init function */ }; static int mod_init(void) { if (init_fifo_server() < 0) return -1; /* Signal to the core that we will be creating one * additional process */ if (fifo) { register_procs(1); /* The child process will keep updating its * local configuration */ cfg_register_child(1); } return 0; } static int child_init(int rank) { /* Note: We call init_fifo_server from mod_init, this * will call the function at an early init stage -- before * do_suid, thus the function would have sufficient permissions * to change the user and group for FIFO * * start_fifo_server gets called from PROC_MAIN child init, this * ensures that the function gets called at the end of the init * process, when all the sockets are properly initialized. */ if (rank == PROC_MAIN) { /* FIXME: nofork rank==1 */ if (start_fifo_server() < 0) return -1; } return 0; } static void mod_destroy(void) { destroy_fifo(); } kamailio-4.0.4/obsolete/fifo/Makefile0000644000000000000000000000031712223032460016175 0ustar rootroot# $Id$ # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=fifo.so LIBS= DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/fifo/fifo.h0000644000000000000000000000207512223032460015634 0ustar rootroot/* * $Id$ * * Copyright (C) 2005 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _FIFO_H #define _FIFO_H #endif /* _FIFO_H */ kamailio-4.0.4/obsolete/jabber_k/0000755000000000000000000000000012223032460015350 5ustar rootrootkamailio-4.0.4/obsolete/jabber_k/xjab_jconf.h0000644000000000000000000000271412223032460017630 0ustar rootroot/* * $Id$ * * eXtended JABber module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _XJAB_JCONF_H_ #define _XHAB_JCONF_H_ #include "../../str.h" #define XJ_JCONF_NULL 0 #define XJ_JCONF_READY 1 #define XJ_JCONF_WAITING 2 #define XJ_JCONF_AUTH 4 /********** ***/ typedef struct _xj_jconf { int jcid; int status; str uri; str room; str server; str nick; str passwd; } t_xj_jconf, *xj_jconf; xj_jconf xj_jconf_new(str *u); int xj_jconf_init_sip(xj_jconf jcf, str *sid, char dl); int xj_jconf_init_jab(xj_jconf jcf); int xj_jconf_set_status(xj_jconf jcf, int s); int xj_jconf_cmp(void *a, void *b); int xj_jconf_free(xj_jconf jcf); int xj_jconf_check_addr(str *addr, char dl); #endif kamailio-4.0.4/obsolete/jabber_k/xode.h0000644000000000000000000002330412223032460016462 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include #include #include #include #include #include #include #include #include #include "expat.h" #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ /* ** Arrange to use either varargs or stdargs */ #define MAXSHORTSTR 203 /* max short string length */ #define QUAD_T unsigned long long #ifdef __STDC__ #include # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap, f) # define VA_END va_end(ap) #else /* __STDC__ */ # include # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap) # define VA_END va_end(ap) #endif /* __STDC__ */ #ifndef INCL_LIBXODE_H #define INCL_LIBXODE_H #ifdef __cplusplus extern "C" { #endif #ifndef HAVE_SNPRINTF extern int ap_snprintf(char *, size_t, const char *, ...); #define snprintf ap_snprintf #endif #ifndef HAVE_VSNPRINTF extern int ap_vsnprintf(char *, size_t, const char *, va_list ap); #define vsnprintf ap_vsnprintf #endif /* --------------------------------------------------------- */ /* */ /* Pool-based memory management routines */ /* */ /* --------------------------------------------------------- */ /* xode_pool_cleaner - callback type which is associated with a pool entry; invoked when the pool entry is free'd */ typedef void (*xode_pool_cleaner)(void *arg); /* pheap - singular allocation of memory */ struct xode_pool_heap { void *block; int size, used; }; /* pool - base node for a pool. Maintains a linked list of pool entries (pool_free) */ typedef struct xode_pool_struct { int size; struct xode_pool_free *cleanup; struct xode_pool_heap *heap; } _xode_pool, *xode_pool; /* pool creation routines */ xode_pool xode_pool_heap(int bytes); xode_pool xode_pool_new(void); /* pool wrappers for malloc */ void *xode_pool_malloc (xode_pool p, int size); void *xode_pool_mallocx (xode_pool p, int size, char c); void *xode_pool_malloco (xode_pool p, int size); /* wrapper around strdup, gains mem from pool */ char *xode_pool_strdup (xode_pool p, const char *src); /* calls f(arg) before the pool is freed during cleanup */ void xode_pool_cleanup (xode_pool p, xode_pool_cleaner f, void *arg); /* pool wrapper for free, called on a pool */ void xode_pool_free (xode_pool p); /* returns total bytes allocated in this pool */ int xode_pool_size (xode_pool p); /* --------------------------------------------------------- */ /* */ /* XML escaping utils */ /* */ /* --------------------------------------------------------- */ char *xode_strescape(xode_pool p, char *buf); /* Escape <>&'" chars */ char *xode_strunescape(xode_pool p, char *buf); /* --------------------------------------------------------- */ /* */ /* String pools (spool) functions */ /* */ /* --------------------------------------------------------- */ struct xode_spool_node { char *c; struct xode_spool_node *next; }; typedef struct xode_spool_struct { xode_pool p; int len; struct xode_spool_node *last; struct xode_spool_node *first; } *xode_spool; xode_spool xode_spool_new ( void ); /* create a string pool on a new pool */ xode_spool xode_spool_newfrompool ( xode_pool p ); /* create a string pool from an existing pool */ xode_pool xode_spool_getpool ( const xode_spool s ); /* returns the xode_pool used by this xode_spool */ void xode_spooler ( xode_spool s, ... ); /* append all the char * args to the pool, terminate args with s again */ char *xode_spool_tostr ( xode_spool s ); /* return a big string */ void xode_spool_add ( xode_spool s, char *str ); /* add a single char to the pool */ char *xode_spool_str ( xode_pool p, ... ); /* wrap all the spooler stuff in one function, the happy fun ball! */ int xode_spool_getlen ( const xode_spool s ); /* returns the total length of the string contained in the pool */ void xode_spool_free ( xode_spool s ); /* Free's the pool associated with the xode_spool */ /* --------------------------------------------------------- */ /* */ /* xodes - Document Object Model */ /* */ /* --------------------------------------------------------- */ #define XODE_TYPE_TAG 0 #define XODE_TYPE_ATTRIB 1 #define XODE_TYPE_CDATA 2 #define XODE_TYPE_LAST 2 #define XODE_TYPE_UNDEF -1 /* -------------------------------------------------------------------------- Node structure. Do not use directly! Always use accessors macros and methods! -------------------------------------------------------------------------- */ typedef struct xode_struct { char* name; unsigned short type; char* data; int data_sz; int complete; xode_pool p; struct xode_struct* parent; struct xode_struct* firstchild; struct xode_struct* lastchild; struct xode_struct* prev; struct xode_struct* next; struct xode_struct* firstattrib; struct xode_struct* lastattrib; } _xode, *xode; /* Node creation routines */ xode xode_wrap(xode x,const char* wrapper); xode xode_new(const char* name); xode xode_new_tag(const char* name); xode xode_new_frompool(xode_pool p, const char* name); xode xode_insert_tag(xode parent, const char* name); xode xode_insert_cdata(xode parent, const char* CDATA, unsigned int size); xode xode_insert_tagnode(xode parent, xode node); void xode_insert_node(xode parent, xode node); xode xode_from_str(char *str, int len); xode xode_from_strx(char *str, int len, int *err, int *pos); xode xode_from_file(char *file); xode xode_dup(xode x); /* duplicate x */ xode xode_dup_frompool(xode_pool p, xode x); /* Node Memory Pool */ xode_pool xode_get_pool(xode node); /* Node editing */ void xode_hide(xode child); void xode_hide_attrib(xode parent, const char *name); /* Node deletion routine, also frees the node pool! */ void xode_free(xode node); /* Locates a child tag by name and returns it */ xode xode_get_tag(xode parent, const char* name); char* xode_get_tagdata(xode parent, const char* name); /* Attribute accessors */ void xode_put_attrib(xode owner, const char* name, const char* value); char* xode_get_attrib(xode owner, const char* name); /* Bastard am I, but these are fun for internal use ;-) */ void xode_put_vattrib(xode owner, const char* name, void *value); void* xode_get_vattrib(xode owner, const char* name); /* Node traversal routines */ xode xode_get_firstattrib(xode parent); xode xode_get_firstchild(xode parent); xode xode_get_lastchild(xode parent); xode xode_get_nextsibling(xode sibling); xode xode_get_prevsibling(xode sibling); xode xode_get_parent(xode node); /* Node information routines */ char* xode_get_name(xode node); char* xode_get_data(xode node); int xode_get_datasz(xode node); int xode_get_type(xode node); int xode_has_children(xode node); int xode_has_attribs(xode node); /* Node-to-string translation */ char* xode_to_str(xode node); char* xode_to_prettystr(xode node); /* Puts \t and \n to make a human-easily readable string */ int xode_cmp(xode a, xode b); /* compares a and b for equality */ int xode_to_file(char *file, xode node); /* writes node to file */ /*********************** * XSTREAM Section ***********************/ #define XODE_STREAM_MAXNODE 1000000 #define XODE_STREAM_MAXDEPTH 100 #define XODE_STREAM_ROOT 0 /* root element */ #define XODE_STREAM_NODE 1 /* normal node */ #define XODE_STREAM_CLOSE 2 /* closed root node */ #define XODE_STREAM_ERROR 4 /* parser error */ typedef void (*xode_stream_onNode)(int type, xode x, void *arg); /* xstream event handler */ typedef struct xode_stream_struct { XML_Parser parser; xode node; char *cdata; int cdata_len; xode_pool p; xode_stream_onNode f; void *arg; int status; int depth; } *xode_stream, _xode_stream; xode_stream xode_stream_new(xode_pool p, xode_stream_onNode f, void *arg); /* create a new xstream */ int xode_stream_eat(xode_stream xs, char *buff, int len); /* parse new data for this xstream, returns last XSTREAM_* status */ /* convenience functions */ #ifdef __cplusplus } #endif #endif /* INCL_LIBXODE_H */ kamailio-4.0.4/obsolete/jabber_k/xjab_jcon.h0000644000000000000000000000624112223032460017461 0ustar rootroot/* * $Id$ * * eXtended JABber module - headers for functions used for JABBER srv connection * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _XJAB_JCON_H_ #define _XJAB_JCON_H_ #include "../../str.h" #include "xjab_jconf.h" #include "xjab_base.h" #include "tree234.h" #include "xjab_presence.h" #define XJ_NET_NUL 0 #define XJ_NET_ALL 0xFFFFFFFF #define XJ_NET_JAB 1 #define XJ_NET_AIM 2 #define XJ_NET_ICQ 4 #define XJ_NET_MSN 8 #define XJ_NET_YAH 16 #define XJ_AIM_NAME "aim." #define XJ_AIM_LEN 4 #define XJ_ICQ_NAME "icq" #define XJ_ICQ_LEN 3 #define XJ_MSN_NAME "msn." #define XJ_MSN_LEN 4 #define XJ_YAH_NAME "yahoo." #define XJ_YAH_LEN 6 #define XJ_JMSG_NORMAL 1 #define XJ_JMSG_CHAT 2 #define XJ_JMSG_GROUPCHAT 4 #define XJ_JCMD_SUBSCRIBE 1 #define XJ_JCMD_UNSUBSCRIBE 2 typedef struct _xj_jcon { int sock; // communication socket int port; // port of the server int juid; // internal id of the Jabber user int seq_nr; // sequence number char *hostname; // hostname of the Jabber server char *stream_id; // stream id of the session char *resource; // resource ID xj_jkey jkey; // id of connection int expire; // time when the open connection is expired int allowed; // allowed IM networks int ready; // time when the connection is ready for sending messages int nrjconf; // number of open conferences tree234 *jconf; // open conferences xj_pres_list plist; // presence list } t_xj_jcon, *xj_jcon; /** --- **/ xj_jcon xj_jcon_init(char*, int); int xj_jcon_free(xj_jcon); int xj_jcon_connect(xj_jcon); int xj_jcon_disconnect(xj_jcon); void xj_jcon_set_juid(xj_jcon, int); int xj_jcon_get_juid(xj_jcon); int xj_jcon_get_roster(xj_jcon); int xj_jcon_set_roster(xj_jcon, char*, char*); int xj_jcon_user_auth(xj_jcon, char*, char*, char*); int xj_jcon_send_presence(xj_jcon, char*, char*, char*, char*); int xj_jcon_send_subscribe(xj_jcon, char*, char*, char*); int xj_jcon_send_msg(xj_jcon, char*, int, char*, int, int); int xj_jcon_send_sig_msg(xj_jcon, char*, int, char*, int, char*, int); int xj_jcon_is_ready(xj_jcon, char *, int, char); xj_jconf xj_jcon_get_jconf(xj_jcon, str*, char); xj_jconf xj_jcon_check_jconf(xj_jcon, char*); int xj_jcon_del_jconf(xj_jcon, str*, char, int); int xj_jcon_jconf_presence(xj_jcon, xj_jconf, char*, char*); /********** ***/ int xj_jcon_set_attrs(xj_jcon, xj_jkey, int, int); int xj_jcon_update(xj_jcon, int); /********** ***/ #endif kamailio-4.0.4/obsolete/jabber_k/xjab_worker.h0000644000000000000000000000635312223032460020045 0ustar rootroot/* * $Id$ * * eXtended JABber module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2003-03-11 major locking changes - uses locking.h (andrei) * 2004-07-28 s/lock_set_t/gen_lock_set_t/ because of a type conflict * on darwin (andrei) */ #ifndef _XJAB_WORKER_H_ #define _XJAB_WORKER_H_ #include "../../str.h" #include "../../lib/srdb1/db.h" #include "../../locking.h" #include "../../modules/tm/tm_load.h" #include "xjab_util.h" #include "tree234.h" /********** ***/ typedef struct _xj_jalias { int size; // number of aliases str *jdm; // Jabber domain char dlm; // user part delimiter str *proxy; // outbound proxy str *a; // aliases char *d; // user part delimiter for aliases } t_xj_jalias, *xj_jalias; typedef struct _xj_worker { int pid; // process id int wpipe; // communication pipe - write int rpipe; // communication pipe - read int nr; // number of jobs tree234 *sip_ids; // sip ids allocated for the worker } t_xj_worker, *xj_worker; typedef struct _xj_wlist { int len; // length of the list int maxj; // maximum jobs / worker int cachet; int delayt; int sleept; gen_lock_set_t *sems; // semaphores xj_jalias aliases; // added aliases xj_worker workers; // the list of workers } t_xj_wlist, *xj_wlist; /********** LOOK AT IMPLEMENTATION OF FUNCTIONS FOR DESCRIPTION ***/ xj_wlist xj_wlist_init(int **, int, int, int, int, int); int xj_wlist_set_pid(xj_wlist, int, int); int xj_wlist_get(xj_wlist, xj_jkey, xj_jkey*); int xj_wlist_check(xj_wlist, xj_jkey, xj_jkey*); int xj_wlist_set_flag(xj_wlist, xj_jkey, int); void xj_wlist_del(xj_wlist, xj_jkey, int); void xj_wlist_free(xj_wlist); int xj_wlist_set_aliases(xj_wlist, char *, char *, char *); int xj_wlist_check_aliases(xj_wlist, str*); int xj_wlist_clean_jobs(xj_wlist, int, int); int xj_worker_process(xj_wlist, char*, int, char*, int, db1_con_t*, db_func_t*); int xj_address_translation(str *src, str *dst, xj_jalias als, int flag); int xj_manage_jab(char *buf, int len, int *pos, xj_jalias als, xj_jcon jbc); void xj_sig_handler(int s); /********** ***/ int xj_send_sip_msg(str *, str *, str *, str *, int *); int xj_send_sip_msgz(str *,str *, str *, char *, int *); void xj_tuac_callback( struct cell *t, int type, struct tmcb_params *ps); void xj_worker_check_jcons(xj_wlist, xj_jcon_pool, int, fd_set*); void xj_worker_check_qmsg(xj_wlist, xj_jcon_pool); void xj_worker_check_watcher(xj_wlist, xj_jcon_pool, xj_jcon, xj_sipmsg); #endif kamailio-4.0.4/obsolete/jabber_k/xode.c0000644000000000000000000004737212223032460016470 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include "xode.h" static int _xode_strcmp(const char *a, const char *b) { if(a == NULL || b == NULL) return -1; return strcmp(a,b); } /* Internal routines */ static xode _xode_new(xode_pool p, const char* name, unsigned int type) { xode result = NULL; if (type > XODE_TYPE_LAST) return NULL; if (type != XODE_TYPE_CDATA && name == NULL) return NULL; if (p == NULL) { p = xode_pool_heap(1*1024); } /* Allocate & zero memory */ result = (xode)xode_pool_malloc(p, sizeof(_xode)); memset(result, '\0', sizeof(_xode)); /* Initialize fields */ if (type != XODE_TYPE_CDATA) result->name = xode_pool_strdup(p,name); result->type = type; result->p = p; return result; } static xode _xode_appendsibling(xode lastsibling, const char* name, unsigned int type) { xode result; result = _xode_new(xode_get_pool(lastsibling), name, type); if (result != NULL) { /* Setup sibling pointers */ result->prev = lastsibling; lastsibling->next = result; } return result; } static xode _xode_insert(xode parent, const char* name, unsigned int type) { xode result; if(parent == NULL || name == NULL) return NULL; /* If parent->firstchild is NULL, simply create a new node for the first child */ if (parent->firstchild == NULL) { result = _xode_new(parent->p, name, type); parent->firstchild = result; } /* Otherwise, append this to the lastchild */ else { result= _xode_appendsibling(parent->lastchild, name, type); } result->parent = parent; parent->lastchild = result; return result; } static xode _xode_search(xode firstsibling, const char* name, unsigned int type) { xode current; /* Walk the sibling list, looking for a XODE_TYPE_TAG xode with the specified name */ current = firstsibling; while (current != NULL) { if (name != NULL && (current->type == type) && (_xode_strcmp(current->name, name) == 0)) return current; else current = current->next; } return NULL; } static char* _xode_merge(xode_pool p, char* dest, unsigned int destsize, const char* src, unsigned int srcsize) { char* result; result = (char*)xode_pool_malloc(p, destsize + srcsize + 1); memcpy(result, dest, destsize); memcpy(result+destsize, src, srcsize); result[destsize + srcsize] = '\0'; /* WARNING: major ugly hack: since we're throwing the old data away, let's jump in the xode_pool and subtract it from the size, this is for xmlstream's big-node checking */ p->size -= destsize; return result; } static void _xode_hidesibling(xode child) { if(child == NULL) return; if(child->prev != NULL) child->prev->next = child->next; if(child->next != NULL) child->next->prev = child->prev; } static void _xode_tag2str(xode_spool s, xode node, int flag) { xode tmp; if(flag==0 || flag==1) { xode_spooler(s,"<",xode_get_name(node),s); tmp = xode_get_firstattrib(node); while(tmp) { xode_spooler(s," ",xode_get_name(tmp),"='",xode_strescape(xode_get_pool(node),xode_get_data(tmp)),"'",s); tmp = xode_get_nextsibling(tmp); } if(flag==0) xode_spool_add(s,"/>"); else xode_spool_add(s,">"); } else { xode_spooler(s,"",s); } } static xode_spool _xode_tospool(xode node) { xode_spool s; int level=0,dir=0; xode tmp; if(!node || xode_get_type(node) != XODE_TYPE_TAG) return NULL; s = xode_spool_newfrompool(xode_get_pool(node)); if(!s) return(NULL); while(1) { if(dir==0) { if(xode_get_type(node) == XODE_TYPE_TAG) { if(xode_has_children(node)) { _xode_tag2str(s,node,1); node = xode_get_firstchild(node); level++; continue; } else { _xode_tag2str(s,node,0); } } else { xode_spool_add(s,xode_strescape(xode_get_pool(node),xode_get_data(node))); } } tmp = xode_get_nextsibling(node); if(!tmp) { node = xode_get_parent(node); level--; if(level>=0) _xode_tag2str(s,node,2); if(level<1) break; dir = 1; } else { node = tmp; dir = 0; } } return s; } /* External routines */ /* * xode_new_tag -- create a tag node * Automatically creates a memory xode_pool for the node. * * parameters * name -- name of the tag * * returns * a pointer to the tag node * or NULL if it was unsuccessful */ xode xode_new(const char* name) { return _xode_new(NULL, name, XODE_TYPE_TAG); } /* * alias for 'xode_new' */ xode xode_new_tag(const char* name) { return _xode_new(NULL, name, XODE_TYPE_TAG); } /* * xode_new_tag_pool -- create a tag node within given pool * * parameters * p -- previously created memory pool * name -- name of the tag * * returns * a pointer to the tag node * or NULL if it was unsuccessful */ xode xode_new_frompool(xode_pool p, const char* name) { return _xode_new(p, name, XODE_TYPE_TAG); } /* * xode_insert_tag -- append a child tag to a tag * * parameters * parent -- pointer to the parent tag * name -- name of the child tag * * returns * a pointer to the child tag node * or NULL if it was unsuccessful */ xode xode_insert_tag(xode parent, const char* name) { return _xode_insert(parent, name, XODE_TYPE_TAG); } /* * xode_insert_cdata -- append character data to a tag * If last child of the parent is CDATA, merges CDATA nodes. Otherwise * creates a CDATA node, and appends it to the parent's child list. * * parameters * parent -- parent tag * CDATA -- character data * size -- size of CDATA * or -1 for null-terminated CDATA strings * * returns * a pointer to the child CDATA node * or NULL if it was unsuccessful */ xode xode_insert_cdata(xode parent, const char* CDATA, unsigned int size) { xode result; if(CDATA == NULL || parent == NULL) return NULL; if(size == -1) size = strlen(CDATA); if ((parent->lastchild != NULL) && (parent->lastchild->type == XODE_TYPE_CDATA)) { result = parent->lastchild; result->data = _xode_merge(result->p, result->data, result->data_sz, CDATA, size); result->data_sz = result->data_sz + size; } else { result = _xode_insert(parent, "", XODE_TYPE_CDATA); if (result != NULL) { result->data = (char*)xode_pool_malloc(result->p, size + 1); memcpy(result->data, CDATA, size); result->data[size] = '\0'; result->data_sz = size; } } return result; } /* * xode_gettag -- find given tag in an xode tree * * parameters * parent -- pointer to the parent tag * name -- "name" for the child tag of that name * "name/name" for a sub child (recurses) * "?attrib" to match the first tag with that attrib defined * "?attrib=value" to match the first tag with that attrib and value * or any combination: "name/name/?attrib", etc * * results * a pointer to the tag matching search criteria * or NULL if search was unsuccessful */ xode xode_get_tag(xode parent, const char* name) { char *str, *slash, *qmark, *equals; xode step, ret; if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL; if(strstr(name, "/") == NULL && strstr(name,"?") == NULL) return _xode_search(parent->firstchild, name, XODE_TYPE_TAG); /* jer's note: why can't I modify the name directly, why do I have to strdup it? damn c grrr! */ str = strdup(name); slash = strstr(str, "/"); qmark = strstr(str, "?"); equals = strstr(str, "="); if(qmark != NULL && (slash == NULL || qmark < slash)) { /* of type ?attrib */ *qmark = '\0'; qmark++; if(equals != NULL) { *equals = '\0'; equals++; } for(step = parent->firstchild; step != NULL; step = xode_get_nextsibling(step)) { if(xode_get_type(step) != XODE_TYPE_TAG) continue; if(*str != '\0') if(_xode_strcmp(xode_get_name(step),str) != 0) continue; if(xode_get_attrib(step,qmark) == NULL) continue; if(equals != NULL && _xode_strcmp(xode_get_attrib(step,qmark),equals) != 0) continue; break; } free(str); return step; } *slash = '\0'; ++slash; for(step = parent->firstchild; step != NULL; step = xode_get_nextsibling(step)) { if(xode_get_type(step) != XODE_TYPE_TAG) continue; if(_xode_strcmp(xode_get_name(step),str) != 0) continue; ret = xode_get_tag(step, slash); if(ret != NULL) { free(str); return ret; } } free(str); return NULL; } /* return the cdata from any tag */ char *xode_get_tagdata(xode parent, const char *name) { xode tag; tag = xode_get_tag(parent, name); if(tag == NULL) return NULL; return xode_get_data(tag); } void xode_put_attrib(xode owner, const char* name, const char* value) { xode attrib; if(owner == NULL || name == NULL || value == NULL) return; /* If there are no existing attributes, allocate a new one to start the list */ if (owner->firstattrib == NULL) { attrib = _xode_new(owner->p, name, XODE_TYPE_ATTRIB); owner->firstattrib = attrib; owner->lastattrib = attrib; } else { attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); if(attrib == NULL) { attrib = _xode_appendsibling(owner->lastattrib, name, XODE_TYPE_ATTRIB); owner->lastattrib = attrib; } } /* Update the value of the attribute */ attrib->data_sz = strlen(value); attrib->data = xode_pool_strdup(owner->p, value); } char* xode_get_attrib(xode owner, const char* name) { xode attrib; if (owner != NULL && owner->firstattrib != NULL) { attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); if (attrib != NULL) return (char*)attrib->data; } return NULL; } void xode_put_vattrib(xode owner, const char* name, void *value) { xode attrib; if (owner != NULL) { attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); if (attrib == NULL) { xode_put_attrib(owner, name, ""); attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); } if (attrib != NULL) attrib->firstchild = (xode)value; } } void* xode_get_vattrib(xode owner, const char* name) { xode attrib; if (owner != NULL && owner->firstattrib != NULL) { attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB); if (attrib != NULL) return (void*)attrib->firstchild; } return NULL; } xode xode_get_firstattrib(xode parent) { if (parent != NULL) return parent->firstattrib; return NULL; } xode xode_get_firstchild(xode parent) { if (parent != NULL) return parent->firstchild; return NULL; } xode xode_get_lastchild(xode parent) { if (parent != NULL) return parent->lastchild; return NULL; } xode xode_get_nextsibling(xode sibling) { if (sibling != NULL) return sibling->next; return NULL; } xode xode_get_prevsibling(xode sibling) { if (sibling != NULL) return sibling->prev; return NULL; } xode xode_get_parent(xode node) { if (node != NULL) return node->parent; return NULL; } char* xode_get_name(xode node) { if (node != NULL) return node->name; return NULL; } char* xode_get_data(xode node) { xode cur; if(node == NULL) return NULL; if(xode_get_type(node) == XODE_TYPE_TAG) /* loop till we find a CDATA */ { for(cur = xode_get_firstchild(node); cur != NULL; cur = xode_get_nextsibling(cur)) if(xode_get_type(cur) == XODE_TYPE_CDATA) return cur->data; }else{ return node->data; } return NULL; } int xode_get_datasz(xode node) { if( node == NULL ) { return (int)(long)NULL; } else if(xode_get_type(node) == XODE_TYPE_TAG) /* loop till we find a CDATA */ { xode cur; for(cur = xode_get_firstchild(node); cur != NULL; cur = xode_get_nextsibling(cur)) if(xode_get_type(cur) == XODE_TYPE_CDATA) return cur->data_sz; }else{ return node->data_sz; } return (int)(long)NULL; } int xode_get_type(xode node) { if (node != NULL) { return node->type; } return (int)(long)NULL; } int xode_has_children(xode node) { if ((node != NULL) && (node->firstchild != NULL)) return 1; return 0; } int xode_has_attribs(xode node) { if ((node != NULL) && (node->firstattrib != NULL)) return 1; return 0; } xode_pool xode_get_pool(xode node) { if (node != NULL) return node->p; return (xode_pool)NULL; } void xode_hide(xode child) { xode parent; if(child == NULL || child->parent == NULL) return; parent = child->parent; /* first fix up at the child level */ _xode_hidesibling(child); /* next fix up at the parent level */ if(parent->firstchild == child) parent->firstchild = child->next; if(parent->lastchild == child) parent->lastchild = child->prev; } void xode_hide_attrib(xode parent, const char *name) { xode attrib; if(parent == NULL || parent->firstattrib == NULL || name == NULL) return; attrib = _xode_search(parent->firstattrib, name, XODE_TYPE_ATTRIB); if(attrib == NULL) return; /* first fix up at the child level */ _xode_hidesibling(attrib); /* next fix up at the parent level */ if(parent->firstattrib == attrib) parent->firstattrib = attrib->next; if(parent->lastattrib == attrib) parent->lastattrib = attrib->prev; } /* * xode2str -- convert given xode tree into a string * * parameters * node -- pointer to the xode structure * * results * a pointer to the created string * or NULL if it was unsuccessful */ char *xode_to_str(xode node) { return xode_spool_tostr(_xode_tospool(node)); } /* loop through both a and b comparing everything, attribs, cdata, children, etc */ int xode_cmp(xode a, xode b) { int ret = 0; while(1) { if(a == NULL && b == NULL) return 0; if(a == NULL || b == NULL) return -1; if(xode_get_type(a) != xode_get_type(b)) return -1; switch(xode_get_type(a)) { case XODE_TYPE_ATTRIB: ret = _xode_strcmp(xode_get_name(a), xode_get_name(b)); if(ret != 0) return -1; ret = _xode_strcmp(xode_get_data(a), xode_get_data(b)); if(ret != 0) return -1; break; case XODE_TYPE_TAG: ret = _xode_strcmp(xode_get_name(a), xode_get_name(b)); if(ret != 0) return -1; ret = xode_cmp(xode_get_firstattrib(a), xode_get_firstattrib(b)); if(ret != 0) return -1; ret = xode_cmp(xode_get_firstchild(a), xode_get_firstchild(b)); if(ret != 0) return -1; break; case XODE_TYPE_CDATA: ret = _xode_strcmp(xode_get_data(a), xode_get_data(b)); if(ret != 0) return -1; } a = xode_get_nextsibling(a); b = xode_get_nextsibling(b); } } xode xode_insert_tagnode(xode parent, xode node) { xode child; child = xode_insert_tag(parent, xode_get_name(node)); if (xode_has_attribs(node)) xode_insert_node(child, xode_get_firstattrib(node)); if (xode_has_children(node)) xode_insert_node(child, xode_get_firstchild(node)); return child; } /* places copy of node and node's siblings in parent */ void xode_insert_node(xode parent, xode node) { if(node == NULL || parent == NULL) return; while(node != NULL) { switch(xode_get_type(node)) { case XODE_TYPE_ATTRIB: xode_put_attrib(parent, xode_get_name(node), xode_get_data(node)); break; case XODE_TYPE_TAG: xode_insert_tagnode(parent, node); break; case XODE_TYPE_CDATA: xode_insert_cdata(parent, xode_get_data(node), xode_get_datasz(node)); } node = xode_get_nextsibling(node); } } /* produce full duplicate of x with a new xode_pool, x must be a tag! */ xode xode_dup(xode x) { xode x2; if(x == NULL) return NULL; x2 = xode_new(xode_get_name(x)); if (xode_has_attribs(x)) xode_insert_node(x2, xode_get_firstattrib(x)); if (xode_has_children(x)) xode_insert_node(x2, xode_get_firstchild(x)); return x2; } xode xode_dup_frompool(xode_pool p, xode x) { xode x2; if(x == NULL) return NULL; x2 = xode_new_frompool(p, xode_get_name(x)); if (xode_has_attribs(x)) xode_insert_node(x2, xode_get_firstattrib(x)); if (xode_has_children(x)) xode_insert_node(x2, xode_get_firstchild(x)); return x2; } xode xode_wrap(xode x,const char *wrapper) { xode wrap; if(x==NULL||wrapper==NULL) return NULL; wrap=xode_new_frompool(xode_get_pool(x),wrapper); if(wrap==NULL) return NULL; wrap->firstchild=x; wrap->lastchild=x; x->parent=wrap; return wrap; } void xode_free(xode node) { if(node == NULL) return; xode_pool_free(node->p); } void _xode_to_prettystr( xode_spool s, xode x, int deep ) { int i; xode y; if(xode_get_type(x) != XODE_TYPE_TAG) return; for(i=0; i"); xode_spool_add(s,"\n"); if( xode_get_data(x)) { for(i=0; i<=deep; i++) xode_spool_add(s, "\t"); xode_spool_add( s , xode_get_data(x)); } y = xode_get_firstchild(x); while( y ) { _xode_to_prettystr(s , y, deep+1); y = xode_get_nextsibling(y); xode_spool_add(s,"\n"); } for(i=0; i" , s ); return; } char * xode_to_prettystr( xode x ) { xode_spool s; if( !x) return NULL; s = xode_spool_newfrompool( xode_get_pool(x)); _xode_to_prettystr( s , x, 0 ); return xode_spool_tostr(s); } kamailio-4.0.4/obsolete/jabber_k/xjab_base.h0000644000000000000000000000420712223032460017442 0ustar rootroot/** * $Id$ * * eXtended JABber module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*** * --- * * History * ------- * 2003-06-05 previously added macro replaced with 'xj_extract_aor', (dcm) * 2003-05-09 added macro for adjusting a SIP address, (dcm) */ #ifndef _XJAB_BASE_H_ #define _XJAB_BASE_H_ #include "../../str.h" #define XJ_NULL 0 #define XJ_SEND_MESSAGE 1 #define XJ_JOIN_JCONF 2 #define XJ_EXIT_JCONF 4 #define XJ_GO_ONLINE 8 #define XJ_GO_OFFLINE 16 #define XJ_REG_WATCHER 32 #define XJ_DEL_WATCHER 64 #define XJ_FLAG_OPEN 0 #define XJ_FLAG_CLOSE 1 typedef void (*pa_callback_f)(str* _user, str* _contact, int _state, void *p); /********** ***/ typedef struct _xj_jkey { int hash; int flag; str *id; } t_xj_jkey, *xj_jkey; /********** ***/ typedef struct _xj_sipmsg { int type; // type of message xj_jkey jkey; // pointer to FROM str to; // destination str msg; // message body pa_callback_f cbf; // callback function void *p; // callback parameter } t_xj_sipmsg, *xj_sipmsg; /********** LOOK AT IMPLEMENTATION OF FUNCTIONS FOR DESCRIPTION ***/ void xj_sipmsg_free(xj_sipmsg); /********** ***/ int xj_jkey_cmp(void*, void*); void xj_jkey_free_p(void*); void xj_jkey_free(xj_jkey); /********** ***/ int xj_get_hash(str*, str*); char *shahash(const char *); int xj_extract_aor(str*, int); #endif kamailio-4.0.4/obsolete/jabber_k/xjab_jconf.c0000644000000000000000000001236112223032460017622 0ustar rootroot/* * $Id$ * * eXtended JABber module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "xjab_jconf.h" #include "xjab_base.h" /** * */ xj_jconf xj_jconf_new(str *u) { xj_jconf jcf = NULL; if(!u || !u->s || u->len<=0) return NULL; jcf = (xj_jconf)pkg_malloc(sizeof(t_xj_jconf)); if(jcf == NULL) { LM_DBG("no pkg memory.\n"); return NULL; } jcf->uri.s = (char*)pkg_malloc((u->len+1)*sizeof(char)); if(jcf->uri.s == NULL) { LM_DBG("no pkg memory!\n"); pkg_free(jcf); return NULL; } strncpy(jcf->uri.s, u->s, u->len); jcf->uri.len = u->len; jcf->uri.s[jcf->uri.len] = 0; jcf->jcid = 0; jcf->status = XJ_JCONF_NULL; jcf->room.s = NULL; jcf->room.len = 0; jcf->server.s = NULL; jcf->server.len = 0; jcf->nick.s = NULL; jcf->nick.len = 0; return jcf; } /** * */ int xj_jconf_init_sip(xj_jconf jcf, str *sid, char dl) { char *p, *p0; int n = 0; if(!jcf || !jcf->uri.s || jcf->uri.len <= 0 || !sid || !sid->s || sid->len <= 0) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("parsing uri\n"); #endif p = jcf->uri.s; while(p<(jcf->uri.s + jcf->uri.len) && *p != '@') p++; if(*p != '@') goto bad_format; p0 = p; while(p0 > jcf->uri.s) { p0--; if(*p0 == dl) { switch(n) { case 0: jcf->server.s = p0+1; jcf->server.len = p - jcf->server.s; break; case 1: jcf->room.s = p0+1; jcf->room.len = p - jcf->room.s; break; case 2: jcf->nick.s = p0+1; jcf->nick.len = p - jcf->nick.s; break; } n++; p = p0; } } if(n != 2 || p0 != jcf->uri.s) goto bad_format; if(p0 == jcf->uri.s && *p0 != dl) { jcf->nick.s = p0; jcf->nick.len = p - jcf->nick.s; } else { jcf->nick.s = p = sid->s; while(p < sid->s + sid->len && *p!='@') { if(*p == ':') jcf->nick.s = p+1; p++; } jcf->nick.len = p - jcf->nick.s; } jcf->jcid = xj_get_hash(&jcf->room, &jcf->server); #ifdef XJ_EXTRA_DEBUG LM_DBG("conference id=%d\n", jcf->jcid); #endif return 0; bad_format: LM_ERR("failed to parse uri - bad format\n"); return -2; } /** * */ int xj_jconf_init_jab(xj_jconf jcf) { char *p, *p0; if(!jcf || !jcf->uri.s || jcf->uri.len <= 0) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("parsing uri\n"); #endif p = jcf->uri.s; while(p<(jcf->uri.s + jcf->uri.len) && *p != '@') p++; if(*p != '@' || p==jcf->uri.s) goto bad_format; p0 = p+1; while(p0 < ((jcf->uri.s + jcf->uri.len)) && *p0 != '/') p0++; jcf->server.s = p+1; jcf->server.len = p0 - jcf->server.s; jcf->room.s = jcf->uri.s; jcf->room.len = p - jcf->room.s; if(p0 < jcf->uri.s + jcf->uri.len) { jcf->nick.s = p0+1; jcf->nick.len = jcf->uri.s + jcf->uri.len - jcf->nick.s; } jcf->jcid = xj_get_hash(&jcf->room, &jcf->server); #ifdef XJ_EXTRA_DEBUG LM_DBG("conference id=%d\n", jcf->jcid); #endif return 0; bad_format: LM_ERR("failed to parse uri - bad format\n"); return -2; } /** * */ int xj_jconf_set_status(xj_jconf jcf, int s) { if(!jcf || !jcf->uri.s || jcf->uri.len <= 0) return -1; jcf->status = s; return 0; } /** * */ int xj_jconf_cmp(void *a, void *b) { int n; if(a == NULL) return -1; if(b == NULL) return 1; // LM_DBG("comparing <%.*s> / <%.*s>\n",((str *)a)->len, // ((str *)a)->s, ((str *)b)->len, ((str *)b)->s); if(((xj_jconf)a)->jcid < ((xj_jconf)b)->jcid) return -1; if(((xj_jconf)a)->jcid > ((xj_jconf)b)->jcid) return 1; if(((xj_jconf)a)->room.len < ((xj_jconf)b)->room.len) return -1; if(((xj_jconf)a)->room.len > ((xj_jconf)b)->room.len) return 1; if(((xj_jconf)a)->server.len < ((xj_jconf)b)->server.len) return -1; if(((xj_jconf)a)->server.len > ((xj_jconf)b)->server.len) return 1; n = strncmp(((xj_jconf)a)->room.s, ((xj_jconf)b)->room.s, ((xj_jconf)a)->room.len); if(n<0) return -1; if(n>0) return 1; n = strncmp(((xj_jconf)a)->server.s, ((xj_jconf)b)->server.s, ((xj_jconf)a)->server.len); if(n<0) return -1; if(n>0) return 1; return 0; } /** * */ int xj_jconf_free(xj_jconf jcf) { if(!jcf) return 0; if(jcf->uri.s != NULL) pkg_free(jcf->uri.s); pkg_free(jcf); jcf = NULL; return 0; } /** * */ int xj_jconf_check_addr(str *addr, char dl) { char *p; int i; if(!addr || !addr->s || addr->len <= 0) return -1; p = addr->s; i= 0; while((p < addr->s+addr->len) && *p != '@') { if(*p==dl) i++; p++; } if(i==2 && *p=='@') return 0; return -1; } kamailio-4.0.4/obsolete/jabber_k/doc/0000755000000000000000000000000012223032460016115 5ustar rootrootkamailio-4.0.4/obsolete/jabber_k/doc/jabber_admin.xml0000644000000000000000000004627512223032460021252 0ustar rootroot %docentities; ]> &adminguide;
Overview This is new version of Jabber module that integrates XODE XML parser for parsing Jabber messages. That introduces a new module dependency: expat library. Expat is a common XML library and is the fastest available for Linux/Unix, the second over all, after msxml library. It is integrated in most of well known Linux distributions. Please not that this module is deprecated and will be removed in the next major release of &kamailio;. The purple module should be used instead.
New Features Presence support (see doc/xxjab.cfg for a sample cfg file) (January 2003). SIP to Jabber conference support (December 2003). Possibility to manage all kinds of Jabber messages (message/presence/iq) (December 2003). Aliases -- Possibility to set host aliases for addresses (see parameter's desc.) (December 2003). Send received &sip; MESSAGE messages to different &im; networks (Jabber, ICQ,MSN, AIM, Yahoo) using a Jabber server (December 2003). Send incoming Jabber instant messages as &sip; MESSAGE messages. Gateways detection -- Ability to see whether an &im; gateway is up or down.
Admin's Guide A more complete guide about SIMPLE2Jabber gateway can be found at &kamailiohomelink;. The part below will be removed soon, only the manual from web will be updated. The Jabber server setup is not a subject of this guide. Check http://www.jabber.org for that. Useful scripts, for creating Jabber Gateway database, or for managing the Jabber accounts form web are located in 'doc' subdirectory of the module. Main steps of using the Jabber gateway: Create the MySQL database. Setup the local Jabber server. Set the module parameter values in cfg file of &kamailio;, load the dependent modules, set up the routing rules for Jabber gateway. Run &kamailio;. The administrator of &kamailio;/Jabber gateway must inform the users what are the aliases for Jabber/Other &im; networks. Other &im;s could be AIM, ICQ, MSN, Yahoo, and so on. These aliases depend on the server hostname where runs &kamailio; and how local Jabber server is setup. Next is presented a use case. Prologue: &kamailio; is running on server.org. Local Jabber server is running on jabsrv.server.org. Jabber network alias (first part of jdomain) is jabber.server.org The aliases for other &im; networks must be the same as JID set in Jabber configuration file for each &im; transport. The JIDs of Jabber transports must start with the name of the network. For AIM, JID must start with aim., for ICQ with icq (that because I use icqv7-t), for MSN with msn. and for Yahoo with yahoo.. The gateway needs these to find out what transport is working and which not. For our use case these could be like aim.server.org, icq.server.org, msn.server.org, yahoo.server.org. It is indicated to have these aliases in DNS, thus the client application can resolve the DNS name. Otherwise there must be set the outbound proxy to &kamailio; server. *** Routing rules for Jabber gateway First step is to configure &kamailio; to recognize messages for Jabber gateway. Look at doc/xjab.cfg to see a sample. The idea is to look in messages for destination address and if it contains Jabber alias or other &im; alias, that means the message is for Jabber gateway. Next step is to find out what means that message for Jabber gateway. It could be a special message what triggers the gateway to take an action or is a simple message which should be delivered to Jabber network (using the method jab_send_message). The special messages are for: Registering to Jabber server (go online in Jabber network)--here must be called jab_go_online method. Leaving the Jabber network (go offline in Jabber network)--here must be called jab_go_offline method. Joining a Jabber conference room--here must be called jab_join_jconf. Leaving a Jabber conference room--here must be called jab_exit_jconf. The destination address must follow the following patterns: For Jabber network: username<delim>jabber_server@jabber_alias. For Jabber conference: nickname<delim>room<delim>conference_server@jabber_alias. For AIM network: aim_username@aim_alias. For ICQ network: icq_number@icq_alias. For MSN network: msn_username<delim>msn_server@msn_alias. msn_server can be msn.com or hotmail.com. For YAHOO network: yahoo_username@yahoo_alias. jabber_alias is the first part of jdomain.
&adminguide; The user must activate his Jabber account associated with his &sip; id. For each other &im; network on which he wants to send messages, he must set an account for that &im; network. The gateway is not able to create new account in foreign networks, excepting local Jabber server. When you want to send a message to someone in other &im; network, you must set the destination of the message according with the pattern corresponding to that &im; network (see last part of Admin guide chapter). Sending a message to user@jabber.xxx.org which is in Jabber network, the destination must be: user<delim>jabber.xxx.org@jabber_alias. For someone who is in Yahoo network the destination must be: user@yahoo_alias The &kamailio; administrator have to set the Jabber transports for each &im; network in order to be able to send messages to those networks. The alias of each &im; network can be found out from &kamailio; admin. You cannot send messages from your &sip; client to your associated Jabber account--is something like sending messages to yourself.
Dependencies
&kamailio; Modules The following modules must be loaded before this module: A database module. pa (Optionally) - Presence Agent. tm - Transaction Manager.
External Libraries or Applications The following libraries or applications must be installed before running &kamailio; with this module loaded: Expat library.
Exported Parameters
<varname>db_url</varname> (string) SQL &url; of database. Default value is mysql://root@127.0.0.1/sip_jab. Set <varname>db_url</varname> parameter ... modparam("jabber", "db_url", "mysql://username:password@host/sip_jab") ...
<varname>jaddress</varname> (string) &ip; or hostname of Jabber server -- it must be the same as the value from <host> tag of Jabber server config file. Default value is 127.0.0.1. Set <varname>jaddress</varname> parameter ... modparam("jabber", "jaddress", "1.2.3.4") ...
<varname>jport</varname> (integer) Port number of Jabber server. Default value is 5222. Set <varname>jport</varname> parameter ... modparam("jabber", "jport", 1234) ...
<varname>jdomain</varname> (string) Format: jabber.sipserver.com=<delim>. If the destination is for Jabber network the &uri; should be like: username<delim>jabber_server@jdomain or nickname<delim>roomname<delim>conference_server@jdomain <delim> must be a un-reserved character. By default this character is * . The destination will be transformed to username@jabber_server or roomname@conference_server/nickname before the message is sent to Jabber server. Default value is none. Set <varname>jdomain</varname> parameter ... modparam("jabber", "jdomain", "jabber.sipserver.com=*") ...
<varname>aliases</varname> (string) Aliases for &im; networks. Format: N;alias1=<delim1>;...;aliasN=<delimN>; Destinations like '*@aliasX' could have other format than those specified for Jabber network. All <delim> from user part of the destination address will be changed to <delimX> if the destination address contains <aliasX>. (Ex: jdomain is 'jabber.x.com=*' and msn_alias is 'msn.x.com=%'. The destination address forM MSN Network, on &sip; side, is like 'username*hotmail.com@msn.x.com'. The destination address will be transformed to 'username%hotmail.com@msn.x.com'. 'msn.x.com' must be the same as the JID associated with MSN transport in Jabber configuration file (usually is 'jabberd.xml')) Default value is none. Set <varname>jdomain</varname> parameter ... modparam("jabber", "aliases", "1;msn.x.com=%") ...
<varname>proxy</varname> (string) Outbound proxy address. Format: ip_address:port hostname:port All &sip; messages generated by gateway will be sent to that address. If is missing, the message will be delivered to the hostname of the destination address Default value is none. Set <varname>proxy</varname> parameter ... modparam("jabber", "proxy", "10.0.0.1:5060 sipserver.com:5060") ...
<varname>registrar</varname> (string) The address in whose behalf the INFO and ERROR messages are sent. Default value is jabber_gateway@127.0.0.1. Set <varname>registrar</varname> parameter ... modparam("jabber", "registrar", "jabber_gateway@127.0.0.1") ...
<varname>workers</varname> (integer) Number of workers. Default value is 2. Set <varname>workers</varname> parameter ... modparam("jabber", "workers", 2) ...
<varname>max_jobs</varname> (integer) Maximum jobs per worker. Default value is 10. Set <varname>max_jobs</varname> parameter ... modparam("jabber", "max_jobs", 10) ...
<varname>cache_time</varname> (integer) Cache time of a Jabber connection. Default value is 600. Set <varname>cache_time</varname> parameter ... modparam("jabber", "cache_time", 600) ...
<varname>delay_time</varname> (integer) Time to keep a &sip; message (in seconds). Default value is 90 seconds. Set <varname>delay_time</varname> parameter ... modparam("jabber", "delay_time", 90) ...
<varname>sleep_time</varname> (integer) Time between expired Jabber connections checking (in seconds). Default value is 20 seconds. Set <varname>sleep_time</varname> parameter ... modparam("jabber", "sleep_time", 20) ...
<varname>check_time</varname> (integer) Time between checking the status of JabberGW workers (in seconds). Default value is 20 seconds. Set <varname>check_time</varname> parameter ... modparam("jabber", "check_time", 20) ...
<varname>priority</varname> (str) Presence priority for Jabber gateway. Default value is 9. Set <varname>priority</varname> parameter ... modparam("jabber", "priority", "3") ...
Exported Functions
<function moreinfo="none">jab_send_message()</function> Converts &sip; MESSAGE message to a Jabber message and sends it to Jabber server. This function can be used from REQUEST_ROUTE. <function>jab_send_message()</function> usage ... jab_send_message(); ...
<function moreinfo="none">jab_join_jconf()</function> Join a Jabber conference--the nickname, room name and conference server address should be included in To header as: nickname%roomname%conference_server@jdomain . If the nickname is missing, then the &sip; username is used. This function can be used from REQUEST_ROUTE. <function>jab_join_jconf()</function> usage ... jab_join_jconf(); ...
<function moreinfo="none">jab_exit_jconf()</function> Leave a Jabber conference--the nickname, room name and conference server address should be included in To header as: nickname%roomname%conference_server@jdomain . This function can be used from REQUEST_ROUTE. <function>jab_exit_jconf()</function> usage ... jab_exit_jconf(); ...
<function moreinfo="none">jab_go_online()</function> Register to the Jabber server with associated Jabber ID of the &sip; user. This function can be used from REQUEST_ROUTE. <function>jab_go_online()</function> usage ... jab_go_online(); ...
<function moreinfo="none">jab_go_offline()</function> Log off from Jabber server the associated Jabber ID of the &sip; user. This function can be used from REQUEST_ROUTE. <function>jab_go_offline()</function> usage ... jab_go_offline(); ...
kamailio-4.0.4/obsolete/jabber_k/doc/web/0000755000000000000000000000000012223032460016672 5ustar rootrootkamailio-4.0.4/obsolete/jabber_k/doc/web/subscribe.php0000644000000000000000000003326112223032460021371 0ustar rootroot"; exit(); } # function dbg_msg($message) { # echo "$message"; } ?> IM Gateway registration
Subscription page for Instant Messaging gateway
You MUST have a SIP account


SIP username:
SIP password:

My Jabber account:      

IM service:
IM nickname:
IM account:
IM password:

     


User info

- for any operation you MUST provide your username and password

AIM Gateway subscription
- choose 'AIM' as 'IM service'
- 'IM nickname' is your display name for AIM network
- 'IM account' is your AIM account name (ex: 'alpha')
- 'IM password' is the password of your AIM account
- click on 'Subscribe'
ICQ Gateway subscription
- choose 'ICQ' as 'IM service'
- 'IM nickname' is your display name for ICQ network
- 'IM account' is your ICQ number (ex: '158251040')
- 'IM password' is the password of your ICQ account
- click on 'Subscribe'
MSN Gateway subscription
- choose 'MSN' as 'IM service'
- 'IM nickname' is your display name for MSN network
- 'IM account' is your MSN account (ex: 'alpha@hotmail.com' or 'alpha@msn.com')
- 'IM password' is the password of your MSN account
- click on 'Subscribe'
Yahoo Gateway subscription
- choose 'Yahoo' as 'IM service'
- 'IM nickname' is your display name for Yahoo network
- 'IM account' is your Yahoo account (ex: 'alpha')
- 'IM password' is the password of your Yahoo account
- click on 'Subscribe'

IM Gateway unsubscription
- choose the 'IM service' from which you want to unsubscribe
- click on 'Unsubscribe'

Instant Messaging Gateway



"; $dblink = mysql_connect($sip_db_srv, $sip_db_usr, $sip_db_pas) or html_die("Could not connect to SIP database server"); mysql_select_db($sip_db_db, $dblink) or html_die("Could not select SIP database"); $query = "SELECT $sip_db_cusr FROM $sip_db_tab WHERE $sip_db_cusr='$sipname' AND $sip_db_cpas='$sippasswd'"; dbg_msg("$query
"); $result = mysql_query($query) or html_die("Invalid SQL query"); if(mysql_num_rows($result) == 0) html_die("Invalid SIP username or password"); mysql_close($dblink); ***************************************** */ # #------------------------------------------------------ # # ----- # check if is already registered to Jabber gateway # ----- $sipuri = "sip:".$sipname."@example.org"; $dblink = mysql_connect($jab_db_srv, $jab_db_usr, $jab_db_pas) or html_die("Could not connect to Jabber database"); mysql_select_db($jab_db_db, $dblink) or html_die("Could not use Jabber database"); # ---- if($action == "Disable") { $query = "UPDATE jusers SET tyep=1 WHERE sip_id='$sipuri'"; $result = mysql_query($query, $dblink); if(mysql_affected_rows() != 1) { mysql_close($dblink); html_die("
Cannot find Jabber ID of '$sipname' in database"); } mysql_close($dblink); html_die("
Your IM account was updated"); } # ---- $query = "SELECT jab_id FROM jusers WHERE sip_id='$sipuri'"; $result = mysql_query($query, $dblink) or html_die("Invalid SQL query"); if(mysql_num_rows($result) == 0) { // no Jabber account - create one $fd = jab_connect($jserver, $jport); if(!$fd) html_die("Could not connect to Jabber server"); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $jid1 = stristr($buf_recv, "id='"); $jid1 = substr($jid1, 4); if($jid1) { $jid2 = strstr($jid1, "'"); if($jid2) { $jid = substr($jid1, 0, strlen($jid1)-strlen($jid2)); dbg_msg("JID: $jid
"); } } $jcid = $jcid + 1; jab_get_reg($fd, $jcid, $jserver); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $jcid = $jcid + 1; $new_passwd = "#".$sipname."%"; jab_set_reg($fd, $jcid, $jserver, $sipname, $new_passwd); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } if(stristr($buf_recv, " id='$jcid'") && stristr($buf_recv, " type='error'")) { mysql_close($dblink); jab_disconnect($fd); html_die("
Something bizarre with account '$sipname'"); } # ----- # Add user in database # ----- $query = "INSERT INTO jusers (jab_id, jab_passwd, sip_id) VALUES ('$sipname', '$new_passwd', '$sipuri')"; $result = mysql_query($query, $dblink); if(mysql_affected_rows() != 1) { mysql_close($dblink); jab_disconnect($fd); html_die("
Can not insert '$sipname' in database"); } jab_disconnect($fd); } # ----- if($action == "Enable") { $query = "UPDATE jusers SET type=0 WHERE sip_id='$sipuri'"; $result = mysql_query($query, $dblink); if(mysql_affected_rows() != 1) { mysql_close($dblink); html_die("
Cannot find Jabber ID of '$sipname' in database"); } mysql_close($dblink); html_die("
Your IM account was updated"); } # ----- $query="SELECT juid,jab_id,jab_passwd,type FROM jusers WHERE sip_id='$sipuri' and type=0"; $result = mysql_query($query, $dblink) or html_die("Invalid SQL query"); if(mysql_num_rows($result) != 1 || (!($row = mysql_fetch_array($result)))) { mysql_close($dblink); html_die("
You do not have an associated Jabber account or it is disabled!
Press 'Enable' in order to create a new one or to activate an old one.
If error persists, please inform the administrator."); } $juid = $row[0]; $jab_id = $row[1]; $jab_passwd = $row[2]; $jab_type = $row[3]; dbg_msg("Jabber User ID: $juid
"); $fd = jab_connect($jserver, $jport); if(!$fd) html_die("Could not connect to Jabber server"); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $jid1 = stristr($buf_recv, "id='"); $jid1 = substr($jid1, 4); if($jid1) { $jid2 = strstr($jid1, "'"); if($jid2) { $jid = substr($jid1, 0, strlen($jid1)-strlen($jid2)); dbg_msg("JID: $jid
"); } } $jcid = $jcid + 1; jab_get_auth($fd, $jcid, $jab_id); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $jcid = $jcid + 1; jab_set_auth($fd, $jcid, $jab_id, $jab_passwd); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } if(stristr($buf_recv, " id='$jcid'") && stristr($buf_recv, " type='error'")) { jab_disconnect($fd); html_die("
Wrong username or password at Jabber authentication"); } # ----- # browse agents # ----- $jcid = $jcid + 1; jab_get_agents($fd, $jcid, $jserver); $buf_agents = fread($fd, 4096); while(!$buf_agents) { usleep(100); $buf_agents = fread($fd, 4096); } # dbg_msg("\n"); # ----- $imag1 = stristr($buf_agents, ""); } } # ----- if(isset($imag)) { if($action == "Subscribe" && isset($imname) && $imname != "") { echo "

IM ($imtype) subscription


"; # ----- # unsubscribe the previous IM account (if exists) # ----- $jcid = $jcid + 1; jab_set_unreg($fd, $jcid, $icqag); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } sleep(1); # ----- # subscription # ----- $jcid = $jcid + 1; jab_get_reg($fd, $jcid, $imag); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } $imkey1 = stristr($buf_recv, ""); $imkey1 = substr($imkey1, 5); if($imkey1) { $imkey2 = strstr($imkey1, ""); if($imkey2) { $imkey = substr($imkey1, 0, strlen($imkey1)-strlen($imkey2)); dbg_msg("IM key: $imkey
"); } } if(!isset($imkey)) { jab_disconnect($fd); mysql_close($dblink); html_die("
Session key for IM ($imtype) Transport not found"); } $jcid = $jcid + 1; jab_set_regk($fd, $jcid, $imag, $imname, $impasswd, $imnick, $imkey); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } if(stristr($buf_recv, " id='$jcid'") && stristr($buf_recv, " type='error'")) { $err1 = stristr($buf_recv, ""); $err1 = substr($err1, 1); if($err1) { $err2 = strstr($err1, ""); if($err2) $err = substr($err1, 0, strlen($err1)-strlen($err2)); } jab_disconnect($fd); mysql_close($dblink); html_die("
Error registering your IM ($imtype) account: $err"); } jab_send_presence($fd, $imag."/registered", "subscribed"); # ----- # Update database $query = "SELECT ".$imtype."_id FROM ".$imtype." WHERE juid='$juid'"; $result = mysql_query($query, $dblink) or html_die("Invalid SQL query"); if(mysql_num_rows($result) == 0) { # INSERT $query = "INSERT INTO ".$imtype." (juid, ".$imtype."_id, ".$imtype."_passwd, ".$imtype."_nick) VALUES ('$juid', '$imname', '$impasswd', '$imnick')"; dbg_msg("$query
"); $result = mysql_query($query, $dblink); if(mysql_affected_rows() != 1) { echo "
Can not register '$sipname'/'$imname'
"; } else { echo "Your IM ($imtype) account was successfully registered
"; } } else { # UPDATE $query = "UPDATE ".$imtype." SET ".$imtype."_id='$imname', ".$imtype."_passwd='$impasswd', ".$imtype."_nick='$imnick' WHERE juid='$juid'"; dbg_msg("$query
"); $result = mysql_query($query, $dblink); if(!$result) { echo "
Can not update '$sipname'/'$imname'
"; } else { if(mysql_affected_rows() == 1) { echo "Your IM ($imtype) account was successfully updated
"; } else { echo "No modification in your IM ($imtype) account
"; } } } } else { echo "

IM ($imtype) unsubscription


"; # ----- # unsubscribe the IM account # ----- $jcid = $jcid + 1; jab_set_unreg($fd, $jcid, $icqag); $buf_recv = fread($fd, 2048); while(!$buf_recv) { usleep(100); $buf_recv = fread($fd, 2048); } sleep(1); $query = "DELETE FROM ".$imtype." WHERE juid='$juid'"; dbg_msg("$query
"); $result = mysql_query($query, $dblink); if(!$result) { echo "
Can not remove IM ($imtype) information from database
"; } else { echo "
Unsubscription from IM ($imtype) completed
"; } } } sleep(1); jab_disconnect($fd); mysql_close($dblink); } ?>
kamailio-4.0.4/obsolete/jabber_k/doc/web/libjab.php0000644000000000000000000000410712223032460020630 0ustar rootroot"; fputs ($fd,$stream); return $fd; } function jab_disconnect($fd) { $stream = ""; fputs ($fd,$stream); fclose($fd); } function jab_get_reg($fd, $id, $server) { $str = ""; fputs ($fd,$str); } function jab_set_reg($fd, $id, $server, $username, $password) { $str = "$username$password"; fputs($fd, $str); } function jab_set_regk($fd, $id, $server, $username, $password, $nick, $key) { $str = "$username$password$nick$key"; fputs($fd, $str); } function jab_set_unreg($fd, $id, $server) { $str = ""; fputs($fd, $str); } function jab_get_agents($fd, $id, $server) { $str = ""; fputs($fd, $str); } function jab_get_auth($fd, $id, $user) { $str = "$user"; fputs($fd, $str); } function jab_set_auth($fd, $id, $user, $passwd) { $str = "$userwebjb$passwd"; fputs($fd, $str); } function jab_send_presence($fd, $to, $presence) { $str = ""; fputs($fd, $str); } ?>kamailio-4.0.4/obsolete/jabber_k/doc/jabberx.cfg0000644000000000000000000001041212223032460020211 0ustar rootroot# # configuration for Jabber module testing # (sample config file using the module with presence support) # # $ID: daniel $ # debug=9 # debug level (cmd line: -dddddddddd) #fork=yes # (cmd. line: -D) fork=no log_stderror=yes # (cmd line: -E) #log_stderror=no # (cmd line: -E) children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) listen=10.0.0.179 port=5060 # for more info: sip_router -h # ------------------ module loading ---------------------------------- #modules loadmodule "modules/print/print.so" loadmodule "modules/textops/textops.so" loadmodule "modules/tm/tm.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/sl/sl.so" loadmodule "modules/mysql/mysql.so" loadmodule "modules/jabber/jabber.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/pa/pa.so" loadmodule "modules/mi_fifo/mi_fifo.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/openser_fifo") # -- registrar params -- modparam("registrar", "default_expires", 120) modparam("registrar", "use_domain", 1) # -- usrloc params -- modparam("usrloc", "use_domain", 1) modparam("usrloc", "db_mode", 0) # -- jabber params -- modparam("jabber","db_url","mysql://user:password@127.0.0.1/sip_jab") modparam("jabber","jaddress","jabber.server.com") modparam("jabber","jport",5222) modparam("jabber","workers",2) modparam("jabber","max_jobs",10) modparam("jabber","cache_time",200) modparam("jabber","delay_time",60) modparam("jabber","jdomain","jabber.server.com=*") modparam("jabber","aliases","4;aim.jabber.server.com;icq.jabber.server.com;msn.jabber.server.com=%;yahoo.jabber.server.com;") route{ #if ( !mf_process_maxfwd_header("10") ) #{ # sl_send_reply("483","To Many Hops"); # drop(); #}; if (uri=~"[@:]sip\.server\.com([;:].*)*") { # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { if (t_newtran()) { save("location"); log("REGISTER received -> reply okay\n"); }; if(search("egistration")) { log("XJAB: Going ONline in Jabber network!!!\n"); if(jab_go_online()) { sl_send_reply("200", "Accepted"); } else { sl_send_reply("404","Not found"); }; } else { log("XJAB: Going OFFline in Jabber network!!!\n"); if(jab_go_offline()) { sl_send_reply("200", "Accepted"); } else { sl_send_reply("404","Not found"); }; }; break; }; if (method=="SUBSCRIBE") { if (t_newtran()) { subscribe("registrar"); }; break; }; if(!lookup("location")) { sl_send_reply("404","Not found"); break; }; }; if ((search("To:.*@icq\.jabber\.server\.com")) || (search("To:.*@jabber\.server\.com")) || (search("To:.*@msn\.jabber\.server\.com")) || (search("To:.*@yahoo\.jabber\.server\.com"))) { if (! t_newtran()) { sl_reply_error(); break; }; if (method=="MESSAGE") { log("MESSAGE received -> manage it with XJAB\n"); if(search("\n:on")) { if (jab_go_online()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not found"); }; break; }; if(search("\n:off")) { if (jab_go_offline()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not found"); }; break; }; if(search("\n:join")) { if (jab_join_jconf()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not Found"); }; break; }; if(search("\n:exit")) { if (jab_exit_jconf()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not Found"); }; break; }; if (jab_send_message()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("503","Service Unavailable"); }; break; }; if (method=="SUBSCRIBE") { subscribe("jabber"); break; }; log("NON_Message request received for JABBER gateway->dropt!\n"); sl_send_reply("202","Accepted"); break; }; if (!t_relay()) { sl_reply_error(); }; #forward(uri:host,uri:port); } kamailio-4.0.4/obsolete/jabber_k/doc/jabber.cfg0000644000000000000000000000426112223032460020026 0ustar rootroot# # configuration for Jabber module testing # # $ID: daniel $ # # # sample of Jabber module usage # debug=9 # debug level (cmd line: -dddddddddd) #fork=yes # (cmd. line: -D) fork=no log_stderror=yes # (cmd line: -E) #log_stderror=no # (cmd line: -E) children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 listen=10.0.0.1 # for more info: sip_router -h #modules loadmodule "modules/print/print.so" loadmodule "modules/textops/textops.so" loadmodule "modules/tm/tm.so" loadmodule "modules/im/im.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/sl/sl.so" loadmodule "modules/mysql/mysql.so" loadmodule "modules/jabber/jabber.so" modparam("jabber","db_url","mysql://user:***@127.0.0.1/sip_jab") modparam("jabber","jaddress","127.0.0.1") modparam("jabber","jport",5222) modparam("jabber","workers",2) modparam("jabber","max_jobs",10) modparam("jabber","aliases","3;icq.domain.com;msn.domain.com;yahoo.domain.com;") modparam("jabber","jdomain","jabber.domain.com") route{ if ((search("To:.*@icq\.domain\.com")) || (search("To:.*@jabber\.domain\.de")) || (search("To:.*@msn\.domain\.com")) || (search("To:.*@yahoo\.domain\.com"))) { if (method=="MESSAGE") { log("MESSAGE received -> manage it with XJAB\n"); if(search("\n:online")) { if (jab_go_online()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not found"); }; break; }; if(search("\n:offline")) { if (jab_go_offline()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not found"); }; break; }; if(search("\n:join")) { if (jab_join_jconf()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not Found"); }; break; }; if(search("\n:exit")) { if (jab_exit_jconf()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("404","Not Found"); }; break; }; if (jab_send_message()) { sl_send_reply("200","Accepted"); }else{ sl_send_reply("503","Service Unavailable"); }; break; }; }; forward(uri:host,uri:port); } kamailio-4.0.4/obsolete/jabber_k/doc/jabber.xml0000644000000000000000000000203612223032460020065 0ustar rootroot %docentities; ]> jabber Module &kamailioname; Daniel-Constantin Mierla miconda@gmail.com Daniel-Constantin Mierla miconda@gmail.com 2003 FhG FOKUS $Revision$ $Date$ kamailio-4.0.4/obsolete/jabber_k/doc/jabber.sql0000644000000000000000000000256212223032460020070 0ustar rootroot# # DATABASE definition # DROP DATABASE IF EXISTS sip_jab; CREATE DATABASE sip_jab; USE sip_jab; # jabber users CREATE TABLE jusers( juid INT NOT NULL AUTO_INCREMENT, jab_id VARCHAR(128) NOT NULL, jab_passwd VARCHAR(50), sip_id VARCHAR(128) NOT NULL, type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), KEY(jab_id), KEY(sip_id) ); # icq users CREATE TABLE icq( juid INT NOT NULL, icq_id INT NOT NULL, icq_passwd VARCHAR(50), icq_nick VARCHAR(50), type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), # --- REFERENCES jusers(juid) ON UPDATE CASCADE ON DELETE CASCADE, KEY(icq_id) ); # msn users CREATE TABLE msn( juid INT NOT NULL, msn_id VARCHAR(128) NOT NULL, msn_passwd VARCHAR(50), msn_nick VARCHAR(50), type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), # --- REFERENCES jusers(juid) ON UPDATE CASCADE ON DELETE CASCADE, KEY(msn_id) ); # aim users CREATE TABLE aim( juid INT NOT NULL, aim_id VARCHAR(128) NOT NULL, aim_passwd VARCHAR(50), aim_nick VARCHAR(50), type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), # --- REFERENCES jusers(juid) ON UPDATE CASCADE ON DELETE CASCADE, KEY(aim_id) ); # yahoo users CREATE TABLE yahoo( juid INT NOT NULL, yahoo_id VARCHAR(128) NOT NULL, yahoo_passwd VARCHAR(50), yahoo_nick VARCHAR(50), type INT NOT NULL DEFAULT 0, PRIMARY KEY(juid), # --- REFERENCES jusers(juid) ON UPDATE CASCADE ON DELETE CASCADE, KEY(yahoo_id) ); kamailio-4.0.4/obsolete/jabber_k/doc/Makefile0000644000000000000000000000013112223032460017550 0ustar rootrootdocs = jabber.xml docbook_dir = ../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/jabber_k/doc/jabberreg.pl0000644000000000000000000000725012223032460020401 0ustar rootroot#!/usr/bin/perl -w # # Register a new user with Jabber gateway # # The parameters must be only the username part of the sip address # Change the $sip_domain according with your SIP server domain name # use Socket; use DBD::mysql; if(@ARGV == 0) { die ("Syntax: regjab.pl username1 username2 ..."); } $db_driver = "mysql"; $sip_domain = "voip.org"; ##### MySQL connection to JabberGW database $jab_db_nm = "sip_jab"; #JabberGW's database name $jab_db_hn = "127.0.0.1"; #JabberGW's database server address $jab_db_pt = "3306"; #JabberGW's database server port $jab_db_us = "user"; #JabberGW's database username $jab_db_ps = "*********"; #JabberGW's database password $jab_db_dsn = "DBI:$db_driver:database=$jab_db_nm;host=$jab_db_hn;port=$jab_db_pt"; ##### users table $jab_tb_nm = "jusers"; #JabberGW's subscriber table ##### username column $jab_cl_usj = "jab_id"; $jab_cl_psj = "jab_passwd"; $jab_cl_uss = "sip_id"; $jab_server = "localhost"; #Jabber server address $jab_srvid = "jabber.x.com"; #Jabber server id $jab_port = 5222; #Jabber server port $iaddr = inet_aton($jab_server) or die "no host: $jab_server"; $paddr = sockaddr_in($jab_port, $iaddr); $proto = getprotobyname('tcp'); ### Connect to MySQL database $jab_dbh = DBI->connect($jab_db_dsn, $jab_db_us, $jab_db_ps); if(!$jab_dbh ) { print ('ERROR: Jabber\'s MySQL server not responding!'); exit; } foreach $jab_usr (@ARGV) { print "Subscribe SIP user <$jab_usr> to Jabber gateway\n"; #checkif user has already a Jabber ID $sip_uri = "sip:$jab_usr\@$sip_domain"; $jab_pwd = "qw1$jab_usr#"; $jab_sth = $jab_dbh->prepare("SELECT $jab_cl_usj FROM $jab_tb_nm WHERE $jab_cl_uss='$sip_uri'"); if (!$jab_sth) { die($jab_dbh->errstr); } if (!$jab_sth->execute) { die($jab_sth->errstr); } if(!($jab_sth->fetchrow_hashref())) { # create Jabber account print "Register SIP user <$jab_usr> to Jabber server\n"; socket(SOCK, PF_INET,SOCK_STREAM, $proto) or die "socket: $!"; connect(SOCK, $paddr) or die "connect: $!"; $jcid = 0; $err_no = 0; $line = ""; send(SOCK, $line, 0); recv(SOCK, $line, 256, 0); $jid_p = index($line, "id="); if($jid_p > 0) { $jid_p = $jid_p+4; $jid_p1 = index($line, "'", $jid_p); if($jid_p1 > 0) { $jid = substr($line, $jid_p, $jid_p1-$jid_p); print("JID: $jid\n"); } } $jcid =$jcid + 1; $line = ""; send(SOCK, $line, 0); recv(SOCK, $line, 512, 0); # here we should check what Jabber really wants # - I know for our srv, so skip it # $jcid =$jcid + 1; $line = "$jab_usr$jab_pwd"; send(SOCK, $line, 0); recv(SOCK, $line, 512, 0); if(index($line, " id='$jcid'")>0 && index($line, " type='error'")>0) { #error creating Jabber user print("Error: creating Jabber user <$jab_usr>\n$line\n"); $err_no = 1; } $line = ""; send(SOCK, $line, 0); close(SOCK) or die "close: $!"; # add to Jabber database if($err_no == 0) { $rows = $jab_dbh->do("INSERT INTO $jab_tb_nm ($jab_cl_usj, $jab_cl_psj, $jab_cl_uss) VALUES ('$jab_usr', '$jab_pwd', '$sip_uri')"); if($rows == 1) { print("SIP user <$jab_usr> added to Jabber database\n"); } else { print("Error: SIP user <$jab_usr> not added to Jabber database\n"); } } } else { print("SIP user <$jab_usr> is already in Jabber database\n"); } } $jab_sth->finish(); #### Disconnect from the database. $jab_dbh->disconnect(); kamailio-4.0.4/obsolete/jabber_k/doc/jabber.gws0000644000000000000000000001070712223032460020071 0ustar rootrootIM Gateway - capabilities and limitations - v1.0 ================================================ The gateway allows the message passing between SIP users and other IM users (ICQ, AIM, Yahoo ...). IM Gateway uses for this a Jabber server which has the facility of supporting many IM protocols (they are called IM transports). So, in fact, the gateway is between SIP and Jabber. Because the most of the IM protocols are not open source and they are the property of some companies, Jabber does not provide full support for these protocols. From time to time, the owner of the protocol changes the protocol structure, and for a while, that transport is no longer available. There are some guys doing some kind of reverse engineering on these protocols. But not whole protocol structure is discovered, each transport has its own limitations. In additions, many of the IM native clients allow only one user to be online at a moment, and IM servers could easy say that the hosts which have more than one open connection are not using a native client and block those addresses or the accounts. Next, are presented the IM transports that are running on our Jabber server, or they are wanted to be available. The capabilities and limitations are what can be found on the net about transports. AIM transport ------------- It is only one well known version of aim-transport. (web location: http://aim-transport.jabberstudio.org/) AIM-t provides basic interoperability between Jabber and AIM/ICQ network. Currently AIM-t is undergoing massive changes. It will be a standalone component once this is finished. * capabilities - currently supporting basic presence and messaging. - reliable messaging (via server only) - simple user info retrieval (ICQ only) may take some time, just wait... - SMS sending (ICQ) send "SEND-SMS:number:text" to any ICQ user, for example "SEND-SMS:+4917012345:Test" - away message retrieval (AIM only) - offline messages (ICQ only) * limitations - NO file transfer - NO multichat - NO user search - NO incoming SMS - NO import of server buddy lists - NO handling of incoming ICQ auth requests - ICQ messages may get dropped by AOL servers if your contact uses an old ICQv5 client. ICQ transport ------------- Excepting the AIM transport, there exists another distribution for ICQ transport. (web location: http://icqv7-t.sourceforge.net/) * capabilities - messaging (through server only) - presence notification - SMS support * limitations - no file transfer The AIM transport for ICQ. - see AIM transport A new versions of ICQ transport seems to be available now. But now place from where it can be downloaded - I sent a mail asking for a distribution. A short description can be found at http://mailman.jabber.org/pipermail/jdev/2002-July/012343.html IRC transport ------------- (web location: http://download.jabber.org/contrib/irc-transport.tar.gz) This version of IRC transport is a beta version * capabilities - It will connect to irc servers and allow you to talk to them, but it is still in heavy development * limitations - private messaging does not yet function - transport registration/notify will not be supported - groupchat 1.0 and 1.1 will not be supported in favor of the 2.0 iq:conferenec/iq:browse protocol you will be able to log into and create password protected groups now (although this may not work yet) - there still may be some other bugs but they aren't caught yet. - in testing, it has been fairly stable, although some functionality may not be there yet. - requires jabberd version 1.4 several functions used, including MIO, MTQ, and xhash functions were not available in previous jabber server versions MSN transport ------------- (web location: http://msn-transport.jabberstudio.org/) * capabilities - provides basic interoperability between Jabber and MSN (messaging/presence) * limitations - there are memory leaks in MSN-t so is better to be run in a seperate process and restart it from time to time - MSN-t uses TCP port 1863 for incoming connections so you have to open this port if you use a firewall. - the other parts excepting messaging/presence Yahoo transport --------------- (web location: http://yahoo-transport.jabberstudio.org/) As of 2002-07-10 this transport has ceased functioning (probably due to YIM protocol changes). The transport is being worked on - see JDEV mailing list : http://mailman.jabber.org/pipermail/jdev/2002-July/012327.html * capabilities - log into Yahoo Messenger server, and send/recv messages. * limitations kamailio-4.0.4/obsolete/jabber_k/tree234.c0000644000000000000000000012050212223032460016704 0ustar rootroot/* * $Id$ * * tree234.c: reasonably generic counted 2-3-4 tree routines. * * This file is copyright 1999-2001 Simon Tatham. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include #include #include #include "tree234.h" #include "../../mem/shm_mem.h" //#define smalloc malloc //#define sfree free #define smalloc shm_malloc #define sfree shm_free #define mknew(typ) ( (typ *) smalloc (sizeof (typ)) ) #ifdef TEST #define LOG123(x) (printf x) #else #define LOG123(x) #endif typedef struct node234_Tag node234; struct tree234_Tag { node234 *root; cmpfn234 cmp; }; struct node234_Tag { node234 *parent; node234 *kids[4]; int counts[4]; void *elems[3]; }; /* * Create a 2-3-4 tree. */ tree234 *newtree234(cmpfn234 cmp) { tree234 *ret = mknew(tree234); LOG123(("created tree %p\n", ret)); ret->root = NULL; ret->cmp = cmp; return ret; } /* * Free a 2-3-4 tree (not including freeing the elements). */ static void freenode234(node234 *n) { if (!n) return; freenode234(n->kids[0]); freenode234(n->kids[1]); freenode234(n->kids[2]); freenode234(n->kids[3]); sfree(n); } void freetree234(tree234 *t) { if(t == NULL) return; freenode234(t->root); sfree(t); } /* * Free a 2-3-4 tree (including freeing the elements with 'fn' function). */ static void free2node234(node234 *n, freefn fn ) { if (!n) return; free2node234(n->kids[0], fn); free2node234(n->kids[1], fn); free2node234(n->kids[2], fn); free2node234(n->kids[3], fn); fn(n->elems[0]); fn(n->elems[1]); fn(n->elems[2]); sfree(n); } void free2tree234(tree234 *t, freefn fn) { if(t == NULL) return; free2node234(t->root, fn); sfree(t); } /* * Internal function to count a node. */ static int countnode234(node234 *n) { int count = 0; int i; if (!n) return 0; for (i = 0; i < 4; i++) count += n->counts[i]; for (i = 0; i < 3; i++) if (n->elems[i]) count++; return count; } /* * Count the elements in a tree. */ int count234(tree234 *t) { if (t->root) return countnode234(t->root); else return 0; } /* * Add an element e to a 2-3-4 tree t. Returns e on success, or if * an existing element compares equal, returns that. */ static void *add234_internal(tree234 *t, void *e, int index) { node234 *n, **np, *left, *right; void *orig_e = e; int c, lcount, rcount; LOG123(("adding node %p to tree %p\n", e, t)); if (t->root == NULL) { t->root = mknew(node234); t->root->elems[1] = t->root->elems[2] = NULL; t->root->kids[0] = t->root->kids[1] = NULL; t->root->kids[2] = t->root->kids[3] = NULL; t->root->counts[0] = t->root->counts[1] = 0; t->root->counts[2] = t->root->counts[3] = 0; t->root->parent = NULL; t->root->elems[0] = e; LOG123((" created root %p\n", t->root)); return orig_e; } np = &t->root; n = *np; while (*np) { int childnum; n = *np; LOG123((" node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); if (index >= 0) { if (!n->kids[0]) { /* * Leaf node. We want to insert at kid position * equal to the index: * * 0 A 1 B 2 C 3 */ childnum = index; } else { /* * Internal node. We always descend through it (add * always starts at the bottom, never in the * middle). */ do { /* this is a do ... while (0) to allow `break' */ if (index <= n->counts[0]) { childnum = 0; break; } index -= n->counts[0] + 1; if (index <= n->counts[1]) { childnum = 1; break; } index -= n->counts[1] + 1; if (index <= n->counts[2]) { childnum = 2; break; } index -= n->counts[2] + 1; if (index <= n->counts[3]) { childnum = 3; break; } return NULL; /* error: index out of range */ } while (0); } } else { if ((c = t->cmp(e, n->elems[0])) < 0) childnum = 0; else if (c == 0) return n->elems[0]; /* already exists */ else if (n->elems[1] == NULL || (c = t->cmp(e, n->elems[1])) < 0) childnum = 1; else if (c == 0) return n->elems[1]; /* already exists */ else if (n->elems[2] == NULL || (c = t->cmp(e, n->elems[2])) < 0) childnum = 2; else if (c == 0) return n->elems[2]; /* already exists */ else childnum = 3; } np = &n->kids[childnum]; LOG123((" moving to child %d (%p)\n", childnum, *np)); } /* * We need to insert the new element in n at position np. */ left = NULL; lcount = 0; right = NULL; rcount = 0; while (n) { LOG123((" at %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG123((" need to insert %p/%d [%p] %p/%d at position %d\n", left, lcount, e, right, rcount, np - n->kids)); if (n->elems[1] == NULL) { /* * Insert in a 2-node; simple. */ if (np == &n->kids[0]) { LOG123((" inserting on left of 2-node\n")); n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1]; n->elems[1] = n->elems[0]; n->kids[1] = right; n->counts[1] = rcount; n->elems[0] = e; n->kids[0] = left; n->counts[0] = lcount; } else { /* np == &n->kids[1] */ LOG123((" inserting on right of 2-node\n")); n->kids[2] = right; n->counts[2] = rcount; n->elems[1] = e; n->kids[1] = left; n->counts[1] = lcount; } if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; if (n->kids[2]) n->kids[2]->parent = n; LOG123((" done\n")); break; } else if (n->elems[2] == NULL) { /* * Insert in a 3-node; simple. */ if (np == &n->kids[0]) { LOG123((" inserting on left of 3-node\n")); n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2]; n->elems[2] = n->elems[1]; n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1]; n->elems[1] = n->elems[0]; n->kids[1] = right; n->counts[1] = rcount; n->elems[0] = e; n->kids[0] = left; n->counts[0] = lcount; } else if (np == &n->kids[1]) { LOG123((" inserting in middle of 3-node\n")); n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2]; n->elems[2] = n->elems[1]; n->kids[2] = right; n->counts[2] = rcount; n->elems[1] = e; n->kids[1] = left; n->counts[1] = lcount; } else { /* np == &n->kids[2] */ LOG123((" inserting on right of 3-node\n")); n->kids[3] = right; n->counts[3] = rcount; n->elems[2] = e; n->kids[2] = left; n->counts[2] = lcount; } if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; if (n->kids[2]) n->kids[2]->parent = n; if (n->kids[3]) n->kids[3]->parent = n; LOG123((" done\n")); break; } else { node234 *m = mknew(node234); m->parent = n->parent; LOG123((" splitting a 4-node; created new node %p\n", m)); /* * Insert in a 4-node; split into a 2-node and a * 3-node, and move focus up a level. * * I don't think it matters which way round we put the * 2 and the 3. For simplicity, we'll put the 3 first * always. */ if (np == &n->kids[0]) { m->kids[0] = left; m->counts[0] = lcount; m->elems[0] = e; m->kids[1] = right; m->counts[1] = rcount; m->elems[1] = n->elems[0]; m->kids[2] = n->kids[1]; m->counts[2] = n->counts[1]; e = n->elems[1]; n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2]; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else if (np == &n->kids[1]) { m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = left; m->counts[1] = lcount; m->elems[1] = e; m->kids[2] = right; m->counts[2] = rcount; e = n->elems[1]; n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2]; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else if (np == &n->kids[2]) { m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1]; m->elems[1] = n->elems[1]; m->kids[2] = left; m->counts[2] = lcount; /* e = e; */ n->kids[0] = right; n->counts[0] = rcount; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else { /* np == &n->kids[3] */ m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1]; m->elems[1] = n->elems[1]; m->kids[2] = n->kids[2]; m->counts[2] = n->counts[2]; n->kids[0] = left; n->counts[0] = lcount; n->elems[0] = e; n->kids[1] = right; n->counts[1] = rcount; e = n->elems[2]; } m->kids[3] = n->kids[3] = n->kids[2] = NULL; m->counts[3] = n->counts[3] = n->counts[2] = 0; m->elems[2] = n->elems[2] = n->elems[1] = NULL; if (m->kids[0]) m->kids[0]->parent = m; if (m->kids[1]) m->kids[1]->parent = m; if (m->kids[2]) m->kids[2]->parent = m; if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; LOG123((" left (%p): %p/%d [%p] %p/%d [%p] %p/%d\n", m, m->kids[0], m->counts[0], m->elems[0], m->kids[1], m->counts[1], m->elems[1], m->kids[2], m->counts[2])); LOG123((" right (%p): %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1])); left = m; lcount = countnode234(left); right = n; rcount = countnode234(right); } if (n->parent) np = (n->parent->kids[0] == n ? &n->parent->kids[0] : n->parent->kids[1] == n ? &n->parent->kids[1] : n->parent->kids[2] == n ? &n->parent->kids[2] : &n->parent->kids[3]); n = n->parent; } /* * If we've come out of here by `break', n will still be * non-NULL and all we need to do is go back up the tree * updating counts. If we've come here because n is NULL, we * need to create a new root for the tree because the old one * has just split into two. */ if (n) { while (n->parent) { int count = countnode234(n); int childnum; childnum = (n->parent->kids[0] == n ? 0 : n->parent->kids[1] == n ? 1 : n->parent->kids[2] == n ? 2 : 3); n->parent->counts[childnum] = count; n = n->parent; } } else { LOG123((" root is overloaded, split into two\n")); t->root = mknew(node234); t->root->kids[0] = left; t->root->counts[0] = lcount; t->root->elems[0] = e; t->root->kids[1] = right; t->root->counts[1] = rcount; t->root->elems[1] = NULL; t->root->kids[2] = NULL; t->root->counts[2] = 0; t->root->elems[2] = NULL; t->root->kids[3] = NULL; t->root->counts[3] = 0; t->root->parent = NULL; if (t->root->kids[0]) t->root->kids[0]->parent = t->root; if (t->root->kids[1]) t->root->kids[1]->parent = t->root; LOG123((" new root is %p/%d [%p] %p/%d\n", t->root->kids[0], t->root->counts[0], t->root->elems[0], t->root->kids[1], t->root->counts[1])); } return orig_e; } void *add234(tree234 *t, void *e) { if (!t->cmp) /* tree is unsorted */ return NULL; return add234_internal(t, e, -1); } void *addpos234(tree234 *t, void *e, int index) { if (index < 0 || /* index out of range */ t->cmp) /* tree is sorted */ return NULL; /* return failure */ return add234_internal(t, e, index); /* this checks the upper bound */ } /* * Look up the element at a given numeric index in a 2-3-4 tree. * Returns NULL if the index is out of range. */ void *index234(tree234 *t, int index) { node234 *n; if (!t->root) return NULL; /* tree is empty */ if (index < 0 || index >= countnode234(t->root)) return NULL; /* out of range */ n = t->root; while (n) { if (index < n->counts[0]) n = n->kids[0]; else if (index -= n->counts[0] + 1, index < 0) return n->elems[0]; else if (index < n->counts[1]) n = n->kids[1]; else if (index -= n->counts[1] + 1, index < 0) return n->elems[1]; else if (index < n->counts[2]) n = n->kids[2]; else if (index -= n->counts[2] + 1, index < 0) return n->elems[2]; else n = n->kids[3]; } /* We shouldn't ever get here. I wonder how we did. */ return NULL; } /* * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not * found. e is always passed as the first argument to cmp, so cmp * can be an asymmetric function if desired. cmp can also be passed * as NULL, in which case the compare function from the tree proper * will be used. */ void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation, int *index) { node234 *n; void *ret; int c; int idx, ecount, kcount, cmpret; if (t->root == NULL) return NULL; if (cmp == NULL) cmp = t->cmp; n = t->root; /* * Attempt to find the element itself. */ idx = 0; ecount = -1; /* * Prepare a fake `cmp' result if e is NULL. */ cmpret = 0; if (e == NULL) { assert(relation == REL234_LT || relation == REL234_GT); if (relation == REL234_LT) cmpret = +1; /* e is a max: always greater */ else if (relation == REL234_GT) cmpret = -1; /* e is a min: always smaller */ } while (1) { for (kcount = 0; kcount < 4; kcount++) { if (kcount >= 3 || n->elems[kcount] == NULL || (c = cmpret ? cmpret : cmp(e, n->elems[kcount])) < 0) { break; } if (n->kids[kcount]) idx += n->counts[kcount]; if (c == 0) { ecount = kcount; break; } idx++; } if (ecount >= 0) break; if (n->kids[kcount]) n = n->kids[kcount]; else break; } if (ecount >= 0) { /* * We have found the element we're looking for. It's * n->elems[ecount], at tree index idx. If our search * relation is EQ, LE or GE we can now go home. */ if (relation != REL234_LT && relation != REL234_GT) { if (index) *index = idx; return n->elems[ecount]; } /* * Otherwise, we'll do an indexed lookup for the previous * or next element. (It would be perfectly possible to * implement these search types in a non-counted tree by * going back up from where we are, but far more fiddly.) */ if (relation == REL234_LT) idx--; else idx++; } else { /* * We've found our way to the bottom of the tree and we * know where we would insert this node if we wanted to: * we'd put it in in place of the (empty) subtree * n->kids[kcount], and it would have index idx * * But the actual element isn't there. So if our search * relation is EQ, we're doomed. */ if (relation == REL234_EQ) return NULL; /* * Otherwise, we must do an index lookup for index idx-1 * (if we're going left - LE or LT) or index idx (if we're * going right - GE or GT). */ if (relation == REL234_LT || relation == REL234_LE) { idx--; } } /* * We know the index of the element we want; just call index234 * to do the rest. This will return NULL if the index is out of * bounds, which is exactly what we want. */ ret = index234(t, idx); if (ret && index) *index = idx; return ret; } void *find234(tree234 *t, void *e, cmpfn234 cmp) { return findrelpos234(t, e, cmp, REL234_EQ, NULL); } void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation) { return findrelpos234(t, e, cmp, relation, NULL); } void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index) { return findrelpos234(t, e, cmp, REL234_EQ, index); } /* * Delete an element e in a 2-3-4 tree. Does not free the element, * merely removes all links to it from the tree nodes. */ static void *delpos234_internal(tree234 *t, int index) { node234 *n; void *retval; int ei = -1; retval = 0; n = t->root; LOG123(("deleting item %d from tree %p\n", index, t)); while (1) { while (n) { int ki; node234 *sub; LOG123((" node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d index=%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3], index)); if (index < n->counts[0]) { ki = 0; } else if (index -= n->counts[0]+1, index < 0) { ei = 0; break; } else if (index < n->counts[1]) { ki = 1; } else if (index -= n->counts[1]+1, index < 0) { ei = 1; break; } else if (index < n->counts[2]) { ki = 2; } else if (index -= n->counts[2]+1, index < 0) { ei = 2; break; } else { ki = 3; } /* * Recurse down to subtree ki. If it has only one element, * we have to do some transformation to start with. */ LOG123((" moving to subtree %d\n", ki)); sub = n->kids[ki]; if (!sub->elems[1]) { LOG123((" subtree has only one element!\n", ki)); if (ki > 0 && n->kids[ki-1]->elems[1]) { /* * Case 3a, left-handed variant. Child ki has * only one element, but child ki-1 has two or * more. So we need to move a subtree from ki-1 * to ki. * * . C . . B . * / \ -> / \ * [more] a A b B c d D e [more] a A b c C d D e */ node234 *sib = n->kids[ki-1]; int lastelem = (sib->elems[2] ? 2 : sib->elems[1] ? 1 : 0); sub->kids[2] = sub->kids[1]; sub->counts[2] = sub->counts[1]; sub->elems[1] = sub->elems[0]; sub->kids[1] = sub->kids[0]; sub->counts[1] = sub->counts[0]; sub->elems[0] = n->elems[ki-1]; sub->kids[0] = sib->kids[lastelem+1]; sub->counts[0] = sib->counts[lastelem+1]; if (sub->kids[0]) sub->kids[0]->parent = sub; n->elems[ki-1] = sib->elems[lastelem]; sib->kids[lastelem+1] = NULL; sib->counts[lastelem+1] = 0; sib->elems[lastelem] = NULL; n->counts[ki] = countnode234(sub); LOG123((" case 3a left\n")); LOG123((" index and left subtree count before adjustment: %d, %d\n", index, n->counts[ki-1])); index += n->counts[ki-1]; n->counts[ki-1] = countnode234(sib); index -= n->counts[ki-1]; LOG123((" index and left subtree count after adjustment: %d, %d\n", index, n->counts[ki-1])); } else if (ki < 3 && n->kids[ki+1] && n->kids[ki+1]->elems[1]) { /* * Case 3a, right-handed variant. ki has only * one element but ki+1 has two or more. Move a * subtree from ki+1 to ki. * * . B . . C . * / \ -> / \ * a A b c C d D e [more] a A b B c d D e [more] */ node234 *sib = n->kids[ki+1]; int j; sub->elems[1] = n->elems[ki]; sub->kids[2] = sib->kids[0]; sub->counts[2] = sib->counts[0]; if (sub->kids[2]) sub->kids[2]->parent = sub; n->elems[ki] = sib->elems[0]; sib->kids[0] = sib->kids[1]; sib->counts[0] = sib->counts[1]; for (j = 0; j < 2 && sib->elems[j+1]; j++) { sib->kids[j+1] = sib->kids[j+2]; sib->counts[j+1] = sib->counts[j+2]; sib->elems[j] = sib->elems[j+1]; } sib->kids[j+1] = NULL; sib->counts[j+1] = 0; sib->elems[j] = NULL; n->counts[ki] = countnode234(sub); n->counts[ki+1] = countnode234(sib); LOG123((" case 3a right\n")); } else { /* * Case 3b. ki has only one element, and has no * neighbor with more than one. So pick a * neighbor and merge it with ki, taking an * element down from n to go in the middle. * * . B . . * / \ -> | * a A b c C d a A b B c C d * * (Since at all points we have avoided * descending to a node with only one element, * we can be sure that n is not reduced to * nothingness by this move, _unless_ it was * the very first node, ie the root of the * tree. In that case we remove the now-empty * root and replace it with its single large * child as shown.) */ node234 *sib; int j; if (ki > 0) { ki--; index += n->counts[ki] + 1; } sib = n->kids[ki]; sub = n->kids[ki+1]; sub->kids[3] = sub->kids[1]; sub->counts[3] = sub->counts[1]; sub->elems[2] = sub->elems[0]; sub->kids[2] = sub->kids[0]; sub->counts[2] = sub->counts[0]; sub->elems[1] = n->elems[ki]; sub->kids[1] = sib->kids[1]; sub->counts[1] = sib->counts[1]; if (sub->kids[1]) sub->kids[1]->parent = sub; sub->elems[0] = sib->elems[0]; sub->kids[0] = sib->kids[0]; sub->counts[0] = sib->counts[0]; if (sub->kids[0]) sub->kids[0]->parent = sub; n->counts[ki+1] = countnode234(sub); sfree(sib); /* * That's built the big node in sub. Now we * need to remove the reference to sib in n. */ for (j = ki; j < 3 && n->kids[j+1]; j++) { n->kids[j] = n->kids[j+1]; n->counts[j] = n->counts[j+1]; n->elems[j] = j<2 ? n->elems[j+1] : NULL; } n->kids[j] = NULL; n->counts[j] = 0; if (j < 3) n->elems[j] = NULL; LOG123((" case 3b ki=%d\n", ki)); if (!n->elems[0]) { /* * The root is empty and needs to be * removed. */ LOG123((" shifting root!\n")); t->root = sub; sub->parent = NULL; sfree(n); } } } n = sub; } if (!retval) retval = n->elems[ei]; if (ei==-1) return NULL; /* although this shouldn't happen */ /* * Treat special case: this is the one remaining item in * the tree. n is the tree root (no parent), has one * element (no elems[1]), and has no kids (no kids[0]). */ if (!n->parent && !n->elems[1] && !n->kids[0]) { LOG123((" removed last element in tree\n")); sfree(n); t->root = NULL; return retval; } /* * Now we have the element we want, as n->elems[ei], and we * have also arranged for that element not to be the only * one in its node. So... */ if (!n->kids[0] && n->elems[1]) { /* * Case 1. n is a leaf node with more than one element, * so it's _really easy_. Just delete the thing and * we're done. */ int i; LOG123((" case 1\n")); for (i = ei; i < 2 && n->elems[i+1]; i++) n->elems[i] = n->elems[i+1]; n->elems[i] = NULL; /* * Having done that to the leaf node, we now go back up * the tree fixing the counts. */ while (n->parent) { int childnum; childnum = (n->parent->kids[0] == n ? 0 : n->parent->kids[1] == n ? 1 : n->parent->kids[2] == n ? 2 : 3); n->parent->counts[childnum]--; n = n->parent; } return retval; /* finished! */ } else if (n->kids[ei]->elems[1]) { /* * Case 2a. n is an internal node, and the root of the * subtree to the left of e has more than one element. * So find the predecessor p to e (ie the largest node * in that subtree), place it where e currently is, and * then start the deletion process over again on the * subtree with p as target. */ node234 *m = n->kids[ei]; void *target; LOG123((" case 2a\n")); while (m->kids[0]) { m = (m->kids[3] ? m->kids[3] : m->kids[2] ? m->kids[2] : m->kids[1] ? m->kids[1] : m->kids[0]); } target = (m->elems[2] ? m->elems[2] : m->elems[1] ? m->elems[1] : m->elems[0]); n->elems[ei] = target; index = n->counts[ei]-1; n = n->kids[ei]; } else if (n->kids[ei+1]->elems[1]) { /* * Case 2b, symmetric to 2a but s/left/right/ and * s/predecessor/successor/. (And s/largest/smallest/). */ node234 *m = n->kids[ei+1]; void *target; LOG123((" case 2b\n")); while (m->kids[0]) { m = m->kids[0]; } target = m->elems[0]; n->elems[ei] = target; n = n->kids[ei+1]; index = 0; } else { /* * Case 2c. n is an internal node, and the subtrees to * the left and right of e both have only one element. * So combine the two subnodes into a single big node * with their own elements on the left and right and e * in the middle, then restart the deletion process on * that subtree, with e still as target. */ node234 *a = n->kids[ei], *b = n->kids[ei+1]; int j; LOG123((" case 2c\n")); a->elems[1] = n->elems[ei]; a->kids[2] = b->kids[0]; a->counts[2] = b->counts[0]; if (a->kids[2]) a->kids[2]->parent = a; a->elems[2] = b->elems[0]; a->kids[3] = b->kids[1]; a->counts[3] = b->counts[1]; if (a->kids[3]) a->kids[3]->parent = a; sfree(b); n->counts[ei] = countnode234(a); /* * That's built the big node in a, and destroyed b. Now * remove the reference to b (and e) in n. */ for (j = ei; j < 2 && n->elems[j+1]; j++) { n->elems[j] = n->elems[j+1]; n->kids[j+1] = n->kids[j+2]; n->counts[j+1] = n->counts[j+2]; } n->elems[j] = NULL; n->kids[j+1] = NULL; n->counts[j+1] = 0; /* * It's possible, in this case, that we've just removed * the only element in the root of the tree. If so, * shift the root. */ if (n->elems[0] == NULL) { LOG123((" shifting root!\n")); t->root = a; a->parent = NULL; sfree(n); } /* * Now go round the deletion process again, with n * pointing at the new big node and e still the same. */ n = a; index = a->counts[0] + a->counts[1] + 1; } } } void *delpos234(tree234 *t, int index) { if (index < 0 || index >= countnode234(t->root)) return NULL; return delpos234_internal(t, index); } void *del234(tree234 *t, void *e) { int index; if (!findrelpos234(t, e, NULL, REL234_EQ, &index)) return NULL; /* it wasn't in there anyway */ return delpos234_internal(t, index); /* it's there; delete it. */ } #ifdef TEST /* * Test code for the 2-3-4 tree. This code maintains an alternative * representation of the data in the tree, in an array (using the * obvious and slow insert and delete functions). After each tree * operation, the verify() function is called, which ensures all * the tree properties are preserved: * - node->child->parent always equals node * - tree->root->parent always equals NULL * - number of kids == 0 or number of elements + 1; * - tree has the same depth everywhere * - every node has at least one element * - subtree element counts are accurate * - any NULL kid pointer is accompanied by a zero count * - in a sorted tree: ordering property between elements of a * node and elements of its children is preserved * and also ensures the list represented by the tree is the same * list it should be. (This last check also doubly verifies the * ordering properties, because the `same list it should be' is by * definition correctly ordered. It also ensures all nodes are * distinct, because the enum functions would get caught in a loop * if not.) */ #include #define srealloc realloc /* * Error reporting function. */ void error(char *fmt, ...) { va_list ap; printf("ERROR: "); va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); printf("\n"); } /* The array representation of the data. */ void **array; int arraylen, arraysize; cmpfn234 cmp; /* The tree representation of the same data. */ tree234 *tree; typedef struct { int treedepth; int elemcount; } chkctx; int chknode(chkctx *ctx, int level, node234 *node, void *lowbound, void *highbound) { int nkids, nelems; int i; int count; /* Count the non-NULL kids. */ for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++); /* Ensure no kids beyond the first NULL are non-NULL. */ for (i = nkids; i < 4; i++) if (node->kids[i]) { error("node %p: nkids=%d but kids[%d] non-NULL", node, nkids, i); } else if (node->counts[i]) { error("node %p: kids[%d] NULL but count[%d]=%d nonzero", node, i, i, node->counts[i]); } /* Count the non-NULL elements. */ for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++); /* Ensure no elements beyond the first NULL are non-NULL. */ for (i = nelems; i < 3; i++) if (node->elems[i]) { error("node %p: nelems=%d but elems[%d] non-NULL", node, nelems, i); } if (nkids == 0) { /* * If nkids==0, this is a leaf node; verify that the tree * depth is the same everywhere. */ if (ctx->treedepth < 0) ctx->treedepth = level; /* we didn't know the depth yet */ else if (ctx->treedepth != level) error("node %p: leaf at depth %d, previously seen depth %d", node, level, ctx->treedepth); } else { /* * If nkids != 0, then it should be nelems+1, unless nelems * is 0 in which case nkids should also be 0 (and so we * shouldn't be in this condition at all). */ int shouldkids = (nelems ? nelems+1 : 0); if (nkids != shouldkids) { error("node %p: %d elems should mean %d kids but has %d", node, nelems, shouldkids, nkids); } } /* * nelems should be at least 1. */ if (nelems == 0) { error("node %p: no elems", node, nkids); } /* * Add nelems to the running element count of the whole tree. */ ctx->elemcount += nelems; /* * Check ordering property: all elements should be strictly > * lowbound, strictly < highbound, and strictly < each other in * sequence. (lowbound and highbound are NULL at edges of tree * - both NULL at root node - and NULL is considered to be < * everything and > everything. IYSWIM.) */ if (cmp) { for (i = -1; i < nelems; i++) { void *lower = (i == -1 ? lowbound : node->elems[i]); void *higher = (i+1 == nelems ? highbound : node->elems[i+1]); if (lower && higher && cmp(lower, higher) >= 0) { error("node %p: kid comparison [%d=%s,%d=%s] failed", node, i, lower, i+1, higher); } } } /* * Check parent pointers: all non-NULL kids should have a * parent pointer coming back to this node. */ for (i = 0; i < nkids; i++) if (node->kids[i]->parent != node) { error("node %p kid %d: parent ptr is %p not %p", node, i, node->kids[i]->parent, node); } /* * Now (finally!) recurse into subtrees. */ count = nelems; for (i = 0; i < nkids; i++) { void *lower = (i == 0 ? lowbound : node->elems[i-1]); void *higher = (i >= nelems ? highbound : node->elems[i]); int subcount = chknode(ctx, level+1, node->kids[i], lower, higher); if (node->counts[i] != subcount) { error("node %p kid %d: count says %d, subtree really has %d", node, i, node->counts[i], subcount); } count += subcount; } return count; } void verify(void) { chkctx ctx; int i; void *p; ctx.treedepth = -1; /* depth unknown yet */ ctx.elemcount = 0; /* no elements seen yet */ /* * Verify validity of tree properties. */ if (tree->root) { if (tree->root->parent != NULL) error("root->parent is %p should be null", tree->root->parent); chknode(&ctx, 0, tree->root, NULL, NULL); } printf("tree depth: %d\n", ctx.treedepth); /* * Enumerate the tree and ensure it matches up to the array. */ for (i = 0; NULL != (p = index234(tree, i)); i++) { if (i >= arraylen) error("tree contains more than %d elements", arraylen); if (array[i] != p) error("enum at position %d: array says %s, tree says %s", i, array[i], p); } if (ctx.elemcount != i) { error("tree really contains %d elements, enum gave %d", ctx.elemcount, i); } if (i < arraylen) { error("enum gave only %d elements, array has %d", i, arraylen); } i = count234(tree); if (ctx.elemcount != i) { error("tree really contains %d elements, count234 gave %d", ctx.elemcount, i); } } void internal_addtest(void *elem, int index, void *realret) { int i, j; void *retval; if (arraysize < arraylen+1) { arraysize = arraylen+1+256; array = (array == NULL ? smalloc(arraysize*sizeof(*array)) : srealloc(array, arraysize*sizeof(*array))); } i = index; /* now i points to the first element >= elem */ retval = elem; /* expect elem returned (success) */ for (j = arraylen; j > i; j--) array[j] = array[j-1]; array[i] = elem; /* add elem to array */ arraylen++; if (realret != retval) { error("add: retval was %p expected %p", realret, retval); } verify(); } void addtest(void *elem) { int i; void *realret; realret = add234(tree, elem); i = 0; while (i < arraylen && cmp(elem, array[i]) > 0) i++; if (i < arraylen && !cmp(elem, array[i])) { void *retval = array[i]; /* expect that returned not elem */ if (realret != retval) { error("add: retval was %p expected %p", realret, retval); } } else internal_addtest(elem, i, realret); } void addpostest(void *elem, int i) { void *realret; realret = addpos234(tree, elem, i); internal_addtest(elem, i, realret); } void delpostest(int i) { int index = i; void *elem = array[i], *ret; /* i points to the right element */ while (i < arraylen-1) { array[i] = array[i+1]; i++; } arraylen--; /* delete elem from array */ if (tree->cmp) ret = del234(tree, elem); else ret = delpos234(tree, index); if (ret != elem) { error("del returned %p, expected %p", ret, elem); } verify(); } void deltest(void *elem) { int i; i = 0; while (i < arraylen && cmp(elem, array[i]) > 0) i++; if (i >= arraylen || cmp(elem, array[i]) != 0) return; /* don't do it! */ delpostest(i); } /* A sample data set and test utility. Designed for pseudo-randomness, * and yet repeatability. */ /* * This random number generator uses the `portable implementation' * given in ANSI C99 draft N869. It assumes `unsigned' is 32 bits; * change it if not. */ int randomnumber(unsigned *seed) { *seed *= 1103515245; *seed += 12345; return ((*seed) / 65536) % 32768; } int mycmp(void *av, void *bv) { char const *a = (char const *)av; char const *b = (char const *)bv; return strcmp(a, b); } #define lenof(x) ( sizeof((x)) / sizeof(*(x)) ) char *strings[] = { "a", "ab", "absque", "coram", "de", "palam", "clam", "cum", "ex", "e", "sine", "tenus", "pro", "prae", "banana", "carrot", "cabbage", "broccoli", "onion", "zebra", "penguin", "blancmange", "pangolin", "whale", "hedgehog", "giraffe", "peanut", "bungee", "foo", "bar", "baz", "quux", "murfl", "spoo", "breen", "flarn", "octothorpe", "snail", "tiger", "elephant", "octopus", "warthog", "armadillo", "aardvark", "wyvern", "dragon", "elf", "dwarf", "orc", "goblin", "pixie", "basilisk", "warg", "ape", "lizard", "newt", "shopkeeper", "wand", "ring", "amulet" }; #define NSTR lenof(strings) int findtest(void) { const static int rels[] = { REL234_EQ, REL234_GE, REL234_LE, REL234_LT, REL234_GT }; const static char *const relnames[] = { "EQ", "GE", "LE", "LT", "GT" }; int i, j, rel, index; char *p, *ret, *realret, *realret2; int lo, hi, mid, c; for (i = 0; i < NSTR; i++) { p = strings[i]; for (j = 0; j < sizeof(rels)/sizeof(*rels); j++) { rel = rels[j]; lo = 0; hi = arraylen-1; while (lo <= hi) { mid = (lo + hi) / 2; c = strcmp(p, array[mid]); if (c < 0) hi = mid-1; else if (c > 0) lo = mid+1; else break; } if (c == 0) { if (rel == REL234_LT) ret = (mid > 0 ? array[--mid] : NULL); else if (rel == REL234_GT) ret = (mid < arraylen-1 ? array[++mid] : NULL); else ret = array[mid]; } else { assert(lo == hi+1); if (rel == REL234_LT || rel == REL234_LE) { mid = hi; ret = (hi >= 0 ? array[hi] : NULL); } else if (rel == REL234_GT || rel == REL234_GE) { mid = lo; ret = (lo < arraylen ? array[lo] : NULL); } else ret = NULL; } realret = findrelpos234(tree, p, NULL, rel, &index); if (realret != ret) { error("find(\"%s\",%s) gave %s should be %s", p, relnames[j], realret, ret); } if (realret && index != mid) { error("find(\"%s\",%s) gave %d should be %d", p, relnames[j], index, mid); } if (realret && rel == REL234_EQ) { realret2 = index234(tree, index); if (realret2 != realret) { error("find(\"%s\",%s) gave %s(%d) but %d -> %s", p, relnames[j], realret, index, index, realret2); } } #if 0 printf("find(\"%s\",%s) gave %s(%d)\n", p, relnames[j], realret, index); #endif } } realret = findrelpos234(tree, NULL, NULL, REL234_GT, &index); if (arraylen && (realret != array[0] || index != 0)) { error("find(NULL,GT) gave %s(%d) should be %s(0)", realret, index, array[0]); } else if (!arraylen && (realret != NULL)) { error("find(NULL,GT) gave %s(%d) should be NULL", realret, index); } realret = findrelpos234(tree, NULL, NULL, REL234_LT, &index); if (arraylen && (realret != array[arraylen-1] || index != arraylen-1)) { error("find(NULL,LT) gave %s(%d) should be %s(0)", realret, index, array[arraylen-1]); } else if (!arraylen && (realret != NULL)) { error("find(NULL,LT) gave %s(%d) should be NULL", realret, index); } } int main(void) { int in[NSTR]; int i, j, k; unsigned seed = 0; for (i = 0; i < NSTR; i++) in[i] = 0; array = NULL; arraylen = arraysize = 0; tree = newtree234(mycmp); cmp = mycmp; verify(); for (i = 0; i < 10000; i++) { j = randomnumber(&seed); j %= NSTR; printf("trial: %d\n", i); if (in[j]) { printf("deleting %s (%d)\n", strings[j], j); deltest(strings[j]); in[j] = 0; } else { printf("adding %s (%d)\n", strings[j], j); addtest(strings[j]); in[j] = 1; } findtest(); } while (arraylen > 0) { j = randomnumber(&seed); j %= arraylen; deltest(array[j]); } freetree234(tree); /* * Now try an unsorted tree. We don't really need to test * delpos234 because we know del234 is based on it, so it's * already been tested in the above sorted-tree code; but for * completeness we'll use it to tear down our unsorted tree * once we've built it. */ tree = newtree234(NULL); cmp = NULL; verify(); for (i = 0; i < 1000; i++) { printf("trial: %d\n", i); j = randomnumber(&seed); j %= NSTR; k = randomnumber(&seed); k %= count234(tree)+1; printf("adding string %s at index %d\n", strings[j], k); addpostest(strings[j], k); } while (count234(tree) > 0) { printf("cleanup: tree size %d\n", count234(tree)); j = randomnumber(&seed); j %= count234(tree); printf("deleting string %s from index %d\n", array[j], j); delpostest(j); } return 0; } #endif kamailio-4.0.4/obsolete/jabber_k/xstream.c0000644000000000000000000001331212223032460017177 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include "xode.h" /* xode_stream is a way to have a consistent method of handling incoming XML Stream based events... it doesn't handle the generation of an XML Stream, but provides some facilities to help do that */ static void _xode_put_expatattribs(xode owner, const char** atts) { int i = 0; if (atts == NULL) return; while (atts[i] != '\0') { xode_put_attrib(owner, atts[i], atts[i+1]); i += 2; } } /******* internal expat callbacks *********/ static void _xode_stream_startElement(xode_stream xs, const char* name, const char** atts) { xode_pool p; /* if xode_stream is bad, get outa here */ if(xs->status > XODE_STREAM_NODE) return; if(xs->node == NULL) { p = xode_pool_heap(5*1024); /* 5k, typically 1-2k each plus copy of self and workspace */ xs->node = xode_new_frompool(p,name); _xode_put_expatattribs(xs->node, atts); if(xs->status == XODE_STREAM_ROOT) { xs->status = XODE_STREAM_NODE; /* flag status that we're processing nodes now */ (xs->f)(XODE_STREAM_ROOT, xs->node, xs->arg); /* send the root, f must free all nodes */ xs->node = NULL; } }else{ xs->node = xode_insert_tag(xs->node, name); _xode_put_expatattribs(xs->node, atts); } /* depth check */ xs->depth++; if(xs->depth > XODE_STREAM_MAXDEPTH) xs->status = XODE_STREAM_ERROR; } static void _xode_stream_endElement(xode_stream xs, const char* name) { xode parent; /* if xode_stream is bad, get outa here */ if(xs->status > XODE_STREAM_NODE) return; /* if it's already NULL we've received , tell the app and we're outta here */ if(xs->node == NULL) { xs->status = XODE_STREAM_CLOSE; (xs->f)(XODE_STREAM_CLOSE, NULL, xs->arg); }else{ parent = xode_get_parent(xs->node); /* we are the top-most node, feed to the app who is responsible to delete it */ if(parent == NULL) (xs->f)(XODE_STREAM_NODE, xs->node, xs->arg); xs->node = parent; } xs->depth--; } static void _xode_stream_charData(xode_stream xs, const char *str, int len) { /* if xode_stream is bad, get outa here */ if(xs->status > XODE_STREAM_NODE) return; if(xs->node == NULL) { /* we must be in the root of the stream where CDATA is irrelevant */ return; } xode_insert_cdata(xs->node, str, len); } static void _xode_stream_cleanup(void *arg) { xode_stream xs = (xode_stream)arg; xode_free(xs->node); /* cleanup anything left over */ XML_ParserFree(xs->parser); } /* creates a new xode_stream with given pool, xode_stream will be cleaned up w/ pool */ xode_stream xode_stream_new(xode_pool p, xode_stream_onNode f, void *arg) { xode_stream newx; if(p == NULL || f == NULL) { fprintf(stderr,"Fatal Programming Error: xode_streamnew() was improperly called with NULL.\n"); return NULL; } newx = xode_pool_malloco(p, sizeof(_xode_stream)); newx->p = p; newx->f = f; newx->arg = arg; /* create expat parser and ensure cleanup */ newx->parser = XML_ParserCreate(NULL); XML_SetUserData(newx->parser, (void *)newx); XML_SetElementHandler(newx->parser, (void (*)(void*, const char*, const char**))_xode_stream_startElement, (void (*)(void*, const char*))_xode_stream_endElement); XML_SetCharacterDataHandler(newx->parser, (void (*)(void*, const char*, int))_xode_stream_charData); xode_pool_cleanup(p, _xode_stream_cleanup, (void *)newx); return newx; } /* attempts to parse the buff onto this stream firing events to the handler, returns the last known status */ int xode_stream_eat(xode_stream xs, char *buff, int len) { char *err; xode xerr; static char maxerr[] = "maximum node size reached"; static char deeperr[] = "maximum node depth reached"; if(xs == NULL) { fprintf(stderr,"Fatal Programming Error: xode_streameat() was improperly called with NULL.\n"); return XODE_STREAM_ERROR; } if(len == 0 || buff == NULL) return xs->status; if(len == -1) /* easy for hand-fed eat calls */ len = strlen(buff); if(!XML_Parse(xs->parser, buff, len, 0)) { err = (char *)XML_ErrorString(XML_GetErrorCode(xs->parser)); xs->status = XODE_STREAM_ERROR; }else if(xode_pool_size(xode_get_pool(xs->node)) > XODE_STREAM_MAXNODE || xs->cdata_len > XODE_STREAM_MAXNODE){ err = maxerr; xs->status = XODE_STREAM_ERROR; }else if(xs->status == XODE_STREAM_ERROR){ /* set within expat handlers */ err = deeperr; }else{ err = deeperr; } /* fire parsing error event, make a node containing the error string */ if(xs->status == XODE_STREAM_ERROR) { xerr = xode_new("error"); xode_insert_cdata(xerr,err,-1); (xs->f)(XODE_STREAM_ERROR, xerr, xs->arg); } return xs->status; } kamailio-4.0.4/obsolete/jabber_k/xjab_presence.h0000644000000000000000000000370412223032460020335 0ustar rootroot/* * $Id$ * * XJAB module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _XJAB_PRESENCE_H_ #define _XJAB_PRESENCE_H_ #include "../../str.h" #include "xjab_base.h" #define XJ_PS_CHECK -1 #define XJ_PS_OFFLINE 0 #define XJ_PS_ONLINE 1 #define XJ_PS_TERMINATED 2 #define XJ_PS_REFUSED 3 #define XJ_PRES_STATUS_NULL 0 #define XJ_PRES_STATUS_SUBS 1 #define XJ_PRES_STATUS_WAIT 2 typedef struct _xj_pres_cell { int key; str userid; int state; int status; pa_callback_f cbf; void *cbp; struct _xj_pres_cell *prev; struct _xj_pres_cell *next; } t_xj_pres_cell, *xj_pres_cell; typedef struct _xj_pres_list { int nr; xj_pres_cell clist; } t_xj_pres_list, *xj_pres_list; xj_pres_cell xj_pres_cell_new(void); void xj_pres_cell_free(xj_pres_cell); void xj_pres_cell_free_all(xj_pres_cell); int xj_pres_cell_init(xj_pres_cell, str*, pa_callback_f, void*); int xj_pres_cell_update(xj_pres_cell, pa_callback_f, void*); xj_pres_list xj_pres_list_init(void); void xj_pres_list_free(xj_pres_list); xj_pres_cell xj_pres_list_add(xj_pres_list, xj_pres_cell); int xj_pres_list_del(xj_pres_list, str*); xj_pres_cell xj_pres_list_check(xj_pres_list, str*); void xj_pres_list_notifyall(xj_pres_list,int); #endif kamailio-4.0.4/obsolete/jabber_k/sha.c0000644000000000000000000001300212223032460016263 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Gabber * Copyright (C) 1999-2000 Dave Smith & Julian Missig * */ /* Implements the Secure Hash Algorithm (SHA1) Copyright (C) 1999 Scott G. Miller Released under the terms of the GNU General Public License v2 see file COPYING for details Credits: Robert Klep -- Expansion function fix Thomas "temas" Muldowney : -- shahash() for string fun -- Will add the int32 stuff in a few --- FIXME: This source takes int to be a 32 bit integer. This may vary from system to system. I'd use autoconf if I was familiar with it. Anyone want to help me out? */ //#include #include #include #include #include #ifndef MACOS # include # include #endif #ifndef WIN32 # include # define INT64 long long #else # define snprintf _snprintf # define INT64 __int64 #endif #define switch_endianness(x) (x<<24 & 0xff000000) | \ (x<<8 & 0x00ff0000) | \ (x>>8 & 0x0000ff00) | \ (x>>24 & 0x000000ff) /* Initial hash values */ #define Ai 0x67452301 #define Bi 0xefcdab89 #define Ci 0x98badcfe #define Di 0x10325476 #define Ei 0xc3d2e1f0 /* SHA1 round constants */ #define K1 0x5a827999 #define K2 0x6ed9eba1 #define K3 0x8f1bbcdc #define K4 0xca62c1d6 /* Round functions. Note that f2() is used in both rounds 2 and 4 */ #define f1(B,C,D) ((B & C) | ((~B) & D)) #define f2(B,C,D) (B ^ C ^ D) #define f3(B,C,D) ((B & C) | (B & D) | (C & D)) /* left circular shift functions (rotate left) */ #define rol1(x) ((x<<1) | ((x>>31) & 1)) #define rol5(A) ((A<<5) | ((A>>27) & 0x1f)) #define rol30(B) ((B<<30) | ((B>>2) & 0x3fffffff)) /* Hashes 'data', which should be a pointer to 512 bits of data (sixteen 32 bit ints), into the ongoing 160 bit hash value (five 32 bit ints) 'hash' */ int sha_hash(int *data, int *hash) { int W[80]; unsigned int A=hash[0], B=hash[1], C=hash[2], D=hash[3], E=hash[4]; unsigned int t, x, TEMP; for (t=0; t<16; t++) { #ifndef WORDS_BIGENDIAN W[t]=switch_endianness(data[t]); #else W[t]=data[t]; #endif } /* SHA1 Data expansion */ for (t=16; t<80; t++) { x=W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]; W[t]=rol1(x); } /* SHA1 main loop (t=0 to 79) This is broken down into four subloops in order to use the correct round function and constant */ for (t=0; t<20; t++) { TEMP=rol5(A) + f1(B,C,D) + E + W[t] + K1; E=D; D=C; C=rol30(B); B=A; A=TEMP; } for (; t<40; t++) { TEMP=rol5(A) + f2(B,C,D) + E + W[t] + K2; E=D; D=C; C=rol30(B); B=A; A=TEMP; } for (; t<60; t++) { TEMP=rol5(A) + f3(B,C,D) + E + W[t] + K3; E=D; D=C; C=rol30(B); B=A; A=TEMP; } for (; t<80; t++) { TEMP=rol5(A) + f2(B,C,D) + E + W[t] + K4; E=D; D=C; C=rol30(B); B=A; A=TEMP; } hash[0]+=A; hash[1]+=B; hash[2]+=C; hash[3]+=D; hash[4]+=E; return 0; } /* Takes a pointer to a 160 bit block of data (five 32 bit ints) and initializes it to the start constants of the SHA1 algorithm. This must be called before using hash in the call to sha_hash */ int sha_init(int *hash) { hash[0]=Ai; hash[1]=Bi; hash[2]=Ci; hash[3]=Di; hash[4]=Ei; return 0; } int strprintsha(char *dest, int *hashval) { int x; char *hashstr = dest; for (x=0; x<5; x++) { snprintf(hashstr, 9, "%08x", hashval[x]); hashstr+=8; } /*old way */ //snprintf(hashstr++, 1, "\0"); /*new way - by bogdan*/ *hashstr = 0; return 0; } char *shahash(const char *str) { char read_buffer[65]; //int read_buffer[64]; int c=1, i; INT64 length=0; int strsz; static char final[40]; int *hashval; hashval = (int *)malloc(20); sha_init(hashval); strsz = strlen(str); if(strsz == 0) { memset(read_buffer, 0, 65); read_buffer[0] = 0x80; sha_hash((int *)read_buffer, hashval); } while (strsz>0) { memset(read_buffer, 0, 65); strncpy((char*)read_buffer, str, 64); c = strlen((char *)read_buffer); length+=c; strsz-=c; if (strsz<=0) { length<<=3; read_buffer[c]=(char)0x80; for (i=c+1; i<64; i++) read_buffer[i]=0; if (c>55) { /* we need to do an entire new block */ sha_hash((int *)read_buffer, hashval); for (i=0; i<14; i++) ((int*)read_buffer)[i]=0; } #ifndef WORDS_BIGENDIAN for (i=0; i<8; i++) { read_buffer[56+i]=(char)(length>>(56-(i*8))) & 0xff; } #else memcpy(read_buffer+56, &length, 8); #endif } sha_hash((int *)read_buffer, hashval); str+=64; } strprintsha((char *)final, hashval); free(hashval); return (char *)final; } kamailio-4.0.4/obsolete/jabber_k/README0000644000000000000000000003310612223032460016233 0ustar rootrootjabber Module Daniel-Constantin Mierla Edited by Daniel-Constantin Mierla Copyright © 2003 FhG FOKUS Revision History Revision $Revision$ $Date: 2008-08-06 12:08:33 +0200 (Mi, 06 Aug 2008) $ __________________________________________________________ Table of Contents 1. Admin Guide 1.1. Overview 1.1.1. New Features 1.2. Admin's Guide 1.3. Admin Guide 1.4. Dependencies 1.4.1. Kamailio Modules 1.4.2. External Libraries or Applications 1.5. Exported Parameters 1.5.1. db_url (string) 1.5.2. jaddress (string) 1.5.3. jport (integer) 1.5.4. jdomain (string) 1.5.5. aliases (string) 1.5.6. proxy (string) 1.5.7. registrar (string) 1.5.8. workers (integer) 1.5.9. max_jobs (integer) 1.5.10. cache_time (integer) 1.5.11. delay_time (integer) 1.5.12. sleep_time (integer) 1.5.13. check_time (integer) 1.5.14. priority (str) 1.6. Exported Functions 1.6.1. jab_send_message() 1.6.2. jab_join_jconf() 1.6.3. jab_exit_jconf() 1.6.4. jab_go_online() 1.6.5. jab_go_offline() List of Examples 1.1. Set db_url parameter 1.2. Set jaddress parameter 1.3. Set jport parameter 1.4. Set jdomain parameter 1.5. Set jdomain parameter 1.6. Set proxy parameter 1.7. Set registrar parameter 1.8. Set workers parameter 1.9. Set max_jobs parameter 1.10. Set cache_time parameter 1.11. Set delay_time parameter 1.12. Set sleep_time parameter 1.13. Set check_time parameter 1.14. Set priority parameter 1.15. jab_send_message() usage 1.16. jab_join_jconf() usage 1.17. jab_exit_jconf() usage 1.18. jab_go_online() usage 1.19. jab_go_offline() usage Chapter 1. Admin Guide 1.1. Overview This is new version of Jabber module that integrates XODE XML parser for parsing Jabber messages. That introduces a new module dependency: expat library. Expat is a common XML library and is the fastest available for Linux/Unix, the second over all, after msxml library. It is integrated in most of well known Linux distributions. Please not that this module is deprecated and will be removed in the next major release of Kamailio. The purple module should be used instead. 1.1.1. New Features * Presence support (see doc/xxjab.cfg for a sample cfg file) (January 2003). * SIP to Jabber conference support (December 2003). * Possibility to manage all kinds of Jabber messages (message/presence/iq) (December 2003). * Aliases -- Possibility to set host aliases for addresses (see parameter's desc.) (December 2003). * Send received SIP MESSAGE messages to different IM networks (Jabber, ICQ,MSN, AIM, Yahoo) using a Jabber server (December 2003). * Send incoming Jabber instant messages as SIP MESSAGE messages. * Gateways detection -- Ability to see whether an IM gateway is up or down. 1.2. Admin's Guide Note A more complete guide about SIMPLE2Jabber gateway can be found at http://www.kamailio.org/. The part below will be removed soon, only the manual from web will be updated. The Jabber server setup is not a subject of this guide. Check http://www.jabber.org for that. Useful scripts, for creating Jabber Gateway database, or for managing the Jabber accounts form web are located in 'doc' subdirectory of the module. Main steps of using the Jabber gateway: * Create the MySQL database. * Setup the local Jabber server. * Set the module parameter values in cfg file of Kamailio, load the dependent modules, set up the routing rules for Jabber gateway. * Run Kamailio. The administrator of Kamailio/Jabber gateway must inform the users what are the aliases for Jabber/Other IM networks. Other IMs could be AIM, ICQ, MSN, Yahoo, and so on. These aliases depend on the server hostname where runs Kamailio and how local Jabber server is setup. Next is presented a use case. Prologue: * Kamailio is running on "server.org". * Local Jabber server is running on "jabsrv.server.org". * Jabber network alias (first part of "jdomain") is "jabber.server.org" The aliases for other IM networks must be the same as JID set in Jabber configuration file for each IM transport. The JIDs of Jabber transports must start with the name of the network. For AIM, JID must start with "aim.", for ICQ with "icq" (that because I use icqv7-t), for MSN with "msn." and for Yahoo with "yahoo.". The gateway needs these to find out what transport is working and which not. For our use case these could be like "aim.server.org", "icq.server.org", "msn.server.org", "yahoo.server.org". It is indicated to have these aliases in DNS, thus the client application can resolve the DNS name. Otherwise there must be set the outbound proxy to Kamailio server. *** Routing rules for Jabber gateway First step is to configure Kamailio to recognize messages for Jabber gateway. Look at "doc/xjab.cfg" to see a sample. The idea is to look in messages for destination address and if it contains Jabber alias or other IM alias, that means the message is for Jabber gateway. Next step is to find out what means that message for Jabber gateway. It could be a special message what triggers the gateway to take an action or is a simple message which should be delivered to Jabber network (using the method "jab_send_message"). The special messages are for: * Registering to Jabber server (go online in Jabber network)--here must be called "jab_go_online" method. * Leaving the Jabber network (go offline in Jabber network)--here must be called "jab_go_offline" method. * Joining a Jabber conference room--here must be called "jab_join_jconf". * Leaving a Jabber conference room--here must be called "jab_exit_jconf". The destination address must follow the following patterns: * For Jabber network: "usernamejabber_server@jabber_alias". * For Jabber conference: "nicknameroomconference_server@jabber_alias". * For AIM network: "aim_username@aim_alias". * For ICQ network: "icq_number@icq_alias". * For MSN network: "msn_usernamemsn_server@msn_alias". msn_server can be "msn.com" or "hotmail.com". * For YAHOO network: "yahoo_username@yahoo_alias". Note "jabber_alias" is the first part of "jdomain". 1.3. Admin Guide The user must activate his Jabber account associated with his SIP id. For each other IM network on which he wants to send messages, he must set an account for that IM network. The gateway is not able to create new account in foreign networks, excepting local Jabber server. When you want to send a message to someone in other IM network, you must set the destination of the message according with the pattern corresponding to that IM network (see last part of "Admin guide" chapter). Sending a message to user@jabber.xxx.org which is in Jabber network, the destination must be: userjabber.xxx.org@jabber_alias. For someone who is in Yahoo network the destination must be: user@yahoo_alias Note The Kamailio administrator have to set the Jabber transports for each IM network in order to be able to send messages to those networks. The alias of each IM network can be found out from Kamailio admin. You cannot send messages from your SIP client to your associated Jabber account--is something like sending messages to yourself. 1.4. Dependencies 1.4.1. Kamailio Modules The following modules must be loaded before this module: * A database module. * pa (Optionally) - Presence Agent. * tm - Transaction Manager. 1.4.2. External Libraries or Applications The following libraries or applications must be installed before running Kamailio with this module loaded: * Expat library. 1.5. Exported Parameters 1.5.1. db_url (string) SQL URL of database. Default value is "mysql://root@127.0.0.1/sip_jab". Example 1.1. Set db_url parameter ... modparam("jabber", "db_url", "mysql://username:password@host/sip_jab") ... 1.5.2. jaddress (string) IP or hostname of Jabber server -- it must be the same as the value from tag of Jabber server config file. Default value is "127.0.0.1". Example 1.2. Set jaddress parameter ... modparam("jabber", "jaddress", "1.2.3.4") ... 1.5.3. jport (integer) Port number of Jabber server. Default value is "5222". Example 1.3. Set jport parameter ... modparam("jabber", "jport", 1234) ... 1.5.4. jdomain (string) Format: jabber.sipserver.com=. If the destination is for Jabber network the URI should be like: usernamejabber_server@jdomain or nicknameroomnameconference_server@jdomain must be a un-reserved character. By default this character is * . The destination will be transformed to username@jabber_server or roomname@conference_server/nickname before the message is sent to Jabber server. Default value is none. Example 1.4. Set jdomain parameter ... modparam("jabber", "jdomain", "jabber.sipserver.com=*") ... 1.5.5. aliases (string) Aliases for IM networks. Format: "N;alias1=;...;aliasN=;" Destinations like '*@aliasX' could have other format than those specified for Jabber network. All from user part of the destination address will be changed to if the destination address contains . (Ex: jdomain is 'jabber.x.com=*' and msn_alias is 'msn.x.com=%'. The destination address forM MSN Network, on SIP side, is like 'username*hotmail.com@msn.x.com'. The destination address will be transformed to 'username%hotmail.com@msn.x.com'. 'msn.x.com' must be the same as the JID associated with MSN transport in Jabber configuration file (usually is 'jabberd.xml')) Default value is none. Example 1.5. Set jdomain parameter ... modparam("jabber", "aliases", "1;msn.x.com=%") ... 1.5.6. proxy (string) Outbound proxy address. Format: ip_address:port hostname:port All SIP messages generated by gateway will be sent to that address. If is missing, the message will be delivered to the hostname of the destination address Default value is none. Example 1.6. Set proxy parameter ... modparam("jabber", "proxy", "10.0.0.1:5060 sipserver.com:5060") ... 1.5.7. registrar (string) The address in whose behalf the INFO and ERROR messages are sent. Default value is "jabber_gateway@127.0.0.1". Example 1.7. Set registrar parameter ... modparam("jabber", "registrar", "jabber_gateway@127.0.0.1") ... 1.5.8. workers (integer) Number of workers. Default value is 2. Example 1.8. Set workers parameter ... modparam("jabber", "workers", 2) ... 1.5.9. max_jobs (integer) Maximum jobs per worker. Default value is 10. Example 1.9. Set max_jobs parameter ... modparam("jabber", "max_jobs", 10) ... 1.5.10. cache_time (integer) Cache time of a Jabber connection. Default value is 600. Example 1.10. Set cache_time parameter ... modparam("jabber", "cache_time", 600) ... 1.5.11. delay_time (integer) Time to keep a SIP message (in seconds). Default value is 90 seconds. Example 1.11. Set delay_time parameter ... modparam("jabber", "delay_time", 90) ... 1.5.12. sleep_time (integer) Time between expired Jabber connections checking (in seconds). Default value is 20 seconds. Example 1.12. Set sleep_time parameter ... modparam("jabber", "sleep_time", 20) ... 1.5.13. check_time (integer) Time between checking the status of JabberGW workers (in seconds). Default value is 20 seconds. Example 1.13. Set check_time parameter ... modparam("jabber", "check_time", 20) ... 1.5.14. priority (str) Presence priority for Jabber gateway. Default value is "9". Example 1.14. Set priority parameter ... modparam("jabber", "priority", "3") ... 1.6. Exported Functions 1.6.1. jab_send_message() Converts SIP MESSAGE message to a Jabber message and sends it to Jabber server. This function can be used from REQUEST_ROUTE. Example 1.15. jab_send_message() usage ... jab_send_message(); ... 1.6.2. jab_join_jconf() Join a Jabber conference--the nickname, room name and conference server address should be included in To header as: nickname%roomname%conference_server@jdomain . If the nickname is missing, then the SIP username is used. This function can be used from REQUEST_ROUTE. Example 1.16. jab_join_jconf() usage ... jab_join_jconf(); ... 1.6.3. jab_exit_jconf() Leave a Jabber conference--the nickname, room name and conference server address should be included in To header as: nickname%roomname%conference_server@jdomain . This function can be used from REQUEST_ROUTE. Example 1.17. jab_exit_jconf() usage ... jab_exit_jconf(); ... 1.6.4. jab_go_online() Register to the Jabber server with associated Jabber ID of the SIP user. This function can be used from REQUEST_ROUTE. Example 1.18. jab_go_online() usage ... jab_go_online(); ... 1.6.5. jab_go_offline() Log off from Jabber server the associated Jabber ID of the SIP user. This function can be used from REQUEST_ROUTE. Example 1.19. jab_go_offline() usage ... jab_go_offline(); ... kamailio-4.0.4/obsolete/jabber_k/xjab_base.c0000644000000000000000000000651312223032460017437 0ustar rootroot/* * $Id$ * * eXtended JABber module - Jabber connections pool * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../parser/parse_uri.h" #include "xjab_base.h" #include "mdefines.h" /** * get the hash code - based on Andrei's function * */ int xj_get_hash(str *x, str *y) { char* p; register unsigned v; register unsigned h; if(!x && !y) return 0; h=0; if(x) { for (p=x->s; p<=(x->s+x->len-4); p+=4) { v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; h+=v^(v>>3); } v=0; for (;p<(x->s+x->len); p++) { v<<=8; v+=*p; } h+=v^(v>>3); } if(y) { for (p=y->s; p<=(y->s+y->len-4); p+=4) { v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; h+=v^(v>>3); } v=0; for (;p<(y->s+y->len); p++) { v<<=8; v+=*p; } h+=v^(v>>3); } h=((h)+(h>>11))+((h>>13)+(h>>23)); return (h)?h:1; } /** * function used to compare two xj_jkey elements */ int xj_jkey_cmp(void *x, void *y) { int n; xj_jkey a, b; a = (xj_jkey)x; b = (xj_jkey)y; if(a == NULL || a->id == NULL || a->id->s == NULL) return -1; if(b == NULL || b->id == NULL || b->id->s == NULL) return 1; // LM_DBG("comparing <%.*s> / <%.*s>\n", ((str *)a)->len, // ((str *)a)->s, ((str *)b)->len, ((str *)b)->s); if(a->hash != b->hash) return (a->hash < b->hash)?-1:1; if(a->id->len != b->id->len) return (a->id->len < b->id->len)?-1:1; n=strncmp(a->id->s,b->id->s,a->id->len); if(n!=0) return (n<0)?-1:1; return 0; } /** * free the information from a jkey */ void xj_jkey_free_p(void *p) { if(p == NULL) return; if(((xj_jkey)p)->id != NULL) { if(((xj_jkey)p)->id->s != NULL) _M_SHM_FREE(((xj_jkey)p)->id->s); _M_SHM_FREE(((xj_jkey)p)->id); } _M_SHM_FREE(p); } /** * free a pointer to a t_jab_sipmsg structure * > element where points 'from' MUST be deliberated separated */ void xj_sipmsg_free(xj_sipmsg jsmsg) { if(jsmsg == NULL) return; if(jsmsg->to.s != NULL) _M_SHM_FREE(jsmsg->to.s); // the key is deallocated when the connection is closed // if(jsmsg->jkey->id->s != NULL) // _M_SHM_FREE(jsmsg->from->id->s); if(jsmsg->msg.s != NULL) _M_SHM_FREE(jsmsg->msg.s); _M_SHM_FREE(jsmsg); } int xj_extract_aor(str* u, int t) { struct sip_uri puri; if(!u) return -1; if (parse_uri(u->s, u->len, &puri) < 0) { LM_ERR("failed to parse URI\n"); return -1; } if(t == 1) u->s = puri.user.s; u->len = puri.host.s + puri.host.len - u->s; return 0; } kamailio-4.0.4/obsolete/jabber_k/xjab_jcon.c0000644000000000000000000004014712223032460017457 0ustar rootroot/* * $Id$ * * eXtended JABber module - functions used for SIP 2 JABBER communication * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../timer.h" #include "xjab_jcon.h" #include "xjab_util.h" #include "xode.h" #include "mdefines.h" #define JB_ID_BASE "SJ" #define JB_START_STREAM "" #define JB_START_STREAM_LEN 21 #define JB_CLIENT_OPEN_STREAM "" #define JB_IQ_ROSTER_GET "" #define XJ_MAX_JCONF 12 /** * init a JABBER connection */ xj_jcon xj_jcon_init(char *hostname, int port) { xj_jcon jbc = NULL; if(hostname==NULL || strlen(hostname)<=0) return NULL; jbc = (xj_jcon)_M_MALLOC(sizeof(struct _xj_jcon)); if(jbc == NULL) return NULL; jbc->sock=-1; jbc->port = port; jbc->juid = -1; jbc->seq_nr = 0; jbc->hostname = (char*)_M_MALLOC(strlen(hostname)+1); if(jbc->hostname == NULL) { _M_FREE(jbc); return NULL; } strcpy(jbc->hostname, hostname); jbc->allowed = jbc->ready = XJ_NET_NUL; jbc->jconf = NULL; jbc->nrjconf = 0; jbc->plist = xj_pres_list_init(); if(jbc->plist == NULL) { _M_FREE(jbc->hostname); _M_FREE(jbc); return NULL; } return jbc; } /** * connect to JABBER server */ int xj_jcon_connect(xj_jcon jbc) { struct sockaddr_in address; struct hostent *he; int sock; // open connection to server if((sock = socket(AF_INET, SOCK_STREAM, 0))<0) { LM_DBG("failed to create the socket\n"); return -1; } #ifdef XJ_EXTRA_DEBUG LM_DBG("socket [%d]\n", sock); #endif he=gethostbyname(jbc->hostname); if(he == NULL) { LM_DBG("failed to get info about Jabber server address\n"); return -1; } // fill the fields of the address memcpy(&address.sin_addr, he->h_addr, he->h_length); address.sin_family=AF_INET; address.sin_port=htons(jbc->port); // try to connect with Jabber server if (connect(sock, (struct sockaddr *)&address, sizeof(address))<0) { LM_DBG("failed to connect with Jabber server\n"); return -1; } jbc->sock = sock; return 0; } /** * set the Jabber internal ID */ void xj_jcon_set_juid(xj_jcon jbc, int _juid) { if(jbc == NULL) return; jbc->juid = _juid; } /** * return the Jabber internal ID */ int xj_jcon_get_juid(xj_jcon jbc) { if(jbc == NULL) return -1; return jbc->juid; } /** * disconnect from JABBER server */ int xj_jcon_disconnect(xj_jcon jbc) { if(jbc == NULL || jbc->sock < 0) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----START-----\n"); LM_DBG("socket [%d]\n", jbc->sock); #endif xj_jcon_send_presence(jbc, NULL, "unavailable", NULL, NULL); if(send(jbc->sock, "", 16, 0) < 16) LM_DBG("failed to close the stream\n"); if(close(jbc->sock) == -1) LM_DBG("failed to close the socket\n"); jbc->sock = -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----END-----\n"); #endif return 0; } /** * authentication to the JABBER server */ int xj_jcon_user_auth(xj_jcon jbc, char *username, char *passwd, char *resource) { char msg_buff[4096]; int n, i, err; char *p0, *p1; xode x, y, z; /*** send open stream tag **/ sprintf(msg_buff, JB_CLIENT_OPEN_STREAM, jbc->hostname); if(send(jbc->sock, msg_buff, strlen(msg_buff), 0) != strlen(msg_buff)) goto error; n = recv(jbc->sock, msg_buff, 4096, 0); msg_buff[n] = 0; if(strncasecmp(msg_buff, JB_START_STREAM, JB_START_STREAM_LEN)) goto error; p0 = strstr(msg_buff + JB_START_STREAM_LEN, "id='"); if(p0 == NULL) goto error; p0 += 4; p1 = strchr(p0, '\''); if(p1 == NULL) goto error; jbc->stream_id = (char*)_M_MALLOC(p1-p0+1); strncpy(jbc->stream_id, p0, p1-p0); jbc->stream_id[p1-p0] = 0; sprintf(msg_buff, "%08X", jbc->seq_nr); x = xode_new_tag("iq"); if(!x) return -1; xode_put_attrib(x, "id", msg_buff); xode_put_attrib(x, "type", "get"); y = xode_insert_tag(x, "query"); xode_put_attrib(y, "xmlns", "jabber:iq:auth"); z = xode_insert_tag(y, "username"); xode_insert_cdata(z, username, -1); p0 = xode_to_str(x); n = strlen(p0); i = send(jbc->sock, p0, n, 0); if(i != n) goto errorx; xode_free(x); // receive response // try 10 times i = 10; while(i) { if((n = recv(jbc->sock, msg_buff, 4096, 0)) > 0) { msg_buff[n] = 0; break; } usleep(1000); i--; } if(!i) goto error; x = xode_from_strx(msg_buff, n, &err, &i); p0 = msg_buff; if(err) p0 += i; if(strncasecmp(xode_get_name(x), "iq", 2)) goto errorx; if((x = xode_get_tag(x, "query?xmlns=jabber:iq:auth")) == NULL) goto errorx; y = xode_new_tag("query"); xode_put_attrib(y, "xmlns", "jabber:iq:auth"); z = xode_insert_tag(y, "username"); xode_insert_cdata(z, username, -1); z = xode_insert_tag(y, "resource"); xode_insert_cdata(z, resource, -1); if(xode_get_tag(x, "digest") != NULL) { // digest authentication //sprintf(msg_buff, "%s%s", jbc->stream_id, passwd); strcpy(msg_buff, jbc->stream_id); strcat(msg_buff, passwd); //LM_DBG("[%s:%s]\n", jbc->stream_id, passwd); p1 = shahash(msg_buff); z = xode_insert_tag(y, "digest"); xode_insert_cdata(z, p1, -1); } else { // plaint text authentication z = xode_insert_tag(y, "password"); xode_insert_cdata(z, passwd, -1); } y = xode_wrap(y, "iq"); jbc->seq_nr++; sprintf(msg_buff, "%08X", jbc->seq_nr); xode_put_attrib(y, "id", msg_buff); xode_put_attrib(y, "type", "set"); p1 = xode_to_str(y); n = strlen(p1); i = send(jbc->sock, p1, n, 0); if(i != n) { xode_free(y); goto errorx; } xode_free(x); xode_free(y); // receive response // try 10 times i = 10; while(i) { if((n = recv(jbc->sock, msg_buff, 4096, 0)) > 0) { msg_buff[n] = 0; break; } usleep(1000); i--; } if(!i) goto error; x = xode_from_strx(msg_buff, n, &err, &i); p0 = msg_buff; if(err) p0 += i; if(strncasecmp(xode_get_name(x), "iq", 2) || strncasecmp(xode_get_attrib(x, "type"), "result", 6)) goto errorx; jbc->resource = (char*)_M_MALLOC(strlen(resource)+1); strcpy(jbc->resource, resource); jbc->allowed = XJ_NET_ALL; jbc->ready = XJ_NET_JAB; return 0; errorx: xode_free(x); error: return -1; } /** * receive the list of the roster */ int xj_jcon_get_roster(xj_jcon jbc) { int n = strlen(JB_IQ_ROSTER_GET); if(send(jbc->sock, JB_IQ_ROSTER_GET, n, 0) != n) return -1; return 0; } /** * add a new contact in user's roster */ int xj_jcon_set_roster(xj_jcon jbc, char* jid, char *type) { xode x; char *p; int n; char buff[16]; if(!jbc || !jid) return -1; x = xode_new_tag("item"); if(!x) return -1; xode_put_attrib(x, "jid", jid); if(type != NULL) xode_put_attrib(x, "subscription", type); x = xode_wrap(x, "query"); xode_put_attrib(x, "xmlns", "jabber:iq:roster"); x = xode_wrap(x, "iq"); xode_put_attrib(x, "type", "set"); jbc->seq_nr++; sprintf(buff, "%08X", jbc->seq_nr); xode_put_attrib(x, "id", buff); p = xode_to_str(x); n = strlen(p); if(send(jbc->sock, p, n, 0) != n) { LM_DBG("item not sent\n"); goto error; } xode_free(x); return 0; error: xode_free(x); return -1; } /** * send a message through a JABBER connection * params are pairs (buffer, len) */ int xj_jcon_send_msg(xj_jcon jbc, char *to, int tol, char *msg, int msgl, int type) { char msg_buff[4096], *p; int n; xode x; if(jbc == NULL) return -1; x = xode_new_tag("body"); if(!x) return -1; xode_insert_cdata(x, msg, msgl); x = xode_wrap(x, "message"); strncpy(msg_buff, to, tol); msg_buff[tol] = 0; xode_put_attrib(x, "to", msg_buff); switch(type) { case XJ_JMSG_CHAT: xode_put_attrib(x, "type", "chat"); break; case XJ_JMSG_GROUPCHAT: xode_put_attrib(x, "type", "groupchat"); break; default: xode_put_attrib(x, "type", "normal"); } p = xode_to_str(x); n = strlen(p); #ifdef XJ_EXTRA_DEBUG LM_DBG("jabber msg:\n%s\n", p); #endif if(send(jbc->sock, p, n, 0) != n) { LM_DBG(" message not sent\n"); goto error; } xode_free(x); return 0; error: xode_free(x); return -1; } /** * receive a message from a JABBER connection * NOT USED */ int xj_jcon_recv_msg(xj_jcon jbc, char *from, char *msg) { //char msg_buff[4096]; //sprintf(msg_buff, JB_MSG_NORMAL, to, msg); //send(jbc->sock, msg_buff, strlen(msg_buff), 0); return 0; } /** * send presence * type - "unavailable", "subscribe", "subscribed" .... * status - "online", "away", "unavailable" ... * priority - "0", "1", ... */ int xj_jcon_send_presence(xj_jcon jbc, char *sto, char *type, char *status, char *priority) { char *p; int n; xode x, y; if(jbc == NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----START-----\n"); #endif x = xode_new_tag("presence"); if(!x) return -1; if(sto != NULL) xode_put_attrib(x, "to", sto); if(type != NULL) xode_put_attrib(x, "type", type); if(status != NULL) { y = xode_insert_tag(x, "status"); xode_insert_cdata(y, status, strlen(status)); } if(priority != NULL) { y = xode_insert_tag(x, "priority"); xode_insert_cdata(y, priority, strlen(priority)); } p = xode_to_str(x); n = strlen(p); if(send(jbc->sock, p, n, 0) != n) { LM_DBG("presence not sent\n"); goto error; } xode_free(x); #ifdef XJ_EXTRA_DEBUG LM_DBG("presence status was sent\n"); #endif return 0; error: xode_free(x); return -1; } /** * send subscribe for user's presence */ int xj_jcon_send_subscribe(xj_jcon jbc, char *to, char *from, char *type) { char *p; int n; xode x; if(!jbc || !to) return -1; x = xode_new_tag("presence"); if(!x) return -1; xode_put_attrib(x, "to", to); if(from != NULL) xode_put_attrib(x, "from", from); if(type != NULL) xode_put_attrib(x, "type", type); p = xode_to_str(x); n = strlen(p); if(send(jbc->sock, p, n, 0) != n) { LM_DBG("subscribe not sent\n"); goto error; } xode_free(x); return 0; error: xode_free(x); return -1; } /** * free the allocated memory space of a JABBER connection */ int xj_jcon_free(xj_jcon jbc) { xj_jconf jcf; if(jbc == NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----START-----\n"); #endif //if(jbc->sock != -1) // jb_disconnect(jbc); if(jbc->hostname != NULL) _M_FREE(jbc->hostname); if(jbc->stream_id != NULL) _M_FREE(jbc->stream_id); if(jbc->resource != NULL) _M_FREE(jbc->resource); #ifdef XJ_EXTRA_DEBUG LM_DBG("%d conferences\n", jbc->nrjconf); #endif while(jbc->nrjconf > 0) { if((jcf=delpos234(jbc->jconf,0))!=NULL) xj_jconf_free(jcf); jbc->nrjconf--; } xj_pres_list_free(jbc->plist); _M_FREE(jbc); #ifdef XJ_EXTRA_DEBUG LM_DBG("-----END-----\n"); #endif return 0; } /** * create a open connection to Jabber * - id : id of the connection * - jbc : pointer to Jabber connection * - cache_time : life time of the connection * - delay_time : time needed to became an active connection * return : pointer to the structure or NULL on error */ int xj_jcon_set_attrs(xj_jcon jbc, xj_jkey jkey, int cache_time, int delay_time) { int t; if(jbc==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; jbc->jkey = jkey; t = get_ticks(); jbc->expire = t + cache_time; jbc->ready = t + delay_time; return 0; } /** * update the life time of the connection * - ojc : pointer to the open connection * - cache_time : number of seconds to keep the connection open * return : 0 on success or <0 on error */ int xj_jcon_update(xj_jcon jbc, int cache_time) { if(jbc == NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("params [%.*s] %d\n", jbc->jkey->id->len, jbc->jkey->id->s, cache_time); #endif jbc->expire = get_ticks() + cache_time; return 0; } int xj_jcon_is_ready(xj_jcon jbc, char *to, int tol, char dl) { char *p; str sto; xj_jconf jcf = NULL; if(!jbc || !to || tol <= 0) return -1; sto.s = to; sto.len = tol; if(!xj_jconf_check_addr(&sto, dl)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("destination=conference\n"); #endif if((jcf=xj_jcon_get_jconf(jbc, &sto, dl))!=NULL) return (jcf->status & XJ_JCONF_READY)?0:3; #ifdef XJ_EXTRA_DEBUG LM_DBG("conference does not exist\n"); #endif return -1; } p = to; while(p < to+tol && *p!='@') p++; if(p>=to+tol) return -1; p++; if(!strncasecmp(p, XJ_AIM_NAME, XJ_AIM_LEN)) return (jbc->ready & XJ_NET_AIM)?0:((jbc->allowed & XJ_NET_AIM)?1:2); if(!strncasecmp(p, XJ_ICQ_NAME, XJ_ICQ_LEN)) return (jbc->ready & XJ_NET_ICQ)?0:((jbc->allowed & XJ_NET_ICQ)?1:2); if(!strncasecmp(p, XJ_MSN_NAME, XJ_MSN_LEN)) return (jbc->ready & XJ_NET_MSN)?0:((jbc->allowed & XJ_NET_MSN)?1:2); if(!strncasecmp(p, XJ_YAH_NAME, XJ_YAH_LEN)) return (jbc->ready & XJ_NET_YAH)?0:((jbc->allowed & XJ_NET_YAH)?1:2); #ifdef XJ_EXTRA_DEBUG LM_DBG("destination=jabber\n"); #endif return 0; } xj_jconf xj_jcon_get_jconf(xj_jcon jbc, str* sid, char dl) { xj_jconf jcf = NULL, p; if(!jbc || !sid || !sid->s || sid->len <= 0) return NULL; #ifdef XJ_EXTRA_DEBUG LM_DBG("looking for conference\n"); #endif if((jcf = xj_jconf_new(sid))==NULL) return NULL; if(xj_jconf_init_sip(jcf, jbc->jkey->id, dl)) goto clean; if(jbc->nrjconf && (p = find234(jbc->jconf, (void*)jcf, NULL)) != NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("conference found\n"); #endif xj_jconf_free(jcf); return p; } if(jbc->nrjconf >= XJ_MAX_JCONF) goto clean; if(jbc->nrjconf==0) if(jbc->jconf==NULL) if((jbc->jconf = newtree234(xj_jconf_cmp)) == NULL) goto clean; if((p = add234(jbc->jconf, (void*)jcf)) != NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("new conference created\n"); #endif jbc->nrjconf++; return p; } clean: LM_DBG("conference not found\n"); xj_jconf_free(jcf); return NULL; } xj_jconf xj_jcon_check_jconf(xj_jcon jbc, char* id) { str sid; xj_jconf jcf = NULL, p = NULL; if(!jbc || !id || !jbc->nrjconf) return NULL; #ifdef XJ_EXTRA_DEBUG LM_DBG("conference not found\n"); #endif sid.s = id; sid.len = strlen(id); if((jcf = xj_jconf_new(&sid))==NULL) return NULL; if(xj_jconf_init_jab(jcf)) goto clean; if((p = find234(jbc->jconf, (void*)jcf, NULL)) != NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("conference found\n"); #endif xj_jconf_free(jcf); return p; } clean: #ifdef XJ_EXTRA_DEBUG LM_DBG("conference not found\n"); #endif xj_jconf_free(jcf); return NULL; } int xj_jcon_jconf_presence(xj_jcon jbc, xj_jconf jcf, char* type, char* status) { char buff[256]; strncpy(buff, jcf->room.s, jcf->room.len + jcf->server.len +1); buff[jcf->room.len + jcf->server.len +1] = '/'; buff[jcf->room.len + jcf->server.len +2] = 0; buff[jcf->room.len] = '@'; strncat(buff, jcf->nick.s, jcf->nick.len); return xj_jcon_send_presence(jbc,buff,type,status,NULL); } int xj_jcon_del_jconf(xj_jcon jbc, str *sid, char dl, int flag) { xj_jconf jcf = NULL, p = NULL; if(!jbc || !sid || !sid->s || sid->len <= 0) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("deleting conference of <%.*s>\n", sid->len, sid->s); #endif if((jcf = xj_jconf_new(sid))==NULL) return -1; if(xj_jconf_init_sip(jcf, jbc->jkey->id, dl)) { xj_jconf_free(jcf); return -1; } p = del234(jbc->jconf, (void*)jcf); if(p != NULL) { if(flag == XJ_JCMD_UNSUBSCRIBE) xj_jcon_jconf_presence(jbc, jcf, "unavailable", NULL); jbc->nrjconf--; xj_jconf_free(p); #ifdef XJ_EXTRA_DEBUG LM_DBG("conference deleted\n"); #endif } xj_jconf_free(jcf); return 0; } /********** *********/ kamailio-4.0.4/obsolete/jabber_k/xjab_util.h0000644000000000000000000000402612223032460017504 0ustar rootroot/* * $Id$ * * eXtended JABber module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _XJAB_UTIL_H_ #define _XJAB_UTIL_H_ #include "xjab_base.h" #include "xjab_jcon.h" #include "../../str.h" /********** ***/ typedef struct _xj_jmsg_queue { int len; // maximum size of the queue int size; // number of elements in the queue int cache; // cache time (seconds) int *expire; // expire time of the queued message xj_sipmsg *jsm; // pointer to the message xj_jcon *ojc; // pointer to the connection which will be used on sending } t_xj_jmsg_queue, *xj_jmsg_queue; /********** ***/ typedef struct _xj_jcon_pool { int len; // maximum len of the pool xj_jcon *ojc; // the connections to the Jabber t_xj_jmsg_queue jmqueue; // messages queue } t_xj_jcon_pool, *xj_jcon_pool; /********** LOOK AT IMPLEMENTATION OF FUNCTIONS FOR DESCRIPTION ***/ xj_jcon_pool xj_jcon_pool_init(int, int, int); int xj_jcon_pool_add(xj_jcon_pool, xj_jcon); xj_jcon xj_jcon_pool_get(xj_jcon_pool, xj_jkey); int xj_jcon_pool_del(xj_jcon_pool, xj_jkey); void xj_jccon_pool_free(xj_jcon_pool); void xj_jcon_pool_print(xj_jcon_pool); int xj_jcon_pool_add_jmsg(xj_jcon_pool, xj_sipmsg, xj_jcon); int xj_jcon_pool_del_jmsg(xj_jcon_pool, int); /********** ***/ #endif kamailio-4.0.4/obsolete/jabber_k/xjab_wlist.c0000644000000000000000000003505612223032460017673 0ustar rootroot/* * $Id$ * * eXtended JABber module - worker implementation * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * --- * * History * ------- * 2003-02-24 added 'xj_wlist_set_flag' function (dcm) * 2003-03-11 major locking changes - uses locking.h (andrei) * */ #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "xjab_worker.h" #include "mdefines.h" #define XJ_DEF_JDELIM '*' /** * init a workers list * - pipes : communication pipes * - size : size of list - number of workers * - max : maximum number of jobs per worker * return : pointer to workers list or NULL on error */ xj_wlist xj_wlist_init(int **pipes, int size, int max, int cache_time, int sleep_time, int delay_time) { int i; xj_wlist jwl = NULL; if(pipes == NULL || size <= 0 || max <= 0) return NULL; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----START-----\n"); #endif jwl = (xj_wlist)_M_SHM_MALLOC(sizeof(t_xj_wlist)); if(jwl == NULL) return NULL; jwl->len = size; jwl->maxj = max; jwl->cachet = cache_time; jwl->delayt = delay_time; jwl->sleept = sleep_time; jwl->aliases = NULL; jwl->sems = NULL; i = 0; /* alloc locks*/ if((jwl->sems = lock_set_alloc(size)) == NULL){ LM_CRIT("failed to alloc lock set\n"); goto clean; }; /* init the locks*/ if (lock_set_init(jwl->sems)==0){ LM_CRIT("failed to initialize the locks\n"); goto clean; }; jwl->workers = (xj_worker)_M_SHM_MALLOC(size*sizeof(t_xj_worker)); if(jwl->workers == NULL){ lock_set_destroy(jwl->sems); goto clean; } for(i = 0; i < size; i++) { jwl->workers[i].nr = 0; jwl->workers[i].pid = 0; jwl->workers[i].wpipe = pipes[i][1]; jwl->workers[i].rpipe = pipes[i][0]; if((jwl->workers[i].sip_ids = newtree234(xj_jkey_cmp)) == NULL){ lock_set_destroy(jwl->sems); goto clean; } } return jwl; clean: LM_DBG("error occurred -> cleaning\n"); if(jwl->sems != NULL) lock_set_dealloc(jwl->sems); if(jwl->workers != NULL) { while(i>=0) { if(jwl->workers[i].sip_ids == NULL) free2tree234(jwl->workers[i].sip_ids, xj_jkey_free_p); i--; } _M_SHM_FREE(jwl->workers); } _M_SHM_FREE(jwl); return NULL; } /** * set the p.id's of the workers * - jwl : pointer to the workers list * - pids : p.id's array * - size : number of pids * return : 0 on success or <0 on error */ int xj_wlist_set_pid(xj_wlist jwl, int pid, int idx) { if(jwl == NULL || pid <= 0 || idx < 0 || idx >= jwl->len) return -1; lock_set_get(jwl->sems, idx); jwl->workers[idx].pid = pid; lock_set_release(jwl->sems, idx); return 0; } /** * free jab_wlist * - jwl : pointer to the workers list */ void xj_wlist_free(xj_wlist jwl) { int i; #ifdef XJ_EXTRA_DEBUG LM_DBG("freeing 'xj_wlist' memory ...\n"); #endif if(jwl == NULL) return; if(jwl->workers != NULL) { for(i=0; ilen; i++) free2tree234(jwl->workers[i].sip_ids, xj_jkey_free_p); _M_SHM_FREE(jwl->workers); } if(jwl->aliases != NULL) { if(jwl->aliases->d) _M_SHM_FREE(jwl->aliases->d); if(jwl->aliases->jdm != NULL) { _M_SHM_FREE(jwl->aliases->jdm->s); _M_SHM_FREE(jwl->aliases->jdm); } if(jwl->aliases->proxy != NULL) { _M_SHM_FREE(jwl->aliases->proxy->s); _M_SHM_FREE(jwl->aliases->proxy); } if(jwl->aliases->size > 0) { for(i=0; ialiases->size; i++) _M_SHM_FREE(jwl->aliases->a[i].s); _M_SHM_FREE(jwl->aliases->a); } _M_SHM_FREE(jwl->aliases); jwl->aliases = NULL; } if(jwl->sems != NULL){ lock_set_destroy(jwl->sems); lock_set_dealloc(jwl->sems); } _M_SHM_FREE(jwl); } /** * return communication pipe with the worker that will process the message for * the id 'sid' only if it exists, or -1 if error * - jwl : pointer to the workers list * - sid : id of the entity (connection to Jabber - usually SHOULD be FROM * header of the incoming SIP message) * - p : will point to the SHM location of the 'sid' in jwl */ int xj_wlist_check(xj_wlist jwl, xj_jkey jkey, xj_jkey *p) { int i; if(jwl==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; i = 0; *p = NULL; while(i < jwl->len) { lock_set_get(jwl->sems, i); if(jwl->workers[i].pid <= 0) { lock_set_release(jwl->sems, i); i++; continue; } if((*p = find234(jwl->workers[i].sip_ids, (void*)jkey, NULL)) != NULL) { lock_set_release(jwl->sems, i); #ifdef XJ_EXTRA_DEBUG LM_DBG("entry exists for <%.*s> in the" " pool of <%d> [%d]\n",jkey->id->len, jkey->id->s, jwl->workers[i].pid,i); #endif return jwl->workers[i].wpipe; } lock_set_release(jwl->sems, i); i++; } #ifdef XJ_EXTRA_DEBUG LM_DBG("entry does not exist for <%.*s>\n", jkey->id->len, jkey->id->s); #endif return -1; } /** * return communication pipe with the worker that will process the message for * the id 'sid', or -1 if error * - jwl : pointer to the workers list * - sid : id of the entity (connection to Jabber - usually SHOULD be FROM * header of the incoming SIP message) * - p : will point to the SHM location of the 'sid' in jwl */ int xj_wlist_get(xj_wlist jwl, xj_jkey jkey, xj_jkey *p) { int i = 0, pos = -1, min = 100000; xj_jkey msid = NULL; if(jwl==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; *p = NULL; while(i < jwl->len) { lock_set_get(jwl->sems, i); if(jwl->workers[i].pid <= 0) { lock_set_release(jwl->sems, i); i++; continue; } if((*p = find234(jwl->workers[i].sip_ids, (void*)jkey, NULL))!=NULL) { if(pos >= 0) lock_set_release(jwl->sems, pos); lock_set_release(jwl->sems, i); #ifdef XJ_EXTRA_DEBUG LM_DBG("entry already exists for <%.*s> in the" " pool of <%d> [%d]\n",jkey->id->len, jkey->id->s, jwl->workers[i].pid,i); #endif return jwl->workers[i].wpipe; } if(min > jwl->workers[i].nr) { if(pos >= 0) lock_set_release(jwl->sems, pos); pos = i; min = jwl->workers[i].nr; } else lock_set_release(jwl->sems, i); i++; } if(pos >= 0 && jwl->workers[pos].nr < jwl->maxj) { jwl->workers[pos].nr++; msid = (xj_jkey)_M_SHM_MALLOC(sizeof(t_xj_jkey)); if(msid == NULL) goto error; msid->id = (str*)_M_SHM_MALLOC(sizeof(str)); if(msid->id == NULL) { _M_SHM_FREE(msid); goto error; } msid->id->s = (char*)_M_SHM_MALLOC(jkey->id->len); if(msid->id == NULL) { _M_SHM_FREE(msid->id); _M_SHM_FREE(msid); goto error; } if((*p = add234(jwl->workers[pos].sip_ids, msid)) != NULL) { msid->id->len = jkey->id->len; memcpy(msid->id->s, jkey->id->s, jkey->id->len); msid->hash = jkey->hash; msid->flag = XJ_FLAG_OPEN; lock_set_release(jwl->sems, pos); #ifdef XJ_EXTRA_DEBUG LM_DBG("new entry for <%.*s> in the pool of" " <%d> - [%d]\n", jkey->id->len, jkey->id->s, jwl->workers[pos].pid, pos); #endif return jwl->workers[pos].wpipe; } _M_SHM_FREE(msid->id->s); _M_SHM_FREE(msid->id); _M_SHM_FREE(msid); } error: if(pos >= 0) lock_set_release(jwl->sems, pos); LM_DBG("cannot create a new entry for <%.*s>\n", jkey->id->len, jkey->id->s); return -1; } /** * set the flag of the connection identified by 'jkey' * */ int xj_wlist_set_flag(xj_wlist jwl, xj_jkey jkey, int fl) { int i; xj_jkey p = NULL; if(jwl==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("looking for <%.*s>" " having id=%d\n", jkey->id->len, jkey->id->s, jkey->hash); #endif i = 0; while(i < jwl->len) { lock_set_get(jwl->sems, i); if(jwl->workers[i].pid <= 0) { lock_set_release(jwl->sems, i); i++; continue; } if((p=find234(jwl->workers[i].sip_ids, (void*)jkey, NULL)) != NULL) { p->flag = fl; lock_set_release(jwl->sems, i); #ifdef XJ_EXTRA_DEBUG LM_DBG("the connection for <%.*s>" " marked with flag=%d", jkey->id->len, jkey->id->s, fl); #endif return jwl->workers[i].wpipe; } lock_set_release(jwl->sems, i); i++; } #ifdef XJ_EXTRA_DEBUG LM_DBG("entry does not exist for <%.*s>\n", jkey->id->len, jkey->id->s); #endif return -1; } /** * set IM aliases, jdomain and outbound proxy * * return 0 if OK */ int xj_wlist_set_aliases(xj_wlist jwl, char *als, char *jd, char *pa) { char *p, *p0, *p1; int i, n; if(jwl == NULL) return -1; if(!jd) // || !als || strlen(als)<2) return 0; if((jwl->aliases = (xj_jalias)_M_SHM_MALLOC(sizeof(t_xj_jalias)))==NULL) { LM_DBG("not enough SHMemory.\n"); return -1; } jwl->aliases->jdm = NULL; jwl->aliases->proxy = NULL; jwl->aliases->dlm = XJ_DEF_JDELIM; // default user part delimiter jwl->aliases->size = 0; jwl->aliases->a = NULL; jwl->aliases->d = NULL; // set the jdomain if(jd != NULL && (n=strlen(jd))>2) { p = jd; while(p < jd+n && *p!='=') p++; if(paliases->dlm = *(p+1); n = p - jd; } if((jwl->aliases->jdm = (str*)_M_SHM_MALLOC(sizeof(str)))== NULL) { LM_DBG("not enough SHMemory!?\n"); _M_SHM_FREE(jwl->aliases); jwl->aliases = NULL; return -1; } jwl->aliases->jdm->len = n; if((jwl->aliases->jdm->s=(char*)_M_SHM_MALLOC(jwl->aliases->jdm->len)) == NULL) { LM_DBG("not enough SHMemory!?!\n"); _M_SHM_FREE(jwl->aliases->jdm); _M_SHM_FREE(jwl->aliases); jwl->aliases = NULL; } strncpy(jwl->aliases->jdm->s, jd, jwl->aliases->jdm->len); #ifdef XJ_EXTRA_DEBUG LM_DBG("jdomain=%.*s delim=%c\n", jwl->aliases->jdm->len, jwl->aliases->jdm->s, jwl->aliases->dlm); #endif } // set the proxy address if(pa && strlen(pa)>0) { if((jwl->aliases->proxy = (str*)_M_SHM_MALLOC(sizeof(str)))==NULL) { LM_DBG(" not enough SHMemory!!\n"); goto clean3; } i = jwl->aliases->proxy->len = strlen(pa); // check if proxy address has sip: prefix if(i < 4 || pa[0]!='s' || pa[1]!='i' || pa[2]!='p' || pa[3]!=':') jwl->aliases->proxy->len += 4; if((jwl->aliases->proxy->s= (char*)_M_SHM_MALLOC(jwl->aliases->proxy->len)) == NULL) { LM_DBG("not enough SHMemory!!!\n"); _M_SHM_FREE(jwl->aliases->proxy); goto clean3; } p0 = jwl->aliases->proxy->s; if(jwl->aliases->proxy->len != i) { strncpy(p0, "sip:", 4); p0 += 4; } strncpy(p0, pa, i); #ifdef XJ_EXTRA_DEBUG LM_DBG("outbound proxy=[%.*s]\n", jwl->aliases->proxy->len, jwl->aliases->proxy->s); #endif } // set the IM aliases if(!als || strlen(als)<2) return 0; if((p = strchr(als, ';')) == NULL) { LM_DBG("bad parameter value\n"); return -1; } if((jwl->aliases->size = atoi(als)) <= 0) { LM_DBG("wrong number of aliases\n"); return 0; } jwl->aliases->d = (char*)_M_SHM_MALLOC(jwl->aliases->size*sizeof(char)); if(jwl->aliases->d == NULL) { LM_DBG("not enough SHMemory..\n"); goto clean2; } memset(jwl->aliases->d, 0, jwl->aliases->size); jwl->aliases->a = (str*)_M_SHM_MALLOC(jwl->aliases->size*sizeof(str)); if(jwl->aliases->a == NULL) { LM_DBG("not enough SHMemory..\n"); goto clean1; } p++; for(i=0; ialiases->size; i++) { if((p0 = strchr(p, ';'))==NULL) { LM_DBG("bad parameter value format\n"); goto clean; } n = p0 - p; p1 = strchr(p, '='); if(p1 && p1aliases->d[i] = *(p1+1); n = p1 - p; } jwl->aliases->a[i].len = n; if((jwl->aliases->a[i].s = (char*)_M_SHM_MALLOC(jwl->aliases->a[i].len)) == NULL) { LM_DBG("not enough SHMemory!\n"); goto clean; } strncpy(jwl->aliases->a[i].s, p, jwl->aliases->a[i].len); #ifdef XJ_EXTRA_DEBUG LM_DBG("alias[%d/%d]=%.*s delim=%c\n", i+1, jwl->aliases->size, jwl->aliases->a[i].len, jwl->aliases->a[i].s, jwl->aliases->d[i]?jwl->aliases->d[i]:'X'); #endif p = p0 + 1; } return 0; clean: while(i>0) { _M_SHM_FREE(jwl->aliases->a[i-1].s); i--; } _M_SHM_FREE(jwl->aliases->a); clean1: if(jwl->aliases->d) _M_SHM_FREE(jwl->aliases->d); clean2: if(jwl->aliases->proxy) { _M_SHM_FREE(jwl->aliases->proxy->s); _M_SHM_FREE(jwl->aliases->proxy); } clean3: if(jwl->aliases->jdm) { _M_SHM_FREE(jwl->aliases->jdm->s); _M_SHM_FREE(jwl->aliases->jdm); } _M_SHM_FREE(jwl->aliases); jwl->aliases = NULL; return -1; } /** * check if the addr contains jdomain or an alias * - jwl : pointer to the workers list * - addr: the address to check against jdomain and aliases * returns 0 - if contains or !=0 if not */ int xj_wlist_check_aliases(xj_wlist jwl, str *addr) { char *p, *p0; int ll, i; if(!jwl || !jwl->aliases || !addr || !addr->s || addr->len<=0) return -1; // find '@' p = addr->s; while(p < addr->s + addr->len && *p != '@') p++; if(p >= addr->s + addr->len) return -1; p++; ll = addr->s + addr->len - p; // check parameters p0 = p; while(p0 < p + ll && *p0 != ';') p0++; if(p0 < p + ll) ll = p0 - p; ll = addr->s + addr->len - p; if(jwl->aliases->jdm && jwl->aliases->jdm->len == ll && !strncasecmp(jwl->aliases->jdm->s, p, ll)) return 0; if(jwl->aliases->size <= 0) return 1; for(i = 0; i < jwl->aliases->size; i++) if(jwl->aliases->a[i].len == ll && !strncasecmp(p, jwl->aliases->a[i].s, ll)) return 0; return 1; } /** * delete an entity from working list of a worker * - jwl : pointer to the workers list * - sid : id of the entity (connection to Jabber - usually SHOULD be FROM * header of the incoming SIP message * - _pid : process id of the worker */ void xj_wlist_del(xj_wlist jwl, xj_jkey jkey, int _pid) { int i; void *p; if(jwl==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return; for(i=0; i < jwl->len; i++) if(jwl->workers[i].pid == _pid) break; if(i >= jwl->len) { LM_DBG("%d: key <%.*s> not found in [%d]...\n", _pid, jkey->id->len, jkey->id->s, i); return; } #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: trying to delete entry for <%.*s>...\n", _pid, jkey->id->len, jkey->id->s); #endif lock_set_get(jwl->sems, i); p = del234(jwl->workers[i].sip_ids, (void*)jkey); if(p != NULL) { jwl->workers[i].nr--; #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: sip id <%.*s> deleted\n", _pid, jkey->id->len, jkey->id->s); #endif xj_jkey_free_p(p); } lock_set_release(jwl->sems, i); } kamailio-4.0.4/obsolete/jabber_k/xjab_load.h0000644000000000000000000000260412223032460017446 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _XJAB_LOAD_H_ #define _XJAB_LOAD_H_ #include "../../sr_module.h" #include "../../str.h" #include "xjab_base.h" #define XJ_NO_SCRIPT_F 1 typedef int (*pa_register_watcher_f)(str*, str *, void*, void*); typedef int (*pa_unregister_watcher_f)(str*, str *, void*, void*); struct xjab_binds { pa_register_watcher_f register_watcher; pa_unregister_watcher_f unregister_watcher; }; typedef int(*load_xjab_f)(struct xjab_binds*); int load_xjab(struct xjab_binds*); void xj_register_watcher(str*, str *, void*, void*); void xj_unregister_watcher(str*, str *, void*, void*); #endif kamailio-4.0.4/obsolete/jabber_k/xjab_load.c0000644000000000000000000000237112223032460017442 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "xjab_load.h" int load_xjab(struct xjab_binds *xjb) { if(!( xjb->register_watcher=(pa_register_watcher_f) find_export("jab_register_watcher", XJ_NO_SCRIPT_F, 0)) ) { LM_ERR("'jab_register_watcher' not found!\n"); return -1; } if(!( xjb->unregister_watcher=(pa_unregister_watcher_f) find_export("jab_unregister_watcher", XJ_NO_SCRIPT_F, 0)) ) { LM_ERR("'jab_unregister_watcher' not found!\n"); return -1; } return 1; } kamailio-4.0.4/obsolete/jabber_k/xjab_worker.c0000644000000000000000000010644512223032460020043 0ustar rootroot/* * $Id$ * * eXtended JABber module - worker implementation * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History * ------- * 2003-01-20 xj_worker_precess function cleaning - some part of it moved to * xj_worker_check_jcons function, (dcm) * 2003-02-28 send NOTIFYs even the connection is closed by user, (dcm) * 2003-03-11 major locking changes - uses locking.h, (andrei) * 2003-05-07 added new presence status - 'terminated' - when connection * with Jabber server is lost or closed, (dcm) * 2003-05-09 added new presence status - 'refused' - when the presence * subscription request is refused by target, (dcm) * 2003-05-09 new xj_worker_precess function cleaning - some part of it moved * to xj_worker_check_qmsg and xj_worker_check_watcher functions, * (dcm) * 2004-06-07 new DB api => xj_worker_process takes another parameter: dbf * (andrei) */ #include #include #include #include #include #include #include #include #include "../../dprint.h" #include "../../timer.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../modules/tm/tm_load.h" #include "xjab_worker.h" #include "xjab_util.h" #include "xjab_jcon.h" #include "xjab_dmsg.h" #include "xode.h" #include "xjab_presence.h" #include "mdefines.h" #define XJAB_RESOURCE "serXjab" #define XJ_ADDRTR_NUL 0 #define XJ_ADDRTR_S2J 1 #define XJ_ADDRTR_J2S 2 #define XJ_ADDRTR_CON 4 #define XJ_MSG_POOL_SIZE 10 // proxy address #define _PADDR(a) ((a)->aliases->proxy) /** TM bind */ extern struct tm_binds tmb; /** debug info */ int _xj_pid = 0; int main_loop = 1; /** **/ extern char *registrar; static str jab_gw_name = {"jabber_gateway@127.0.0.1", 24}; /** * address correction * alias A~B: flag == 0 => A->B, otherwise B->A */ int xj_address_translation(str *src, str *dst, xj_jalias als, int flag) { char *p, *p0; int i, ll; if(!src || !dst || !src->s || !dst->s ) return -1; if(!als || !als->jdm || !als->jdm->s || als->jdm->len <= 0) goto done; dst->len = 0; #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: - checking aliases\n", _xj_pid); #endif p = src->s; while(p<(src->s + src->len) && *p != '@') p++; if(*p != '@') goto done; p++; ll = src->s + src->len - p; #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: - domain is [%.*s]\n",_xj_pid,ll,p); #endif /*** checking aliases */ if(als->size > 0) { for(i=0; isize; i++) if(als->a[i].len == ll && !strncasecmp(p, als->a[i].s, als->a[i].len)) { if(als->d[i]) { if(flag & XJ_ADDRTR_S2J) { strncpy(dst->s, src->s, src->len); p0 = dst->s; while(p0 < dst->s + (p-src->s)) { if(*p0 == als->dlm) *p0 = als->d[i]; p0++; } return 0; } if(flag & XJ_ADDRTR_J2S) { strncpy(dst->s, src->s, src->len); p0 = dst->s; while(p0 < dst->s + (p-src->s)) { if(*p0 == als->d[i]) *p0 = als->dlm; p0++; } return 0; } } goto done; } } #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: - doing address correction\n", _xj_pid); #endif if(flag & XJ_ADDRTR_S2J) { if(als->jdm->len != ll || strncasecmp(p, als->jdm->s, als->jdm->len)) { LM_DBG("%d: - wrong Jabber" " destination <%.*s>!\n", _xj_pid, src->len, src->s); return -1; } if(flag & XJ_ADDRTR_CON) { #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: - that is for" " Jabber conference\n", _xj_pid); #endif p0 = p-1; while(p0 > src->s && *p0 != als->dlm) p0--; if(p0 <= src->s) return -1; p0--; while(p0 > src->s && *p0 != als->dlm) p0--; if(*p0 != als->dlm) return -1; dst->len = p - p0 - 2; strncpy(dst->s, p0+1, dst->len); dst->s[dst->len]=0; p = dst->s; while(p < (dst->s + dst->len) && *p!=als->dlm) p++; if(*p==als->dlm) *p = '@'; return 0; } #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: - that is for Jabber network\n", _xj_pid); #endif dst->len = p - src->s - 1; strncpy(dst->s, src->s, dst->len); dst->s[dst->len]=0; if((p = strchr(dst->s, als->dlm)) != NULL) *p = '@'; else { LM_DBG("%d: - wrong Jabber" " destination <%.*s>!!!\n", _xj_pid, src->len, src->s); return -1; } return 0; } if(flag & XJ_ADDRTR_J2S) { *(p-1) = als->dlm; p0 = src->s + src->len; while(p0 > p) { if(*p0 == '/') { src->len = p0 - src->s; *p0 = 0; } p0--; } strncpy(dst->s, src->s, src->len); dst->s[src->len] = '@'; dst->s[src->len+1] = 0; strncat(dst->s, als->jdm->s, als->jdm->len); dst->len = strlen(dst->s); return 0; } done: dst->s = src->s; dst->len = src->len; return 0; } /** * worker implementation * - jwl : pointer to the workers list * - jaddress : address of the jabber server * - jport : port of the jabber server * - rank : worker's rank * - db_con : connection to database * - priority: jabber's priority * dbf: database module callbacks structure * return : 0 on success or <0 on error */ int xj_worker_process(xj_wlist jwl, char* jaddress, int jport, char* priority, int rank, db1_con_t* db_con, db_func_t* dbf) { int pipe, ret, i, pos, maxfd, flag; xj_jcon_pool jcp; struct timeval tmv; fd_set set, mset; xj_sipmsg jsmsg; str sto; xj_jcon jbc = NULL; xj_jconf jcf = NULL; char *p, buff[1024], recv_buff[4096]; int flags, nr, ltime = 0; static str tmp1 = str_init("sip_id"); static str tmp2 = str_init("type"); static str tmp3 = str_init("jab_id"); static str tmp4 = str_init("jab_passwd"); db_key_t keys[] = {&tmp1, &tmp2}; db_val_t vals[2]; db_key_t col[] = {&tmp3, &tmp4}; db1_res_t* res = NULL; vals[0].type=DB1_STRING; vals[0].nul=0; vals[0].val.string_val=buff; vals[1].type=DB1_INT; vals[1].nul=0; vals[1].val.int_val=0; _xj_pid = getpid(); //signal(SIGTERM, xj_sig_handler); //signal(SIGINT, xj_sig_handler); //signal(SIGQUIT, xj_sig_handler); signal(SIGSEGV, xj_sig_handler); if(registrar) { jab_gw_name.s = registrar; jab_gw_name.len = strlen(registrar); if(registrar[0]== 's' && registrar[1]== 'i' && registrar[2]== 'p' && registrar[3]== ':') { jab_gw_name.s += 4; jab_gw_name.len -= 4; } } if(!jwl || !jwl->aliases || !jwl->aliases->jdm || !jaddress || rank >= jwl->len) { LM_DBG("[%d]:%d: exiting - wrong parameters\n", rank, _xj_pid); return -1; } pipe = jwl->workers[rank].rpipe; LM_DBG("[%d]:%d: started - pipe=<%d> : 1st message delay" " <%d>\n", rank, _xj_pid, pipe, jwl->delayt); if((jcp=xj_jcon_pool_init(jwl->maxj,XJ_MSG_POOL_SIZE,jwl->delayt))==NULL) { LM_DBG("cannot allocate the pool\n"); return -1; } maxfd = pipe; tmv.tv_sec = jwl->sleept; tmv.tv_usec = 0; FD_ZERO(&set); FD_SET(pipe, &set); while(main_loop) { mset = set; tmv.tv_sec = (jcp->jmqueue.size == 0)?jwl->sleept:1; #ifdef XJ_EXTRA_DEBUG //LM_DBG("XJAB:xj_worker[%d]:%d: select waiting %ds - queue=%d\n",rank, // _xj_pid, (int)tmv.tv_sec, jcp->jmqueue.size); #endif tmv.tv_usec = 0; ret = select(maxfd+1, &mset, NULL, NULL, &tmv); // check the msg queue xj_worker_check_qmsg(jwl, jcp); if(ret <= 0) goto step_x; #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: something is coming\n", _xj_pid); #endif if(!FD_ISSET(pipe, &mset)) goto step_y; if(read(pipe, &jsmsg, sizeof(jsmsg)) < (int)sizeof(jsmsg)) { LM_DBG("%d: BROKEN PIPE - exiting\n", _xj_pid); break; } #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: job <%p> from SER\n", _xj_pid, jsmsg); #endif if(jsmsg == NULL || jsmsg->jkey==NULL || jsmsg->jkey->id==NULL) goto step_w; strncpy(buff, jsmsg->jkey->id->s, jsmsg->jkey->id->len); buff[jsmsg->jkey->id->len] = 0; jbc = xj_jcon_pool_get(jcp, jsmsg->jkey); switch(jsmsg->type) { case XJ_SEND_MESSAGE: if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm) && (!jbc||!xj_jcon_get_jconf(jbc,&jsmsg->to,jwl->aliases->dlm))) { xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOTJCONF, NULL); goto step_w; } break; case XJ_REG_WATCHER: case XJ_JOIN_JCONF: case XJ_GO_ONLINE: break; case XJ_EXIT_JCONF: if(jbc == NULL) goto step_w; // close the conference session here if(jbc->nrjconf <= 0) goto step_w; if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm)) xj_jcon_del_jconf(jbc, &jsmsg->to, jwl->aliases->dlm, XJ_JCMD_UNSUBSCRIBE); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_INF_JCONFEXIT, NULL); goto step_w; case XJ_GO_OFFLINE: if(jbc != NULL) jbc->expire = ltime = -1; goto step_w; case XJ_DEL_WATCHER: default: goto step_w; } if(jbc != NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: connection already exists" " for <%s> ...\n", _xj_pid, buff); #endif xj_jcon_update(jbc, jwl->cachet); goto step_z; } // NO OPEN CONNECTION FOR THIS SIP ID #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: new connection for <%s>.\n", _xj_pid, buff); #endif if(dbf->query(db_con, keys, 0, vals, col, 2, 2, NULL, &res) != 0 || RES_ROW_N(res) <= 0) { #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: no database result when looking" " for associated Jabber account\n", _xj_pid); #endif xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_JGWFORB, NULL); goto step_v; } jbc = xj_jcon_init(jaddress, jport); if(xj_jcon_connect(jbc)) { LM_DBG("%d: Cannot connect" " to the Jabber server ...\n", _xj_pid); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOJSRV, NULL); goto step_v; } #ifdef XJ_EXTRA_DEBUG LM_DBG("auth to jabber as: [%s] / [xxx]\n", (char*)(ROW_VALUES(RES_ROWS(res))[0].val.string_val)); // (char*)(ROW_VALUES(RES_ROWS(res))[1].val.string_val)); #endif if(xj_jcon_user_auth(jbc, (char*)(ROW_VALUES(RES_ROWS(res))[0].val.string_val), (char*)(ROW_VALUES(RES_ROWS(res))[1].val.string_val), XJAB_RESOURCE) < 0) { LM_DBG("athentication to the Jabber server failed ...\n"); xj_jcon_disconnect(jbc); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_JAUTH, NULL); xj_jcon_free(jbc); goto step_v; } if(xj_jcon_set_attrs(jbc, jsmsg->jkey, jwl->cachet, jwl->delayt) || xj_jcon_pool_add(jcp, jbc)) { LM_DBG("keeping connection to Jabber server" " failed! Not enough memory ...\n"); xj_jcon_disconnect(jbc); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_JGWFULL, NULL); xj_jcon_free(jbc); goto step_v; } /** add socket descriptor to select */ #ifdef XJ_EXTRA_DEBUG LM_DBG("add connection on <%d> \n",jbc->sock); #endif if(jbc->sock > maxfd) maxfd = jbc->sock; FD_SET(jbc->sock, &set); xj_jcon_get_roster(jbc); xj_jcon_send_presence(jbc, NULL, NULL, "Online", priority); /** wait for a while - the worker is tired */ //sleep(3); if ((res != NULL) && (dbf->free_result(db_con,res) < 0)) { LM_DBG("failed to free SQL result - worker terminated\n"); return -1; } else res = NULL; step_z: if(jsmsg->type == XJ_GO_ONLINE) goto step_w; if(jsmsg->type == XJ_REG_WATCHER) { // update or register a presence watcher xj_worker_check_watcher(jwl, jcp, jbc, jsmsg); goto step_w; } flag = 0; if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm)) { if((jcf = xj_jcon_get_jconf(jbc, &jsmsg->to, jwl->aliases->dlm)) != NULL) { if((jsmsg->type == XJ_JOIN_JCONF) && !(jcf->status & XJ_JCONF_READY || jcf->status & XJ_JCONF_WAITING)) { if(!xj_jcon_jconf_presence(jbc,jcf,NULL,"online")) jcf->status = XJ_JCONF_WAITING; else { // unable to join the conference // --- send back to SIP user a msg xj_send_sip_msgz(_PADDR(jwl),jsmsg->jkey->id,&jsmsg->to, XJ_DMSG_ERR_JOINJCONF, &jbc->jkey->flag); goto step_w; } } flag |= XJ_ADDRTR_CON; } else { // unable to get the conference // --- send back to SIP user a msg xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NEWJCONF, &jbc->jkey->flag); goto step_w; } } if(jsmsg->type != XJ_SEND_MESSAGE) goto step_w; // here will come only XJ_SEND_MESSAGE switch(xj_jcon_is_ready(jbc,jsmsg->to.s,jsmsg->to.len,jwl->aliases->dlm)) { case 0: #ifdef XJ_EXTRA_DEBUG LM_DBG("sending the message to Jabber network ...\n"); #endif /*** address correction ***/ sto.s = buff; sto.len = 0; flag |= XJ_ADDRTR_S2J; if(xj_address_translation(&jsmsg->to, &sto, jwl->aliases, flag) == 0) { if(xj_jcon_send_msg(jbc, sto.s, sto.len, jsmsg->msg.s, jsmsg->msg.len, (flag&XJ_ADDRTR_CON)?XJ_JMSG_GROUPCHAT:XJ_JMSG_CHAT)<0) xj_send_sip_msgz(_PADDR(jwl),jsmsg->jkey->id,&jsmsg->to, XJ_DMSG_ERR_SENDJMSG, &jbc->jkey->flag); } else LM_ERR("sending as Jabber message ...\n"); goto step_w; case 1: #ifdef XJ_EXTRA_DEBUG LM_DBG("scheduling the message.\n"); #endif if(xj_jcon_pool_add_jmsg(jcp, jsmsg, jbc) < 0) { LM_DBG("scheduling the message FAILED." "Message was dropped.\n"); xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_STOREJMSG, &jbc->jkey->flag); goto step_w; } else // skip freeing the SIP message - now is in queue goto step_y; case 2: xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOREGIM, &jbc->jkey->flag); goto step_w; case 3: // not joined to Jabber conference xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_NOTJCONF, &jbc->jkey->flag); goto step_w; default: xj_send_sip_msgz(_PADDR(jwl), jsmsg->jkey->id, &jsmsg->to, XJ_DMSG_ERR_SENDJMSG, &jbc->jkey->flag); goto step_w; } step_v: // error connecting to Jabber server // cleaning jab_wlist xj_wlist_del(jwl, jsmsg->jkey, _xj_pid); // cleaning db_query if ((res != NULL) && (dbf->free_result(db_con,res) < 0)) { LM_DBG("failed to free the SQL result - worker terminated\n"); return -1; } else res = NULL; step_w: if(jsmsg!=NULL) { xj_sipmsg_free(jsmsg); jsmsg = NULL; } step_y: // check for new message from ... JABBER for(i = 0; i < jcp->len && main_loop; i++) { if(jcp->ojc[i] == NULL) continue; #ifdef XJ_EXTRA_DEBUG LM_DBG("checking socket <%d> ...\n", jcp->ojc[i]->sock); #endif if(!FD_ISSET(jcp->ojc[i]->sock, &mset)) continue; pos = nr = 0; do { p = recv_buff; if(pos != 0) { while(pos < nr) { *p = recv_buff[pos]; pos++; p++; } *p = 0; /** * flush out the socket - set it to nonblocking */ flags = fcntl(jcp->ojc[i]->sock, F_GETFL, 0); if(flags!=-1 && !(flags & O_NONBLOCK)) { /* set NONBLOCK bit to enable non-blocking */ fcntl(jcp->ojc[i]->sock, F_SETFL, flags|O_NONBLOCK); } } if((nr = read(jcp->ojc[i]->sock, p, sizeof(recv_buff)-(p-recv_buff))) == 0 ||(nr < 0 && errno != EAGAIN)) { LM_DBG("connection to jabber lost on socket <%d> ...\n", jcp->ojc[i]->sock); xj_send_sip_msgz(_PADDR(jwl), jcp->ojc[i]->jkey->id, &jab_gw_name,XJ_DMSG_ERR_DISCONNECTED,&jbc->jkey->flag); // make sure that will ckeck expired connections ltime = jcp->ojc[i]->expire = -1; FD_CLR(jcp->ojc[i]->sock, &set); goto step_xx; } #ifdef XJ_EXTRA_DEBUG LM_DBG("received: %dbytes Err:%d/EA:%d\n", nr, errno, EAGAIN); #endif xj_jcon_update(jcp->ojc[i], jwl->cachet); if(nr>0) p[nr] = 0; nr = strlen(recv_buff); pos = 0; #ifdef XJ_EXTRA_DEBUG LM_DBG("JMSG START ----------\n%.*s\n" " JABBER: JMSGL:%d END ----------\n", nr, recv_buff, nr); #endif } while(xj_manage_jab(recv_buff, nr, &pos, jwl->aliases, jcp->ojc[i]) == 9 && main_loop); /** * flush out the socket - set it back to blocking */ flags = fcntl(jcp->ojc[i]->sock, F_GETFL, 0); if(flags!=-1 && (flags & O_NONBLOCK)) { /* reset NONBLOCK bit to enable blocking */ fcntl(jcp->ojc[i]->sock, F_SETFL, flags & ~O_NONBLOCK); } #ifdef XJ_EXTRA_DEBUG LM_DBG("msgs from socket <%d> parsed ...\n", jcp->ojc[i]->sock); #endif } // end FOR(i = 0; i < jcp->len; i++) step_x: if(ret < 0) { LM_DBG("signal received!!!!!!!!\n"); maxfd = pipe; FD_ZERO(&set); FD_SET(pipe, &set); for(i = 0; i < jcp->len; i++) { if(jcp->ojc[i] != NULL) { FD_SET(jcp->ojc[i]->sock, &set); if( jcp->ojc[i]->sock > maxfd ) maxfd = jcp->ojc[i]->sock; } } } step_xx: if(ltime < 0 || ltime + jwl->sleept <= get_ticks()) { ltime = get_ticks(); #ifdef XJ_EXTRA_DEBUG //LM_DBG("XJAB:xj_worker:%d: scanning for expired connection\n", // _xj_pid); #endif xj_worker_check_jcons(jwl, jcp, ltime, &set); } } // END while LM_DBG("cleaning procedure\n"); return 0; } // end xj_worker_process /** * parse incoming message from Jabber server */ int xj_manage_jab(char *buf, int len, int *pos, xj_jalias als, xj_jcon jbc) { int j, err=0; char *p, *to, *from, *msg, *type, *emsg, *ecode, lbuf[4096], fbuf[128]; xj_jconf jcf = NULL; str ts, tf; xode x, y, z; str *sid; xj_pres_cell prc = NULL; if(!jbc) return -1; sid = jbc->jkey->id; x = xode_from_strx(buf, len, &err, &j); #ifdef XJ_EXTRA_DEBUG LM_DBG("xode ret:%d pos:%d\n", err, j); #endif if(err && pos != NULL) *pos= j; if(x == NULL) return -1; lbuf[0] = 0; ecode = NULL; /******************** XMPP 'MESSAGE' HANDLING **********************/ if(!strncasecmp(xode_get_name(x), "message", 7)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("jabber [message] received\n"); #endif if((to = xode_get_attrib(x, "to")) == NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("missing 'to' attribute\n"); #endif err = -1; goto ready; } if((from = xode_get_attrib(x, "from")) == NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("missing 'from' attribute\n"); #endif err = -1; goto ready; } if((y = xode_get_tag(x, "body")) == NULL || (msg = xode_get_data(y)) == NULL) { #ifdef XJ_EXTRA_DEBUG LM_DBG("missing 'body' of message\n"); #endif err = -1; goto ready; } type = xode_get_attrib(x, "type"); if(type != NULL && !strncasecmp(type, "error", 5)) { if((y = xode_get_tag(x, "error")) == NULL || (emsg = xode_get_data(y)) == NULL) strcpy(lbuf, "{Error sending following message} - "); else { ecode = xode_get_attrib(y, "code"); strcpy(lbuf, "{Error ("); if(ecode != NULL) { strcat(lbuf, ecode); strcat(lbuf, " - "); } strcat(lbuf, emsg); strcat(lbuf, ") when trying to send following message}"); } } // is from a conference?!?! if((jcf=xj_jcon_check_jconf(jbc, from))!=NULL) { if(lbuf[0] == 0) { p = from + strlen(from); while(p>from && *p != '/') p--; if(*p == '/') { if(jcf->nick.len>0 && strlen(p+1) == jcf->nick.len && !strncasecmp(p+1, jcf->nick.s, jcf->nick.len)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("message sent by myself\n"); #endif goto ready; } lbuf[0] = '['; lbuf[1] = 0; strcat(lbuf, p+1); strcat(lbuf, "] "); } } else { jcf->status = XJ_JCONF_NULL; xj_jcon_jconf_presence(jbc,jcf,NULL,"online"); } strcat(lbuf, msg); ts.s = lbuf; ts.len = strlen(lbuf); if(xj_send_sip_msg(als->proxy, sid, &jcf->uri, &ts, &jbc->jkey->flag)<0) LM_ERR("sip message was not sent!\n"); #ifdef XJ_EXTRA_DEBUG else LM_DBG("sip message was sent!\n"); #endif goto ready; } strcat(lbuf, msg); ts.s = from; ts.len = strlen(from); tf.s = fbuf; tf.len = 0; if(xj_address_translation(&ts, &tf, als, XJ_ADDRTR_J2S) == 0) { ts.s = lbuf; ts.len = strlen(lbuf); if(xj_send_sip_msg(als->proxy, sid, &tf, &ts, &jbc->jkey->flag)<0) LM_ERR("sip message was not sent!\n"); #ifdef XJ_EXTRA_DEBUG else LM_DBG("sip message was sent!\n"); #endif } goto ready; } /*------------------- END 'MESSAGE' HANDLING ----------------------*/ /******************** XMPP 'PRESENCE' HANDLING *********************/ if(!strncasecmp(xode_get_name(x), "presence", 8)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("jabber [presence] received\n"); #endif type = xode_get_attrib(x, "type"); from = xode_get_attrib(x, "from"); if(from == NULL) goto ready; ts.s = from; p = from; while(pready |= XJ_NET_AIM; #ifdef XJ_EXTRA_DEBUG LM_DBG("AIM network ready\n"); #endif } else if(!strncasecmp(from, XJ_ICQ_NAME, XJ_ICQ_LEN)) { jbc->ready |= XJ_NET_ICQ; #ifdef XJ_EXTRA_DEBUG LM_DBG("ICQ network ready\n"); #endif } else if(!strncasecmp(from, XJ_MSN_NAME, XJ_MSN_LEN)) { jbc->ready |= XJ_NET_MSN; #ifdef XJ_EXTRA_DEBUG LM_DBG("MSN network ready\n"); #endif } else if(!strncasecmp(from, XJ_YAH_NAME, XJ_YAH_LEN)) { jbc->ready |= XJ_NET_YAH; #ifdef XJ_EXTRA_DEBUG LM_DBG("YAHOO network ready\n"); #endif } } else if((jcf=xj_jcon_check_jconf(jbc, from))!=NULL) { jcf->status = XJ_JCONF_READY; #ifdef XJ_EXTRA_DEBUG LM_DBG(" %s conference ready\n", from); #endif } else { #ifdef XJ_EXTRA_DEBUG LM_DBG("user <%.*s> is online\n",ts.len,ts.s); #endif prc = xj_pres_list_check(jbc->plist, &ts); if(prc) { if(prc->state != XJ_PS_ONLINE) { prc->state = XJ_PS_ONLINE; goto call_pa_cbf; } } else { #ifdef XJ_EXTRA_DEBUG LM_DBG("user state received - creating" " presence cell for [%.*s]\n", ts.len, ts.s); #endif prc = xj_pres_cell_new(); if(prc == NULL) { LM_DBG("cannot create presence" " cell for [%s]\n", from); goto ready; } if(xj_pres_cell_init(prc, &ts, NULL, NULL)<0) { LM_DBG("cannot init presence" " cell for [%s]\n", from); xj_pres_cell_free(prc); goto ready; } prc = xj_pres_list_add(jbc->plist, prc); if(prc) { prc->state = XJ_PS_ONLINE; goto call_pa_cbf; } } } goto ready; } if(strchr(from, '@') == NULL) goto ready; if(!strncasecmp(type, "error", 5)) { if((jcf=xj_jcon_check_jconf(jbc, from))!=NULL) { tf.s = from; tf.len = strlen(from); if((y = xode_get_tag(x, "error")) == NULL) goto ready; if ((p = xode_get_attrib(y, "code")) != NULL && atoi(p) == 409) { xj_send_sip_msgz(als->proxy, sid, &tf, XJ_DMSG_ERR_JCONFNICK, &jbc->jkey->flag); goto ready; } xj_send_sip_msgz(als->proxy,sid,&tf,XJ_DMSG_ERR_JCONFREFUSED, &jbc->jkey->flag); } goto ready; } if(type!=NULL && !strncasecmp(type, "subscribe", 9)) { xj_jcon_send_presence(jbc, from, "subscribed", NULL, NULL); goto ready; } prc = xj_pres_list_check(jbc->plist, &ts); if(!prc) goto ready; if(!strncasecmp(type, "unavailable", 11)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("user <%s> is offline\n", from); #endif if(prc->state != XJ_PS_OFFLINE) { prc->state = XJ_PS_OFFLINE; goto call_pa_cbf; } goto ready; } if(!strncasecmp(type, "unsubscribed", 12)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("user <%s> does not allow to see his" " presence status\n", from); #endif if(prc->state != XJ_PS_REFUSED) { prc->state = XJ_PS_REFUSED; goto call_pa_cbf; } } // ignoring unknown types goto ready; } /*------------------- END XMPP 'PRESENCE' HANDLING ----------------*/ /******************** XMPP 'IQ' HANDLING ***************************/ if(!strncasecmp(xode_get_name(x), "iq", 2)) { #ifdef XJ_EXTRA_DEBUG LM_DBG("jabber [iq] received\n"); #endif if(!strncasecmp(xode_get_attrib(x, "type"), "result", 6)) { if((y = xode_get_tag(x, "query?xmlns=jabber:iq:roster")) == NULL) goto ready; z = xode_get_firstchild(y); while(z) { if(!strncasecmp(xode_get_name(z), "item", 5) && (from = xode_get_attrib(z, "jid")) != NULL) { if(strchr(from, '@') == NULL) { // transports if(!strncasecmp(from, XJ_AIM_NAME, XJ_AIM_LEN)) { jbc->allowed |= XJ_NET_AIM; #ifdef XJ_EXTRA_DEBUG LM_DBG("AIM network available\n"); #endif } else if(!strncasecmp(from, XJ_ICQ_NAME, XJ_ICQ_LEN)) { jbc->allowed |= XJ_NET_ICQ; #ifdef XJ_EXTRA_DEBUG LM_DBG("ICQ network available\n"); #endif } else if(!strncasecmp(from, XJ_MSN_NAME, XJ_MSN_LEN)) { jbc->allowed |= XJ_NET_MSN; #ifdef XJ_EXTRA_DEBUG LM_DBG("MSN network available\n"); #endif } else if(!strncasecmp(from, XJ_YAH_NAME, XJ_YAH_LEN)) { jbc->allowed |= XJ_NET_YAH; #ifdef XJ_EXTRA_DEBUG LM_DBG("YAHOO network available\n"); #endif } goto next_sibling; } } next_sibling: z = xode_get_nextsibling(z); } } goto ready; } /*------------------- END XMPP 'IQ' HANDLING ----------------------*/ call_pa_cbf: if(prc && prc->cbf) { // call the PA callback function tf.s = fbuf; tf.len = 0; if(xj_address_translation(&ts,&tf,als,XJ_ADDRTR_J2S)==0) { #ifdef XJ_EXTRA_DEBUG LM_DBG("calling CBF(%.*s,%d)\n", tf.len, tf.s, prc->state); #endif (*(prc->cbf))(&tf, &tf, prc->state, prc->cbp); } } ready: xode_free(x); return err; } /** * */ void xj_sig_handler(int s) { //signal(SIGTERM, xj_sig_handler); //signal(SIGINT, xj_sig_handler); //signal(SIGQUIT, xj_sig_handler); signal(SIGSEGV, xj_sig_handler); main_loop = 0; LM_DBG("%d: SIGNAL received=%d\n **************", _xj_pid, s); } /***************************** ****************************************/ /** * send a SIP MESSAGE message * - to : destination * - from : origin * - contact : contact header * - msg : body of the message * return : 0 on success or <0 on error */ int xj_send_sip_msg(str *proxy, str *to, str *from, str *msg, int *cbp) { str msg_type = { "MESSAGE", 7}; char buf[512]; str tfrom; str str_hdr; char buf1[1024]; uac_req_t uac_r; if( !to || !to->s || to->len <= 0 || !from || !from->s || from->len <= 0 || !msg || !msg->s || msg->len <= 0 || (cbp && *cbp!=0) ) return -1; // from correction tfrom.len = 0; strncpy(buf+tfrom.len, "s, from->len); tfrom.len += from->len; buf[tfrom.len++] = '>'; tfrom.s = buf; // building Contact and Content-Type strcpy(buf1,"Content-Type: text/plain"CRLF"Contact: "); str_hdr.len = 24 + CRLF_LEN + 9; strncat(buf1,tfrom.s,tfrom.len); str_hdr.len += tfrom.len; strcat(buf1, CRLF); str_hdr.len += CRLF_LEN; str_hdr.s = buf1; if(cbp) { #ifdef XJ_EXTRA_DEBUG LM_DBG("uac callback parameter [%p==%d]\n", cbp, *cbp); #endif set_uac_req(&uac_r, &msg_type, &str_hdr, msg, 0, TMCB_LOCAL_COMPLETED, xj_tuac_callback, (void*)cbp); } else { set_uac_req(&uac_r, &msg_type, &str_hdr, msg, 0, 0, 0, 0); } return tmb.t_request(&uac_r, 0, to, &tfrom, 0); } /** * send a SIP MESSAGE message * - to : destination * - from : origin * - contact : contact header * - msg : body of the message, string terminated by zero * return : 0 on success or <0 on error */ int xj_send_sip_msgz(str *proxy, str *to, str *from, char *msg, int *cbp) { str tstr; int n; if(!to || !from || !msg || (cbp && *cbp!=0)) return -1; tstr.s = msg; tstr.len = strlen(msg); if((n = xj_send_sip_msg(proxy, to, from, &tstr, cbp)) < 0) LM_ERR("sip message wasn't sent to [%.*s]...\n", to->len, to->s); #ifdef XJ_EXTRA_DEBUG else LM_DBG("sip message was sent to [%.*s]...\n", to->len, to->s); #endif return n; } /** * send disconnected info to all SIP users associated with worker idx * and clean the entries from wlist */ int xj_wlist_clean_jobs(xj_wlist jwl, int idx, int fl) { xj_jkey p; if(jwl==NULL || idx < 0 || idx >= jwl->len || !jwl->workers[idx].sip_ids) return -1; lock_set_get(jwl->sems, idx); while((p=(xj_jkey)delpos234(jwl->workers[idx].sip_ids, 0))!=NULL) { if(fl) { #ifdef XJ_EXTRA_DEBUG LM_DBG("sending disconnect message" " to <%.*s>\n", p->id->len, p->id->s); #endif xj_send_sip_msgz(_PADDR(jwl), p->id, &jab_gw_name, XJ_DMSG_INF_DISCONNECTED, NULL); } jwl->workers[idx].nr--; xj_jkey_free_p(p); } lock_set_release(jwl->sems, idx); return 0; } /** * callback function for TM */ void xj_tuac_callback( struct cell *t, int type, struct tmcb_params *ps) { #ifdef XJ_EXTRA_DEBUG LM_DBG("completed with status %d\n", ps->code); #endif if(!ps->param) { LM_DBG("parameter not received\n"); return; } #ifdef XJ_EXTRA_DEBUG LM_DBG("parameter [%p : ex-value=%d]\n", ps->param,*((int*)ps->param) ); #endif if(ps->code < 200 || ps->code >= 300) { #ifdef XJ_EXTRA_DEBUG LM_DBG("no 2XX return code - connection set as expired \n"); #endif *((int*)ps->param) = XJ_FLAG_CLOSE; } } /** * check for expired connections */ void xj_worker_check_jcons(xj_wlist jwl, xj_jcon_pool jcp, int ltime, fd_set *pset) { int i; xj_jconf jcf; for(i = 0; i < jcp->len && main_loop; i++) { if(jcp->ojc[i] == NULL) continue; if(jcp->ojc[i]->jkey->flag==XJ_FLAG_OPEN && jcp->ojc[i]->expire > ltime) continue; #ifdef XJ_EXTRA_DEBUG LM_DBG("connection expired for <%.*s> \n", jcp->ojc[i]->jkey->id->len, jcp->ojc[i]->jkey->id->s); #endif xj_send_sip_msgz(_PADDR(jwl), jcp->ojc[i]->jkey->id, &jab_gw_name, XJ_DMSG_INF_JOFFLINE, NULL); #ifdef XJ_EXTRA_DEBUG LM_DBG("connection's close flag =%d\n", jcp->ojc[i]->jkey->flag); #endif // CLEAN JAB_WLIST xj_wlist_del(jwl, jcp->ojc[i]->jkey, _xj_pid); // looking for open conference rooms #ifdef XJ_EXTRA_DEBUG LM_DBG("having %d open conferences\n", jcp->ojc[i]->nrjconf); #endif while(jcp->ojc[i]->nrjconf > 0) { if((jcf=delpos234(jcp->ojc[i]->jconf,0))!=NULL) { // get out of room xj_jcon_jconf_presence(jcp->ojc[i],jcf, "unavailable", NULL); xj_jconf_free(jcf); } jcp->ojc[i]->nrjconf--; } // send offline presence to all subscribers if(jcp->ojc[i]->plist) { #ifdef XJ_EXTRA_DEBUG LM_DBG("sending 'terminated' status to SIP subscriber\n"); #endif xj_pres_list_notifyall(jcp->ojc[i]->plist, XJ_PS_TERMINATED); } FD_CLR(jcp->ojc[i]->sock, pset); xj_jcon_disconnect(jcp->ojc[i]); xj_jcon_free(jcp->ojc[i]); jcp->ojc[i] = NULL; } } /** * check if there are msg to send or delete from queue */ void xj_worker_check_qmsg(xj_wlist jwl, xj_jcon_pool jcp) { int i, flag; str sto; char buff[1024]; if(!jwl || !jcp) return; /** check the msg queue AND if the target connection is ready */ for(i = 0; ijmqueue.size && main_loop; i++) { if(jcp->jmqueue.jsm[i]==NULL || jcp->jmqueue.ojc[i]==NULL) { if(jcp->jmqueue.jsm[i]!=NULL) { xj_sipmsg_free(jcp->jmqueue.jsm[i]); jcp->jmqueue.jsm[i] = NULL; xj_jcon_pool_del_jmsg(jcp, i); } if(jcp->jmqueue.ojc[i]!=NULL) xj_jcon_pool_del_jmsg(jcp, i); continue; } if(jcp->jmqueue.expire[i] < get_ticks()) { #ifdef XJ_EXTRA_DEBUG LM_DBG("message to %.*s is expired\n", jcp->jmqueue.jsm[i]->to.len, jcp->jmqueue.jsm[i]->to.s); #endif xj_send_sip_msgz(_PADDR(jwl), jcp->jmqueue.jsm[i]->jkey->id, &jcp->jmqueue.jsm[i]->to, XJ_DMSG_ERR_SENDIM, &jcp->jmqueue.ojc[i]->jkey->flag); if(jcp->jmqueue.jsm[i]!=NULL) { xj_sipmsg_free(jcp->jmqueue.jsm[i]); jcp->jmqueue.jsm[i] = NULL; } /** delete message from queue */ xj_jcon_pool_del_jmsg(jcp, i); continue; } #ifdef XJ_EXTRA_DEBUG LM_DBG("%d: QUEUE: message[%d] from [%.*s]" "/to [%.*s]/body[%.*s] expires at %d\n", get_ticks(), i, jcp->jmqueue.jsm[i]->jkey->id->len, jcp->jmqueue.jsm[i]->jkey->id->s, jcp->jmqueue.jsm[i]->to.len,jcp->jmqueue.jsm[i]->to.s, jcp->jmqueue.jsm[i]->msg.len,jcp->jmqueue.jsm[i]->msg.s, jcp->jmqueue.expire[i]); #endif if(xj_jcon_is_ready(jcp->jmqueue.ojc[i], jcp->jmqueue.jsm[i]->to.s, jcp->jmqueue.jsm[i]->to.len, jwl->aliases->dlm)) continue; /*** address correction ***/ flag = XJ_ADDRTR_S2J; if(!xj_jconf_check_addr(&jcp->jmqueue.jsm[i]->to,jwl->aliases->dlm)) flag |= XJ_ADDRTR_CON; sto.s = buff; sto.len = 0; if(xj_address_translation(&jcp->jmqueue.jsm[i]->to, &sto, jwl->aliases, flag) == 0) { /** send message from queue */ #ifdef XJ_EXTRA_DEBUG LM_DBG("sending the message from" " local queue to Jabber network ...\n"); #endif xj_jcon_send_msg(jcp->jmqueue.ojc[i], sto.s, sto.len, jcp->jmqueue.jsm[i]->msg.s, jcp->jmqueue.jsm[i]->msg.len, (flag&XJ_ADDRTR_CON)?XJ_JMSG_GROUPCHAT:XJ_JMSG_CHAT); } else LM_ERR("sending the message from" " local queue to Jabber network ...\n"); if(jcp->jmqueue.jsm[i]!=NULL) { xj_sipmsg_free(jcp->jmqueue.jsm[i]); jcp->jmqueue.jsm[i] = NULL; } /** delete message from queue */ xj_jcon_pool_del_jmsg(jcp, i); } } /** * update or register a presence watcher */ void xj_worker_check_watcher(xj_wlist jwl, xj_jcon_pool jcp, xj_jcon jbc, xj_sipmsg jsmsg) { str sto; char buff[1024]; xj_pres_cell prc = NULL; if(!jwl || !jcp || !jbc || !jsmsg) return; if(!jsmsg->cbf) { #ifdef XJ_EXTRA_DEBUG LM_DBG("null PA callback function\n"); #endif return; } if(!xj_jconf_check_addr(&jsmsg->to, jwl->aliases->dlm)) { // is for a conference - ignore?!?! #ifdef XJ_EXTRA_DEBUG LM_DBG("presence request for a conference.\n"); #endif // set as offline (*(jsmsg->cbf))(&jsmsg->to, &jsmsg->to, XJ_PS_OFFLINE, jsmsg->p); return; } sto.s = buff; sto.len = 0; if(xj_address_translation(&jsmsg->to, &sto, jwl->aliases, XJ_ADDRTR_S2J) == 0) { prc = xj_pres_list_check(jbc->plist, &sto); if(!prc) { #ifdef XJ_EXTRA_DEBUG LM_DBG("new presence cell for %.*s.\n", sto.len, sto.s); #endif prc = xj_pres_cell_new(); if(!prc) { LM_DBG("cannot create a presence cell for %.*s.\n",sto.len, sto.s); return; } if(xj_pres_cell_init(prc, &sto, jsmsg->cbf, jsmsg->p)<0) { LM_DBG("cannot init the presence" " cell for %.*s.\n", sto.len, sto.s); xj_pres_cell_free(prc); return; } if((prc = xj_pres_list_add(jbc->plist, prc))==NULL) { LM_DBG("cannot add the presence" " cell for %.*s.\n", sto.len, sto.s); return; } sto.s[sto.len] = 0; if(!xj_jcon_send_subscribe(jbc, sto.s, NULL, "subscribe")) prc->status = XJ_PRES_STATUS_WAIT; } else { xj_pres_cell_update(prc, jsmsg->cbf, jsmsg->p); #ifdef XJ_EXTRA_DEBUG LM_DBG("calling CBF(%.*s,%d)\n", jsmsg->to.len, jsmsg->to.s, prc->state); #endif // send presence info to SIP subscriber (*(prc->cbf))(&jsmsg->to, &jsmsg->to, prc->state, prc->cbp); } } } /***************************** ****************************************/ kamailio-4.0.4/obsolete/jabber_k/xjab_presence.c0000644000000000000000000001171612223032460020332 0ustar rootroot/* * $Id$ * * XJAB module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "xjab_presence.h" /** * create a presence cell */ xj_pres_cell xj_pres_cell_new(void) { xj_pres_cell prc = NULL; prc = (xj_pres_cell)pkg_malloc(sizeof(t_xj_pres_cell)); if(prc == NULL) return NULL; prc->key = 0; prc->userid.s = NULL; prc->userid.len = 0; prc->state = XJ_PS_OFFLINE; prc->status = XJ_PRES_STATUS_NULL; prc->cbf = NULL; prc->cbp = NULL; prc->prev = NULL; prc->next = NULL; return prc; } /** * free the presence cell */ void xj_pres_cell_free(xj_pres_cell prc) { if(!prc) return; if(prc->userid.s) pkg_free(prc->userid.s); pkg_free(prc); prc = NULL; } /** * free all presence cell linked to */ void xj_pres_cell_free_all(xj_pres_cell prc) { xj_pres_cell p, p0; if(!prc) return; p = prc; while(p) { p0 = p->next; xj_pres_cell_free(p); p = p0; } } /** * init a presence cell */ int xj_pres_cell_init(xj_pres_cell prc, str* uid, pa_callback_f f, void* p) { if(!prc || !uid || !uid->s || uid->len<=0) return -1; prc->userid.s = (char*)pkg_malloc(uid->len*sizeof(char)); if(prc->userid.s == NULL) return -1; strncpy(prc->userid.s, uid->s, uid->len); prc->userid.len = uid->len; prc->key = xj_get_hash(uid, NULL); prc->cbf = f; prc->cbp = p; return 0; } /** * update attributes for a presence cell */ int xj_pres_cell_update(xj_pres_cell prc, pa_callback_f f, void *p) { if(!prc) return -1; prc->cbf = f; prc->cbp = p; return 0; } /** * init a presence list */ xj_pres_list xj_pres_list_init(void) { xj_pres_list prl = NULL; prl = (xj_pres_list)pkg_malloc(sizeof(t_xj_pres_list)); if(!prl) return NULL; prl->nr = 0; prl->clist = NULL; return prl; } /** * free the presence list */ void xj_pres_list_free(xj_pres_list prl) { if(!prl) return; xj_pres_cell_free_all(prl->clist); pkg_free(prl); prl = NULL; } /** * add, if does not exist, an user in present list */ xj_pres_cell xj_pres_list_add(xj_pres_list prl, xj_pres_cell prc) { xj_pres_cell p, p0; if(!prc) return NULL; if(!prl) { xj_pres_cell_free(prc); return NULL; } // presence list empty if(prl->clist==NULL) { prl->nr++; prl->clist = prc; return prc; } p0 = p = prl->clist; while(p && p->key <= prc->key) { if(p->key == prc->key && p->userid.len == prc->userid.len && !strncasecmp(p->userid.s, prc->userid.s, p->userid.len)) { // cell already exist // update cbf and cbp p->cbf = prc->cbf; p->cbp = prc->cbp; xj_pres_cell_free(prc); return p; } p0 = p; p = p->next; } // add a the cell in list prc->next = p0->next; prc->prev = p0; if(p0->next) p0->next->prev = prc; p0->next = prc; prl->nr++; return prc; } /** * delete a user from presence list */ int xj_pres_list_del(xj_pres_list prl, str *uid) { xj_pres_cell p; int lkey; if(!prl || !uid || !uid->s || uid->len<=0) return -1; if(prl->nr<=0 || prl->clist==NULL) return 0; lkey = xj_get_hash(uid, NULL); p = prl->clist; while(p && p->key <= lkey) { if(p->key == lkey && p->userid.len == uid->len && !strncasecmp(p->userid.s, uid->s, uid->len)) { prl->nr--; if(p->next) p->next->prev = p->prev; if(p->prev == NULL) prl->clist = p->next; else p->prev->next = p->next; xj_pres_cell_free(p); return 0; } p = p->next; } return 0; } /** * Check if a user is already in presence list */ xj_pres_cell xj_pres_list_check(xj_pres_list prl, str* uid) { xj_pres_cell p; int lkey; if(!prl || !uid || !uid->s || uid->len<=0 || prl->nr<=0 || prl->clist==NULL) return NULL; lkey = xj_get_hash(uid, NULL); p = prl->clist; while(p && p->key <= lkey) { if(p->key == lkey && p->userid.len == uid->len && !strncasecmp(p->userid.s, uid->s, uid->len)) return p; p = p->next; } return NULL; } /** * Notify all users from list */ void xj_pres_list_notifyall(xj_pres_list prl, int s) { xj_pres_cell p; if(!prl || prl->nr<=0 || prl->clist==NULL) return; p = prl->clist; while(p) { if(p->cbf) (*(p->cbf))(&(p->userid),&(p->userid), (s==XJ_PS_CHECK)?p->state:s, p->cbp); p = p->next; } } kamailio-4.0.4/obsolete/jabber_k/jabber.c0000644000000000000000000005544512223032460016756 0ustar rootroot/* * $Id$ * * XJAB module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * --- * * History * ------- * 2003-02-28 connection management with ihttp implemented (dcm) * 2003-02-24 first version of callback functions for ihttp (dcm) * 2003-02-13 lot of comments enclosed in #ifdef XJ_EXTRA_DEBUG (dcm) * 2003-03-11 New module interface (janakj) * 2003-03-16 flags export parameter added (janakj) * 2003-04-06 rank 0 changed to 1 in child_init (janakj) * 2003-06-19 fixed too many Jabber workers bug (mostly on RH9.0) (dcm) * 2003-08-05 adapted to the new parse_content_type_hdr function (bogdan) * 2004-06-07 db API update (andrei) */ #include #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../error.h" #include "../../ut.h" #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../globals.h" #include "../../timer.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_content.h" #include "../../parser/parse_from.h" #include "../../lib/srdb1/db.h" #include "../../modules/tm/tm_load.h" #ifdef HAVE_IHTTP #include "../ihttp/ih_load.h" #endif #include "xjab_load.h" #include "xjab_worker.h" #include "xjab_util.h" MODULE_VERSION /** TM bind */ struct tm_binds tmb; #ifdef HAVE_IHTTP /** iHTTP bind */ struct ih_binds ihb; /** iHTTP callback functions */ int xjab_mod_info(ih_req_p _irp, void *_p, char *_bb, int *_bl, char *_hb, int *_hl); int xjab_connections(ih_req_p _irp, void *_p, char *_bb, int *_bl, char *_hb, int *_hl); #endif /** workers list */ xj_wlist jwl = NULL; /** Structure that represents database connection */ static db1_con_t** db_con; static db_func_t jabber_dbf; /** parameters */ static str db_url = str_init("mysql://root@127.0.0.1/sip_jab"); static str db_table = str_init("jusers"); char *registrar=NULL; /*"sip:registrar@example.org";*/ int nrw = 2; int max_jobs = 10; char *jaddress = "127.0.0.1"; int jport = 5222; char *jaliases = NULL; char *jdomain = NULL; char *proxy = NULL; char* priority = "9"; int delay_time = 90; int sleep_time = 20; int cache_time = 600; int check_time = 20; int **pipes = NULL; static int mod_init(void); static int child_init(int rank); int xjab_manage_sipmsg(struct sip_msg *msg, int type); void xjab_check_workers(int mpid); static int xj_send_message(struct sip_msg*, char*, char*); static int xj_join_jconf(struct sip_msg*, char*, char*); static int xj_exit_jconf(struct sip_msg*, char*, char*); static int xj_go_online(struct sip_msg*, char*, char*); static int xj_go_offline(struct sip_msg*, char*, char*); static void destroy(void); /* * Exported functions */ static cmd_export_t cmds[] = { {"jab_send_message", (cmd_function)xj_send_message, 0, 0, 0, REQUEST_ROUTE}, {"jab_join_jconf", (cmd_function)xj_join_jconf, 0, 0, 0, REQUEST_ROUTE}, {"jab_exit_jconf", (cmd_function)xj_exit_jconf, 0, 0, 0, REQUEST_ROUTE}, {"jab_go_online", (cmd_function)xj_go_online, 0, 0, 0, REQUEST_ROUTE}, {"jab_go_offline", (cmd_function)xj_go_offline, 0, 0, 0, REQUEST_ROUTE}, {"jab_register_watcher", (cmd_function)xj_register_watcher, XJ_NO_SCRIPT_F, 0, 0, 0 }, {"jab_unregister_watcher", (cmd_function)xj_unregister_watcher, XJ_NO_SCRIPT_F, 0, 0, 0 }, {"load_xjab", (cmd_function)load_xjab, XJ_NO_SCRIPT_F, 0, 0, 0 }, {0, 0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"db_url", STR_PARAM, &db_url.s }, {"jaddress", STR_PARAM, &jaddress }, {"aliases", STR_PARAM, &jaliases }, {"proxy", STR_PARAM, &proxy }, {"jdomain", STR_PARAM, &jdomain }, {"registrar", STR_PARAM, ®istrar }, {"priority", STR_PARAM, &priority }, {"jport", INT_PARAM, &jport }, {"workers", INT_PARAM, &nrw }, {"max_jobs", INT_PARAM, &max_jobs }, {"cache_time", INT_PARAM, &cache_time}, {"delay_time", INT_PARAM, &delay_time}, {"sleep_time", INT_PARAM, &sleep_time}, {"check_time", INT_PARAM, &check_time}, {0, 0, 0} }; struct module_exports exports= { "jabber", DEFAULT_DLFLAGS, /* dlopen flags */ cmds, /* Exported functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, (destroy_function) destroy, child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { LM_WARN("This module is deprecated and will be removed in the next release. Use the module."); #ifdef HAVE_IHTTP load_ih_f load_ih; #endif int i; db_url.len = strlen(db_url.s); if(!jdomain) { LM_ERR("jdomain is NULL\n"); return -1; } /* import mysql functions */ if (db_bind_mod(&db_url, &jabber_dbf)<0) { LM_ERR("database module not found\n"); return -1; } if (!DB_CAPABILITY(jabber_dbf, DB_CAP_QUERY)) { LM_ERR("database module does not implement 'query' function\n"); return -1; } db_con = (db1_con_t**)shm_malloc(nrw*sizeof(db1_con_t*)); if (db_con == NULL) { LM_ERR("no more shm memory\n"); return -1; } /* load the TM API */ if (load_tm_api(&tmb)!=0) { LM_ERR("can't load TM API\n"); return -1; } #ifdef HAVE_IHTTP /* import the iHTTP auto-loading function */ if ( !(load_ih=(load_ih_f)find_export("load_ih", IH_NO_SCRIPT_F, 0))) { LM_ERR("can't import load_ih\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_ih( &ihb )==-1) return -1; #endif pipes = (int**)pkg_malloc(nrw*sizeof(int*)); if (pipes == NULL) { LM_ERR("no more pkg memory (pipes)\n"); return -1; } for(i=0; i-<%d>\n", i, pipes[i][0], pipes[i][1]); } if((jwl = xj_wlist_init(pipes,nrw,max_jobs,cache_time,sleep_time, delay_time)) == NULL) { LM_ERR("failed to initialize workers list\n"); return -1; } if(xj_wlist_set_aliases(jwl, jaliases, jdomain, proxy) < 0) { LM_ERR("failed to set aliases and outbound proxy\n"); return -1; } LM_DBG("initialized ...\n"); return 0; } /* * Initialize children */ static int child_init(int rank) { int i, j, mpid, cpid; LM_DBG("initializing child <%d>\n", rank); /* Rank 0 is main process now - 1 is the first child (janakj) */ if(rank == 1) { #ifdef HAVE_IHTTP /** register iHTTP callbacks -- go forward in any case*/ ihb.reg_f("xjab", "XMPP Gateway", IH_MENU_YES, xjab_mod_info, NULL); ihb.reg_f("xjabc", "XMPP connections", IH_MENU_YES, xjab_connections, NULL); #endif if((mpid=fork())<0 ) { LM_ERR("cannot launch worker's manager\n"); return -1; } if(mpid == 0) { /** launching the workers */ for(i=0;icontent_length) { LM_ERR("no Content-Length header found!\n"); goto error; } body.len = get_content_length(msg); /* parse the content-type header */ if((mime=parse_content_type_hdr(msg))<1) { LM_ERR("cannot parse Content-Type header\n"); goto error; } /* check the content-type value */ if(mime!=(TYPE_TEXT<<16)+SUBTYPE_PLAIN && mime!=(TYPE_MESSAGE<<16)+SUBTYPE_CPIM) { LM_ERR("invalid content-type for" " a message request! type found=%d\n", mime); goto error; } } // check for TO and FROM headers - if is not SIP MESSAGE if(parse_headers(msg,HDR_TO_F|HDR_FROM_F,0)==-1 || !msg->to || !msg->from) { LM_ERR("cannot find TO or FROM HEADERS!\n"); goto error; } /* parsing from header */ if ( parse_from_header( msg )<0 || msg->from->parsed==NULL) { LM_DBG("cannot get FROM header\n"); goto error; } from_uri.s = ((struct to_body*)msg->from->parsed)->uri.s; from_uri.len = ((struct to_body*)msg->from->parsed)->uri.len; if(xj_extract_aor(&from_uri, 0)) { LM_DBG("cannot get AoR from FROM header\n"); goto error; } jkey.hash = xj_get_hash(&from_uri, NULL); jkey.id = &from_uri; // get the communication pipe with the worker switch(type) { case XJ_SEND_MESSAGE: case XJ_JOIN_JCONF: case XJ_GO_ONLINE: if((pipe = xj_wlist_get(jwl, &jkey, &p)) < 0) { LM_DBG("cannot find pipe of the worker!\n"); goto error; } break; case XJ_EXIT_JCONF: case XJ_GO_OFFLINE: if((pipe = xj_wlist_check(jwl, &jkey, &p)) < 0) { LM_DBG("no open Jabber session for" " <%.*s>!\n", from_uri.len, from_uri.s); goto error; } break; default: LM_DBG("error, strange SIP msg type!\n"); goto error; } // if is for going ONLINE/OFFLINE we do not need the destination if(type==XJ_GO_ONLINE || type==XJ_GO_OFFLINE) goto prepare_job; // determination of destination // - try to get it from new_uri, r-uri or to hdr, but check it against // jdomain and aliases dst.len = 0; if( msg->new_uri.len > 0) { dst.s = msg->new_uri.s; dst.len = msg->new_uri.len; if(xj_wlist_check_aliases(jwl, &dst)) dst.len = 0; #ifdef XJ_EXTRA_DEBUG else LM_DBG("using NEW URI for destination\n"); #endif } if (dst.len == 0 && msg->first_line.u.request.uri.s != NULL && msg->first_line.u.request.uri.len > 0 ) { dst.s = msg->first_line.u.request.uri.s; dst.len = msg->first_line.u.request.uri.len; if(xj_wlist_check_aliases(jwl, &dst)) dst.len = 0; #ifdef XJ_EXTRA_DEBUG else LM_DBG("using R-URI for destination\n"); #endif } if(dst.len == 0 && msg->to->parsed) { dst.s = ((struct to_body*)msg->to->parsed)->uri.s; dst.len = ((struct to_body*)msg->to->parsed)->uri.len; if(dst.s == NULL || xj_wlist_check_aliases(jwl, &dst)) dst.len = 0; #ifdef XJ_EXTRA_DEBUG else LM_DBG("using TO-URI for destination\n"); #endif } if(dst.len == 0) { LM_DBG("destination not found in SIP message\n"); goto error; } /** skip 'sip:' and parameters in destination address */ if(xj_extract_aor(&dst, 1)) { LM_ERR("cannot get AoR for destination\n"); goto error; } #ifdef XJ_EXTRA_DEBUG LM_DBG("destination after correction [%.*s].\n", dst.len, dst.s); #endif prepare_job: //putting the SIP message parts in share memory to be accessible by workers jsmsg = (xj_sipmsg)shm_malloc(sizeof(t_xj_sipmsg)); memset(jsmsg, 0, sizeof(t_xj_sipmsg)); if(jsmsg == NULL) return -1; switch(type) { case XJ_SEND_MESSAGE: jsmsg->msg.len = body.len; if((jsmsg->msg.s = (char*)shm_malloc(jsmsg->msg.len+1)) == NULL) { shm_free(jsmsg); goto error; } strncpy(jsmsg->msg.s, body.s, jsmsg->msg.len); break; case XJ_GO_ONLINE: case XJ_GO_OFFLINE: dst.len = 0; dst.s = 0; case XJ_JOIN_JCONF: case XJ_EXIT_JCONF: jsmsg->msg.len = 0; jsmsg->msg.s = NULL; break; default: LM_DBG("this SHOULD NOT appear\n"); shm_free(jsmsg); goto error; } if(dst.len>0) { jsmsg->to.len = dst.len; if((jsmsg->to.s = (char*)shm_malloc(jsmsg->to.len+1))==NULL) { if(type == XJ_SEND_MESSAGE) shm_free(jsmsg->msg.s); shm_free(jsmsg); goto error; } strncpy(jsmsg->to.s, dst.s, jsmsg->to.len); } else { jsmsg->to.len = 0; jsmsg->to.s = 0; } jsmsg->jkey = p; jsmsg->type = type; //jsmsg->jkey->hash = jkey.hash; LM_DBG("sending <%p> to worker through <%d>\n", jsmsg, pipe); // sending the SHM pointer of SIP message to the worker fl = write(pipe, &jsmsg, sizeof(jsmsg)); if(fl != sizeof(jsmsg)) { LM_ERR("failed to write to worker pipe!\n"); if(type == XJ_SEND_MESSAGE) shm_free(jsmsg->msg.s); shm_free(jsmsg->to.s); shm_free(jsmsg); goto error; } return 1; error: return -1; } /** * destroy function of module */ static void destroy(void) { int i; #ifdef XJ_EXTRA_DEBUG LM_DBG("unloading module ...\n"); #endif if(pipes) { // close the pipes for(i = 0; i < nrw; i++) { if(pipes[i]) { close(pipes[i][0]); close(pipes[i][1]); } pkg_free(pipes[i]); } pkg_free(pipes); } // cleaning MySQL connections if(db_con != NULL) { for(i = 0; ilen, from->s, to->len, to->s); #endif from_uri.s = from->s; from_uri.len = from->len; if(xj_extract_aor(&from_uri, 0)) { LM_ERR("cannot get AoR from FROM header\n"); goto error; } jkey.hash = xj_get_hash(&from_uri, NULL); jkey.id = &from_uri; if((pipe = xj_wlist_get(jwl, &jkey, &jp)) < 0) { LM_DBG("cannot find pipe of the worker!\n"); goto error; } //putting the SIP message parts in share memory to be accessible by workers jsmsg = (xj_sipmsg)shm_malloc(sizeof(t_xj_sipmsg)); memset(jsmsg, 0, sizeof(t_xj_sipmsg)); if(jsmsg == NULL) goto error; jsmsg->msg.len = 0; jsmsg->msg.s = NULL; to_uri.s = to->s; to_uri.len = to->len; /** skip 'sip:' and parameters in destination address */ if(xj_extract_aor(&to_uri, 1)) { LM_ERR("cannot get AoR for destination\n"); goto error; } #ifdef XJ_EXTRA_DEBUG LM_DBG("destination after correction [%.*s].\n", to_uri.len, to_uri.s); #endif jsmsg->to.len = to_uri.len; if((jsmsg->to.s = (char*)shm_malloc(jsmsg->to.len+1)) == NULL) { if(jsmsg->msg.s) shm_free(jsmsg->msg.s); shm_free(jsmsg); goto error; } strncpy(jsmsg->to.s, to_uri.s, jsmsg->to.len); jsmsg->to.s[jsmsg->to.len] = '\0'; jsmsg->jkey = jp; jsmsg->type = XJ_REG_WATCHER; //jsmsg->jkey->hash = jkey.hash; jsmsg->cbf = (pa_callback_f)cbf; jsmsg->p = pp; #ifdef XJ_EXTRA_DEBUG LM_DBG("sending <%p> to worker through <%d>\n", jsmsg, pipe); #endif // sending the SHM pointer of SIP message to the worker fl = write(pipe, &jsmsg, sizeof(jsmsg)); if(fl != sizeof(jsmsg)) { LM_ERR("failed to write to worker pipe!\n"); if(jsmsg->msg.s) shm_free(jsmsg->msg.s); shm_free(jsmsg->to.s); shm_free(jsmsg); goto error; } error: return; } /** * unregister a watcher for a Jabber user' presence */ void xj_unregister_watcher(str *from, str *to, void *cbf, void *pp) { if(!to || !from) return; } /** * check if all SER2Jab workers are still alive * - if not, try to launch new ones */ void xjab_check_workers(int mpid) { int i, n, stat; //LM_DBG("time=%d\n", get_ticks()); if(!jwl || jwl->len <= 0) return; for(i=0; i < jwl->len; i++) { if(jwl->workers[i].pid > 0) { stat = 0; n = waitpid(jwl->workers[i].pid, &stat, WNOHANG); if(n == 0 || n!=jwl->workers[i].pid) continue; LM_ERR("worker[%d][pid=%d] has exited - status=%d err=%d" "errno=%d\n", i, jwl->workers[i].pid, stat, n, errno); xj_wlist_clean_jobs(jwl, i, 1); xj_wlist_set_pid(jwl, -1, i); } #ifdef XJ_EXTRA_DEBUG LM_DBG("create a new worker[%d]\n", i); #endif if ( (stat=fork())<0 ) { #ifdef XJ_EXTRA_DEBUG LM_DBG("cannot launch new worker[%d]\n", i); #endif LM_ERR("worker[%d] lost forever \n", i); return; } if (stat == 0) { if(xj_wlist_set_pid(jwl, getpid(), i) < 0) { LM_ERR("failed to set new worker's pid - w[%d]\n", i); return; } xj_worker_process(jwl,jaddress,jport,priority, i, db_con[i], &jabber_dbf); exit(0); } } } #ifdef HAVE_IHTTP /** * Module's information retrieval - function to use with iHttp module * */ int xjab_mod_info(ih_req_p _irp, void *_p, char *_bb, int *_bl, char *_hb, int *_hl) { if(!_irp || !_bb || !_bl || *_bl <= 0 || !_hb || !_hl || *_hl <= 0) return -1; *_hl = 0; *_hb = 0; strcpy(_bb, "

SER2Jabber Gateway

"); strcat(_bb, "
Module parameters:
"); strcat(_bb, "
-- db table = "); strcat(_bb, db_table); strcat(_bb, "
-- workers = "); strcat(_bb, int2str(nrw, NULL)); strcat(_bb, "
-- max jobs per worker = "); strcat(_bb, int2str(max_jobs, NULL)); strcat(_bb, "
-- jabber server address = "); strcat(_bb, jaddress); strcat(_bb, "
-- jabber server port = "); strcat(_bb, int2str(jport, NULL)); strcat(_bb, "
-- aliases = "); strcat(_bb, (jaliases)?jaliases:"NULL"); strcat(_bb, "
-- jabber domain = "); strcat(_bb, (jdomain)?jdomain:"NULL"); strcat(_bb, "
-- proxy address = "); strcat(_bb, (proxy)?proxy:"NULL"); strcat(_bb, "
-- delay time = "); strcat(_bb, int2str(delay_time, NULL)); strcat(_bb, "
-- sleep time = "); strcat(_bb, int2str(sleep_time, NULL)); strcat(_bb, "
-- cache time = "); strcat(_bb, int2str(cache_time, NULL)); strcat(_bb, "
-- check time = "); strcat(_bb, int2str(check_time, NULL)); *_bl = strlen(_bb); return 0; } /** * SER2Jab connection management - function to use with iHttp module * - be aware of who is able to use the ihttp because he can close any * open connection between SER and Jabber server */ int xjab_connections(ih_req_p _irp, void *_p, char *_bb, int *_bl, char *_hb, int *_hl) { t_xj_jkey jkey, *p; str _u; ih_param_p _ipp = NULL; int idx, i, maxcount; char *cp; if(!_irp || !_bb || !_bl || *_bl <= 0 || !_hb || !_hl || *_hl <= 0) return -1; *_hl = 0; *_hb = 0; idx = -1; strcpy(_bb, "

Active XMPP connections

"); if(_irp->params) { strcat(_bb, "
Close action is alpha release!
"); _ipp = _irp->params; i = 0; while(_ipp) { switch(_ipp->name[0]) { case 'w': idx = 0; cp = _ipp->value; while(*cp && *cp>='0' && *cp<='9') { idx = idx*10 + *cp-'0'; cp++; } i++; break; case 'u': _u.s = _ipp->value; _u.len = strlen(_ipp->value); jkey.id = &_u; i++; break; case 'i': jkey.hash = 0; cp = _ipp->value; while(*cp && *cp>='0' && *cp<='9') { jkey.hash = jkey.hash*10 + *cp-'0'; cp++; } i++; break; } _ipp = _ipp->next; } if(i!=3 || idx < 0 || idx >= jwl->len) { strcat(_bb, "
Bad parameters!\n"); } else { strcat(_bb, "
The connection of ["); strcat(_bb, _u.s); if(xj_wlist_set_flag(jwl, &jkey, XJ_FLAG_CLOSE) < 0) strcat(_bb, "] does not exist!\n"); else strcat(_bb, "] was scheduled for closing!
\n"); } *_bl = strlen(_bb); return 0; } if(jwl!=NULL && jwl->len > 0 && jwl->workers!=NULL) { for(idx=0; idxlen; idx++) { strcat(_bb, "
Worker["); strcat(_bb, int2str(idx, NULL)); strcat(_bb, "]   pid="); strcat(_bb, int2str(jwl->workers[idx].pid, NULL)); strcat(_bb, "   nr of jobs="); strcat(_bb, int2str(jwl->workers[idx].nr, NULL)); if(!jwl->workers[idx].sip_ids) continue; lock_set_get(jwl->sems, idx); maxcount = count234(jwl->workers[idx].sip_ids); for (i = 0; i < maxcount; i++) { p = (xj_jkey)index234(jwl->workers[idx].sip_ids, i); if(p == NULL) continue; strcat(_bb, "
   "); strcat(_bb, int2str(i, NULL)); strcat(_bb, ".   "); strcat(_bb, "close"); strcat(_bb, "   "); strcat(_bb, int2str(p->hash, NULL)); strcat(_bb, "   "); strncat(_bb, p->id->s, p->id->len); } lock_set_release(jwl->sems, idx); } } *_bl = strlen(_bb); return 0; } #endif // HAVE_IHTTP kamailio-4.0.4/obsolete/jabber_k/tree234.h0000644000000000000000000001332212223032460016712 0ustar rootroot/* * $Id$ * * tree234.h: header defining functions in tree234.c. * * This file is copyright 1999-2001 Simon Tatham. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #ifndef TREE234_H #define TREE234_H /* * This typedef is opaque outside tree234.c itself. */ typedef struct tree234_Tag tree234; typedef int (*cmpfn234)(void *, void *); /** * function for deallocation of a element pointer from a node */ typedef void (*freefn)(void *); /* * Create a 2-3-4 tree. If `cmp' is NULL, the tree is unsorted, and * lookups by key will fail: you can only look things up by numeric * index, and you have to use addpos234() and delpos234(). */ tree234 *newtree234(cmpfn234 cmp); /* * Free a 2-3-4 tree (not including freeing the elements). */ void freetree234(tree234 *t); /* * Free a 2-3-4 tree (including freeing the elements with 'fn' function). */ void free2tree234(tree234 *t, freefn fn); /* * Add an element e to a sorted 2-3-4 tree t. Returns e on success, * or if an existing element compares equal, returns that. */ void *add234(tree234 *t, void *e); /* * Add an element e to an unsorted 2-3-4 tree t. Returns e on * success, NULL on failure. (Failure should only occur if the * index is out of range or the tree is sorted.) * * Index range can be from 0 to the tree's current element count, * inclusive. */ void *addpos234(tree234 *t, void *e, int index); /* * Look up the element at a given numeric index in a 2-3-4 tree. * Returns NULL if the index is out of range. * * One obvious use for this function is in iterating over the whole * of a tree (sorted or unsorted): * * for (i = 0; (p = index234(tree, i)) != NULL; i++) consume(p); * * or * * int maxcount = count234(tree); * for (i = 0; i < maxcount; i++) { * p = index234(tree, i); * assert(p != NULL); * consume(p); * } */ void *index234(tree234 *t, int index); /* * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not * found. e is always passed as the first argument to cmp, so cmp * can be an asymmetric function if desired. cmp can also be passed * as NULL, in which case the compare function from the tree proper * will be used. * * Three of these functions are special cases of findrelpos234. The * non-`pos' variants lack the `index' parameter: if the parameter * is present and non-NULL, it must point to an integer variable * which will be filled with the numeric index of the returned * element. * * The non-`rel' variants lack the `relation' parameter. This * parameter allows you to specify what relation the element you * provide has to the element you're looking for. This parameter * can be: * * REL234_EQ - find only an element that compares equal to e * REL234_LT - find the greatest element that compares < e * REL234_LE - find the greatest element that compares <= e * REL234_GT - find the smallest element that compares > e * REL234_GE - find the smallest element that compares >= e * * Non-`rel' variants assume REL234_EQ. * * If `rel' is REL234_GT or REL234_LT, the `e' parameter may be * NULL. In this case, REL234_GT will return the smallest element * in the tree, and REL234_LT will return the greatest. This gives * an alternative means of iterating over a sorted tree, instead of * using index234: * * // to loop forwards * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_GT)) != NULL ;) * consume(p); * * // to loop backwards * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_LT)) != NULL ;) * consume(p); */ enum { REL234_EQ, REL234_LT, REL234_LE, REL234_GT, REL234_GE }; void *find234(tree234 *t, void *e, cmpfn234 cmp); void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation); void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index); void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation, int *index); /* * Delete an element e in a 2-3-4 tree. Does not free the element, * merely removes all links to it from the tree nodes. * * delpos234 deletes the element at a particular tree index: it * works on both sorted and unsorted trees. * * del234 deletes the element passed to it, so it only works on * sorted trees. (It's equivalent to using findpos234 to determine * the index of an element, and then passing that index to * delpos234.) * * Both functions return a pointer to the element they delete, for * the user to free or pass on elsewhere or whatever. If the index * is out of range (delpos234) or the element is already not in the * tree (del234) then they return NULL. */ void *del234(tree234 *t, void *e); void *delpos234(tree234 *t, int index); /* * Return the total element count of a tree234. */ int count234(tree234 *t); #endif /* TREE234_H */ kamailio-4.0.4/obsolete/jabber_k/xode_str.c0000644000000000000000000001262312223032460017347 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include "xode.h" xode_pool xode_spool_getpool(const xode_spool s) { if(s == NULL) return NULL; return s->p; } int xode_spool_getlen(const xode_spool s) { if(s == NULL) return 0; return s->len; } void xode_spool_free(xode_spool s) { xode_pool_free(xode_spool_getpool(s)); } xode_spool xode_spool_newfrompool(xode_pool p) { xode_spool s; s = xode_pool_malloc(p, sizeof(struct xode_spool_struct)); s->p = p; s->len = 0; s->last = NULL; s->first = NULL; return s; } xode_spool xode_spool_new(void) { return xode_spool_newfrompool(xode_pool_heap(512)); } void xode_spool_add(xode_spool s, char *str) { struct xode_spool_node *sn; int len; if(str == NULL) return; len = strlen(str); if(len == 0) return; sn = xode_pool_malloc(s->p, sizeof(struct xode_spool_node)); sn->c = xode_pool_strdup(s->p, str); sn->next = NULL; s->len += len; if(s->last != NULL) s->last->next = sn; s->last = sn; if(s->first == NULL) s->first = sn; } void xode_spooler(xode_spool s, ...) { va_list ap; char *arg = NULL; if(s == NULL) return; va_start(ap, s); /* loop till we hit our end flag, the first arg */ while(1) { arg = va_arg(ap,char *); if((void*)arg == (void*)s || arg == NULL) break; else xode_spool_add(s, arg); } va_end(ap); } char *xode_spool_tostr(xode_spool s) { char *ret,*tmp; struct xode_spool_node *next; if(s == NULL || s->len == 0 || s->first == NULL) return NULL; ret = xode_pool_malloc(s->p, s->len + 1); *ret = '\0'; next = s->first; tmp = ret; while(next != NULL) { tmp = strcat(tmp,next->c); next = next->next; } return ret; } /* convenience :) */ char *xode_spool_str(xode_pool p, ...) { va_list ap; xode_spool s; char *arg = NULL; if(p == NULL) return NULL; s = xode_spool_newfrompool(p); va_start(ap, p); /* loop till we hit our end flag, the first arg */ while(1) { arg = va_arg(ap,char *); if((void*)arg == (void*)p) break; else xode_spool_add(s, arg); } va_end(ap); return xode_spool_tostr(s); } char *xode_strunescape(xode_pool p, char *buf) { int i,j=0; char *temp; if (p == NULL || buf == NULL) return(NULL); if (strchr(buf,'&') == NULL) return(buf); temp = xode_pool_malloc(p,strlen(buf)+1); if (temp == NULL) return(NULL); for(i=0;i': newlen+=4; break; } } if(oldlen == newlen) return buf; temp = xode_pool_malloc(p,newlen+1); if (temp==NULL) return(NULL); for(i=j=0;i': memcpy(&temp[j],">",4); j += 4; break; default: temp[j++] = buf[i]; } } temp[j] = '\0'; return temp; } kamailio-4.0.4/obsolete/jabber_k/xpool.c0000644000000000000000000001457212223032460016666 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ * * 2/27/00:3am, random plans by jer * * ok based on gprof, we really need some innovation here... my thoughs are this: * * most things are strings, so have a string-based true-blue garbage collector * one big global hash containing all the strings created by any pstrdup, returning const char * * a refcount on each string block * when a pool is freed, it moves down the refcount * garbage collector collects pools on the free stack, and runs through the hash for unused strings * j_strcmp can check for == (if they are both from a pstrdup) * * let's see... this would change: * pstrdup: do a hash lookup, success=return, fail=pmalloc & hash put * pool_free: * * * * * */ #include "xode.h" //#include "config.h" #define _xode_pool__malloc malloc #define _xode_pool__free free /* xode_pfree - a linked list node which stores an allocation chunk, plus a callback */ struct xode_pool_free { xode_pool_cleaner f; void *arg; struct xode_pool_heap *heap; struct xode_pool_free *next; }; /* make an empty pool */ xode_pool _xode_pool_new(void) { xode_pool p; while((p = _xode_pool__malloc(sizeof(_xode_pool))) == NULL) sleep(1); p->cleanup = NULL; p->heap = NULL; p->size = 0; return p; } /* free a heap */ void _xode_pool_heapfree(void *arg) { struct xode_pool_heap *h = (struct xode_pool_heap *)arg; _xode_pool__free(h->block); _xode_pool__free(h); } /* mem should always be freed last */ void _xode_pool_cleanup_append(xode_pool p, struct xode_pool_free *pf) { struct xode_pool_free *cur; if(p->cleanup == NULL) { p->cleanup = pf; return; } /* fast forward to end of list */ for(cur = p->cleanup; cur->next != NULL; cur = cur->next); cur->next = pf; } /* create a cleanup tracker */ struct xode_pool_free *_xode_pool_free(xode_pool p, xode_pool_cleaner f, void *arg) { struct xode_pool_free *ret; /* make the storage for the tracker */ while((ret = _xode_pool__malloc(sizeof(struct xode_pool_free))) == NULL) sleep(1); ret->f = f; ret->arg = arg; ret->next = NULL; return ret; } /* create a heap and make sure it get's cleaned up */ struct xode_pool_heap *_xode_pool_heap(xode_pool p, int size) { struct xode_pool_heap *ret; struct xode_pool_free *clean; /* make the return heap */ while((ret = _xode_pool__malloc(sizeof(struct xode_pool_heap))) == NULL) sleep(1); while((ret->block = _xode_pool__malloc(size)) == NULL) sleep(1); ret->size = size; p->size += size; ret->used = 0; /* append to the cleanup list */ clean = _xode_pool_free(p, _xode_pool_heapfree, (void *)ret); clean->heap = ret; /* for future use in finding used mem for pstrdup */ _xode_pool_cleanup_append(p, clean); return ret; } xode_pool _xode_pool_newheap(int bytes) { xode_pool p; p = _xode_pool_new(); p->heap = _xode_pool_heap(p,bytes); return p; } void *xode_pool_malloc(xode_pool p, int size) { void *block; if(p == NULL) { fprintf(stderr,"Memory Leak! xode_pmalloc received NULL pool, unable to track allocation, exiting]\n"); abort(); } /* if there is no heap for this pool or it's a big request, just raw, I like how we clean this :) */ if(p->heap == NULL || size > (p->heap->size / 2)) { while((block = _xode_pool__malloc(size)) == NULL) sleep(1); p->size += size; _xode_pool_cleanup_append(p, _xode_pool_free(p, _xode_pool__free, block)); return block; } /* we have to preserve boundaries, long story :) */ if(size >= 4) while(p->heap->used&7) p->heap->used++; /* if we don't fit in the old heap, replace it */ if(size > (p->heap->size - p->heap->used)) p->heap = _xode_pool_heap(p, p->heap->size); /* the current heap has room */ block = (char *)p->heap->block + p->heap->used; p->heap->used += size; return block; } void *xode_pool_mallocx(xode_pool p, int size, char c) { void* result = xode_pool_malloc(p, size); if (result != NULL) memset(result, c, size); return result; } /* easy safety utility (for creating blank mem for structs, etc) */ void *xode_pool_malloco(xode_pool p, int size) { void *block = xode_pool_malloc(p, size); memset(block, 0, size); return block; } /* XXX efficient: move this to const char * and then loop through the existing heaps to see if src is within a block in this pool */ char *xode_pool_strdup(xode_pool p, const char *src) { char *ret; if(src == NULL) return NULL; ret = xode_pool_malloc(p,strlen(src) + 1); strcpy(ret,src); return ret; } /* when move above, this one would actually return a new block */ char *xode_pool_strdupx(xode_pool p, const char *src) { return xode_pool_strdup(p, src); } int xode_pool_size(xode_pool p) { if(p == NULL) return 0; return p->size; } void xode_pool_free(xode_pool p) { struct xode_pool_free *cur, *stub; if(p == NULL) return; cur = p->cleanup; while(cur != NULL) { (*cur->f)(cur->arg); stub = cur->next; _xode_pool__free(cur); cur = stub; } _xode_pool__free(p); } /* public cleanup utils, insert in a way that they are run FIFO, before mem frees */ void xode_pool_cleanup(xode_pool p, xode_pool_cleaner f, void *arg) { struct xode_pool_free *clean; clean = _xode_pool_free(p, f, arg); clean->next = p->cleanup; p->cleanup = clean; } xode_pool xode_pool_new(void) { return _xode_pool_new(); } xode_pool xode_pool_heap(const int bytes) { return _xode_pool_newheap(bytes); } kamailio-4.0.4/obsolete/jabber_k/xode_from.c0000644000000000000000000001301412223032460017475 0ustar rootroot/* * $Id$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Jabber * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ */ #include "xode.h" #include #include #include static void _xode_put_expatattribs(xode current, const char **atts) { int i = 0; if (atts == NULL) return; while (atts[i] != '\0') { xode_put_attrib(current, atts[i], atts[i+1]); i += 2; } } static void _xode_expat_startElement(void* userdata, const char* name, const char** atts) { /* get the xmlnode pointed to by the userdata */ xode *x = userdata; xode current = *x; if (current == NULL) { /* allocate a base node */ current = xode_new(name); _xode_put_expatattribs(current, atts); *x = current; } else { *x = xode_insert_tag(current, name); _xode_put_expatattribs(*x, atts); } } static void _xode_expat_endElement(void* userdata, const char* name) { xode *x = userdata; xode current = *x; current->complete = 1; current = xode_get_parent(current); /* if it's NULL we've hit the top folks, otherwise back up a level */ if(current != NULL) *x = current; } static void _xode_expat_charData(void* userdata, const char* s, int len) { xode *x = userdata; xode current = *x; xode_insert_cdata(current, s, len); } xode xode_from_str(char *str, int len) { XML_Parser p; xode *x, node; /* pointer to an xmlnode */ if(NULL == str) return NULL; if(len == -1) len = strlen(str); x = malloc(sizeof(void *)); *x = NULL; /* pointer to NULL */ p = XML_ParserCreate(NULL); XML_SetUserData(p, x); XML_SetElementHandler(p, _xode_expat_startElement, _xode_expat_endElement); XML_SetCharacterDataHandler(p, _xode_expat_charData); if(!XML_Parse(p, str, len, 1)) { /* jdebug(ZONE,"xmlnode_str_error: %s",(char *)XML_ErrorString(XML_GetErrorCode(p)));*/ xode_free(*x); *x = NULL; } node = *x; free(x); XML_ParserFree(p); return node; /* return the xmlnode x points to */ } xode xode_from_strx(char *str, int len, int *err, int *pos) { XML_Parser p; xode *x, node; /* pointer to an xmlnode */ if(NULL == str) return NULL; if(len == -1) len = strlen(str); x = malloc(sizeof(void *)); *x = NULL; /* pointer to NULL */ p = XML_ParserCreate(NULL); XML_SetUserData(p, x); XML_SetElementHandler(p, _xode_expat_startElement, _xode_expat_endElement); XML_SetCharacterDataHandler(p, _xode_expat_charData); XML_Parse(p, str, len, 0); if(err != NULL) *err = XML_GetErrorCode(p); if(pos != NULL) *pos = XML_GetCurrentByteIndex(p); node = *x; free(x); XML_ParserFree(p); return node; /* return the xmlnode x points to */ } xode xode_from_file(char *file) { XML_Parser p; xode *x, node; /* pointer to an xmlnode */ char buf[BUFSIZ]; int done, fd, len; char _file[1000]; if(NULL == file) return NULL; /* perform tilde expansion */ if(*file == '~') { char *env = getenv("HOME"); if(env != NULL) snprintf((char*)_file, 1000, "%s%s", env, file + 1); else snprintf((char*)_file, 1000, "%s", file); } else { snprintf((char*)_file, 1000, "%s", file); } fd = open((char*)&_file,O_RDONLY); if(fd < 0) return NULL; x = malloc(sizeof(void *)); *x = NULL; /* pointer to NULL */ p = XML_ParserCreate(NULL); XML_SetUserData(p, x); XML_SetElementHandler(p, _xode_expat_startElement, _xode_expat_endElement); XML_SetCharacterDataHandler(p, _xode_expat_charData); do{ len = read(fd, buf, BUFSIZ); done = len < BUFSIZ; if(!XML_Parse(p, buf, len, done)) { /* jdebug(ZONE,"xmlnode_file_parseerror: %s",(char *)XML_ErrorString(XML_GetErrorCode(p)));*/ xode_free(*x); *x = NULL; done = 1; } }while(!done); node = *x; XML_ParserFree(p); free(x); close(fd); return node; /* return the xmlnode x points to */ } int xode_to_file(char *file, xode node) { char *doc; int fd, i; char _file[1000]; if(file == NULL || node == NULL) return -1; /* perform tilde expansion */ if(*file == '~') { char *env = getenv("HOME"); if(env != NULL) snprintf((char*)_file, 1000, "%s%s", env, file + 1); else snprintf((char*)_file, 1000, "%s", file); } else { snprintf((char*)_file, 1000, "%s", file); } fd = open((char*)&_file, O_CREAT | O_WRONLY | O_TRUNC, 0600); if(fd < 0) return -1; doc = xode_to_str(node); i = write(fd,doc,strlen(doc)); if(i < 0) return -1; close(fd); return 1; } kamailio-4.0.4/obsolete/jabber_k/xjab_dmsg.h0000644000000000000000000000546612223032460017472 0ustar rootroot/* * $Id$ * * eXtended JABber module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*** error and info messages ***/ #ifndef _XJAB_DMSG_H_ #define _XJAB_DMSG_H_ #define XJ_DMSG_INF_DISCONNECTED "INFO: Connection to Jabber server lost. You have to login to Jabber server again (join again the conferences that you were participating, too)." #define XJ_DMSG_ERR_SENDIM "ERROR: Your message was not sent. Connection to IM network failed." #define XJ_DMSG_ERR_NOTJCONF "ERROR: Your message was not sent. You are not joined in the conference. Please join the room before sending messages." #define XJ_DMSG_INF_JCONFEXIT "INFO: You have just left the conference." #define XJ_DMSG_ERR_JGWFORB "ERROR: Your message was not sent. You do not have permission to use the gateway." #define XJ_DMSG_ERR_NOJSRV "ERROR: Your message was not sent. Cannot connect to Jabber server." #define XJ_DMSG_ERR_JAUTH "ERROR: Your message was not sent. Authentication to Jabber server failed." #define XJ_DMSG_ERR_JGWFULL "ERROR: Your message was not sent. SIP-2-JABBER gateway is full." #define XJ_DMSG_ERR_JOINJCONF "ERROR: Cannot join the conference room." #define XJ_DMSG_ERR_NEWJCONF "ERROR:Cannot create a new conference session." #define XJ_DMSG_ERR_SENDJMSG "ERROR: Your message was not sent. Something wrong during transmitting to Jabber network." #define XJ_DMSG_ERR_STOREJMSG "ERROR: Your message was not sent. Something wrong while trying to transmit it to Jabber network." #define XJ_DMSG_ERR_NOREGIM "ERROR: Your message was not sent. You are not registered with this IM gateway." #define XJ_DMSG_ERR_DISCONNECTED "ERROR: Connection to Jabber server lost. You have to login to Jabber server again (join again the conferences that you were participating, too)." #define XJ_DMSG_INF_JOFFLINE "INFO: Your are now offline in Jabber network. Thank you for using SIP-Jabber gateway." #define XJ_DMSG_ERR_JCONFNICK "ERROR: Your nickname already exists in the conference room. Please choose a new one." #define XJ_DMSG_ERR_JCONFREFUSED "ERROR: Your participation to the conference room was refused." #endif kamailio-4.0.4/obsolete/jabber_k/xsnprintf.c0000644000000000000000000006453312223032460017562 0ustar rootroot/* ==================================================================== * $Id$ * * Copyright (c) 1995-1998 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * 4. The names "Apache Server" and "Apache Group" must not be used to * endorse or promote products derived from this software without * prior written permission. * * 5. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Group and was originally based * on public domain software written at the National Center for * Supercomputing Applications, University of Illinois, Urbana-Champaign. * For more information on the Apache Group and the Apache HTTP server * project, please see . * * This code is based on, and used with the permission of, the * SIO stdio-replacement strx_* functions by Panos Tsirigotis * for xinetd. */ #include "xode.h" #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) #include #include #include #include #include #include #include #ifdef HAVE_GCVT #define ap_ecvt ecvt #define ap_fcvt fcvt #define ap_gcvt gcvt #else /* * cvt.c - IEEE floating point formatting routines for FreeBSD * from GNU libc-4.6.27 */ /* * ap_ecvt converts to decimal * the number of digits is specified by ndigit * decpt is set to the position of the decimal point * sign is set to 0 for positive, 1 for negative */ #define NDIG 80 static char * ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag) { register int r2; double fi, fj; register char *p, *p1; static char buf[NDIG]; if (ndigits >= NDIG - 1) ndigits = NDIG - 2; r2 = 0; *sign = 0; p = &buf[0]; if (arg < 0) { *sign = 1; arg = -arg; } arg = modf(arg, &fi); p1 = &buf[NDIG]; /* * Do integer part */ if (fi != 0) { p1 = &buf[NDIG]; while (fi != 0) { fj = modf(fi / 10, &fi); *--p1 = (int) ((fj + .03) * 10) + '0'; r2++; } while (p1 < &buf[NDIG]) *p++ = *p1++; } else if (arg > 0) { while ((fj = arg * 10) < 1) { arg = fj; r2--; } } p1 = &buf[ndigits]; if (eflag == 0) p1 += r2; *decpt = r2; if (p1 < &buf[0]) { buf[0] = '\0'; return (buf); } while (p <= p1 && p < &buf[NDIG]) { arg *= 10; arg = modf(arg, &fj); *p++ = (int) fj + '0'; } if (p1 >= &buf[NDIG]) { buf[NDIG - 1] = '\0'; return (buf); } p = p1; *p1 += 5; while (*p1 > '9') { *p1 = '0'; if (p1 > buf) ++ * --p1; else { *p1 = '1'; (*decpt)++; if (eflag == 0) { if (p > buf) *p = '0'; p++; } } } *p = '\0'; return (buf); } static char * ap_ecvt(double arg, int ndigits, int *decpt, int *sign) { return (ap_cvt(arg, ndigits, decpt, sign, 1)); } static char * ap_fcvt(double arg, int ndigits, int *decpt, int *sign) { return (ap_cvt(arg, ndigits, decpt, sign, 0)); } /* * ap_gcvt - Floating output conversion to * minimal length string */ static char * ap_gcvt(double number, int ndigit, char *buf) { int sign, decpt; register char *p1, *p2; int i; p1 = ap_ecvt(number, ndigit, &decpt, &sign); p2 = buf; if (sign) *p2++ = '-'; for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--) ndigit--; if ((decpt >= 0 && decpt - ndigit > 4) || (decpt < 0 && decpt < -3)) { /* use E-style */ decpt--; *p2++ = *p1++; *p2++ = '.'; for (i = 1; i < ndigit; i++) *p2++ = *p1++; *p2++ = 'e'; if (decpt < 0) { decpt = -decpt; *p2++ = '-'; } else *p2++ = '+'; if (decpt / 100 > 0) *p2++ = decpt / 100 + '0'; if (decpt / 10 > 0) *p2++ = (decpt % 100) / 10 + '0'; *p2++ = decpt % 10 + '0'; } else { if (decpt <= 0) { if (*p1 != '0') *p2++ = '.'; while (decpt < 0) { decpt++; *p2++ = '0'; } } for (i = 1; i <= ndigit; i++) { *p2++ = *p1++; if (i == decpt) *p2++ = '.'; } if (ndigit < decpt) { while (ndigit++ < decpt) *p2++ = '0'; *p2++ = '.'; } } if (p2[-1] == '.') p2--; *p2 = '\0'; return (buf); } #endif /* HAVE_CVT */ typedef enum { NO = 0, YES = 1 } boolean_e; #define FALSE 0 #define TRUE 1 #define NUL '\0' #define INT_NULL ((int *)0) #define WIDE_INT long typedef WIDE_INT wide_int; typedef unsigned WIDE_INT u_wide_int; typedef int bool_int; #define S_NULL "(null)" #define S_NULL_LEN 6 #define FLOAT_DIGITS 6 #define EXPONENT_LENGTH 10 /* * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions * * XXX: this is a magic number; do not decrease it */ #define NUM_BUF_SIZE 512 /* * Descriptor for buffer area */ struct buf_area { char *buf_end; char *nextb; /* pointer to next byte to read/write */ }; typedef struct buf_area buffy; /* * The INS_CHAR macro inserts a character in the buffer and writes * the buffer back to disk if necessary * It uses the char pointers sp and bep: * sp points to the next available character in the buffer * bep points to the end-of-buffer+1 * While using this macro, note that the nextb pointer is NOT updated. * * NOTE: Evaluation of the c argument should not have any side-effects */ #define INS_CHAR( c, sp, bep, cc ) \ { \ if ( sp < bep ) \ { \ *sp++ = c ; \ cc++ ; \ } \ } #define NUM( c ) ( c - '0' ) #define STR_TO_DEC( str, num ) \ num = NUM( *str++ ) ; \ while ( isdigit((int)*str ) ) \ { \ num *= 10 ; \ num += NUM( *str++ ) ; \ } /* * This macro does zero padding so that the precision * requirement is satisfied. The padding is done by * adding '0's to the left of the string that is going * to be printed. */ #define FIX_PRECISION( adjust, precision, s, s_len ) \ if ( adjust ) \ while ( s_len < precision ) \ { \ *--s = '0' ; \ s_len++ ; \ } /* * Macro that does padding. The padding is done by printing * the character ch. */ #define PAD( width, len, ch ) do \ { \ INS_CHAR( ch, sp, bep, cc ) ; \ width-- ; \ } \ while ( width > len ) /* * Prefix the character ch to the string str * Increase length * Set the has_prefix flag */ #define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES /* * Convert num to its decimal format. * Return value: * - a pointer to a string containing the number (no sign) * - len contains the length of the string * - is_negative is set to TRUE or FALSE depending on the sign * of the number (always set to FALSE if is_unsigned is TRUE) * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) */ static char * conv_10(register wide_int num, register bool_int is_unsigned, register bool_int * is_negative, char *buf_end, register int *len) { register char *p = buf_end; register u_wide_int magnitude; if (is_unsigned) { magnitude = (u_wide_int) num; *is_negative = FALSE; } else { *is_negative = (num < 0); /* * On a 2's complement machine, negating the most negative integer * results in a number that cannot be represented as a signed integer. * Here is what we do to obtain the number's magnitude: * a. add 1 to the number * b. negate it (becomes positive) * c. convert it to unsigned * d. add 1 */ if (*is_negative) { wide_int t = num + 1; magnitude = ((u_wide_int) - t) + 1; } else magnitude = (u_wide_int) num; } /* * We use a do-while loop so that we write at least 1 digit */ do { register u_wide_int new_magnitude = magnitude / 10; *--p = magnitude - new_magnitude * 10 + '0'; magnitude = new_magnitude; } while (magnitude); *len = buf_end - p; return (p); } /* * Convert a floating point number to a string formats 'f', 'e' or 'E'. * The result is placed in buf, and len denotes the length of the string * The sign is returned in the is_negative argument (and is not placed * in buf). */ static char * conv_fp(register char format, register double num, boolean_e add_dp, int precision, bool_int * is_negative, char *buf, int *len) { register char *s = buf; register char *p; int decimal_point; if (format == 'f') p = ap_fcvt(num, precision, &decimal_point, is_negative); else /* either e or E format */ p = ap_ecvt(num, precision + 1, &decimal_point, is_negative); /* * Check for Infinity and NaN */ if (isalpha((int)*p)) { *len = strlen(strcpy(buf, p)); *is_negative = FALSE; return (buf); } if (format == 'f') { if (decimal_point <= 0) { *s++ = '0'; if (precision > 0) { *s++ = '.'; while (decimal_point++ < 0) *s++ = '0'; } else if (add_dp) { *s++ = '.'; } } else { while (decimal_point-- > 0) { *s++ = *p++; } if (precision > 0 || add_dp) { *s++ = '.'; } } } else { *s++ = *p++; if (precision > 0 || add_dp) *s++ = '.'; } /* * copy the rest of p, the NUL is NOT copied */ while (*p) *s++ = *p++; if (format != 'f') { char temp[EXPONENT_LENGTH]; /* for exponent conversion */ int t_len; bool_int exponent_is_negative; *s++ = format; /* either e or E */ decimal_point--; if (decimal_point != 0) { p = conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative, &temp[EXPONENT_LENGTH], &t_len); *s++ = exponent_is_negative ? '-' : '+'; /* * Make sure the exponent has at least 2 digits */ if (t_len == 1) *s++ = '0'; while (t_len--) *s++ = *p++; } else { *s++ = '+'; *s++ = '0'; *s++ = '0'; } } *len = s - buf; return (buf); } /* * Convert num to a base X number where X is a power of 2. nbits determines X. * For example, if nbits is 3, we do base 8 conversion * Return value: * a pointer to a string containing the number * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) */ static char * conv_p2(register u_wide_int num, register int nbits, char format, char *buf_end, register int *len) { register int mask = (1 << nbits) - 1; register char *p = buf_end; static char low_digits[] = "0123456789abcdef"; static char upper_digits[] = "0123456789ABCDEF"; register char *digits = (format == 'X') ? upper_digits : low_digits; do { *--p = digits[num & mask]; num >>= nbits; } while (num); *len = buf_end - p; return (p); } /* * Do format conversion placing the output in buffer */ static int format_converter(register buffy * odp, const char *fmt, va_list ap) { register char *sp; register char *bep; register int cc = 0; register int i; register char *s = NULL; char *q; int s_len; register int min_width = 0; int precision = 0; enum { LEFT, RIGHT } adjust; char pad_char; char prefix_char; double fp_num; wide_int i_num = (wide_int) 0; u_wide_int ui_num; char num_buf[NUM_BUF_SIZE]; char char_buf[2]; /* for printing %% and % */ /* * Flag variables */ boolean_e is_long; boolean_e alternate_form; boolean_e print_sign; boolean_e print_blank; boolean_e adjust_precision; boolean_e adjust_width; bool_int is_negative; s_len = 0; sp = odp->nextb; bep = odp->buf_end; while (*fmt) { if (*fmt != '%') { INS_CHAR(*fmt, sp, bep, cc); } else { /* * Default variable settings */ adjust = RIGHT; alternate_form = print_sign = print_blank = NO; pad_char = ' '; prefix_char = NUL; fmt++; /* * Try to avoid checking for flags, width or precision */ if (isascii((int)*fmt) && !islower((int)*fmt)) { /* * Recognize flags: -, #, BLANK, + */ for (;; fmt++) { if (*fmt == '-') adjust = LEFT; else if (*fmt == '+') print_sign = YES; else if (*fmt == '#') alternate_form = YES; else if (*fmt == ' ') print_blank = YES; else if (*fmt == '0') pad_char = '0'; else break; } /* * Check if a width was specified */ if (isdigit((int)*fmt)) { STR_TO_DEC(fmt, min_width); adjust_width = YES; } else if (*fmt == '*') { min_width = va_arg(ap, int); fmt++; adjust_width = YES; if (min_width < 0) { adjust = LEFT; min_width = -min_width; } } else adjust_width = NO; /* * Check if a precision was specified * * XXX: an unreasonable amount of precision may be specified * resulting in overflow of num_buf. Currently we * ignore this possibility. */ if (*fmt == '.') { adjust_precision = YES; fmt++; if (isdigit((int)*fmt)) { STR_TO_DEC(fmt, precision); } else if (*fmt == '*') { precision = va_arg(ap, int); fmt++; if (precision < 0) precision = 0; } else precision = 0; } else adjust_precision = NO; } else adjust_precision = adjust_width = NO; /* * Modifier check */ if (*fmt == 'l') { is_long = YES; fmt++; } else is_long = NO; /* * Argument extraction and printing. * First we determine the argument type. * Then, we convert the argument to a string. * On exit from the switch, s points to the string that * must be printed, s_len has the length of the string * The precision requirements, if any, are reflected in s_len. * * NOTE: pad_char may be set to '0' because of the 0 flag. * It is reset to ' ' by non-numeric formats */ switch (*fmt) { case 'u': if (is_long) i_num = va_arg(ap, u_wide_int); else i_num = (wide_int) va_arg(ap, unsigned int); /* * The rest also applies to other integer formats, so fall * into that case. */ case 'd': case 'i': /* * Get the arg if we haven't already. */ if ((*fmt) != 'u') { if (is_long) i_num = va_arg(ap, wide_int); else i_num = (wide_int) va_arg(ap, int); }; s = conv_10(i_num, (*fmt) == 'u', &is_negative, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); if (*fmt != 'u') { if (is_negative) prefix_char = '-'; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; } break; case 'o': if (is_long) ui_num = va_arg(ap, u_wide_int); else ui_num = (u_wide_int) va_arg(ap, unsigned int); s = conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); if (alternate_form && *s != '0') { *--s = '0'; s_len++; } break; case 'x': case 'X': if (is_long) ui_num = (u_wide_int) va_arg(ap, u_wide_int); else ui_num = (u_wide_int) va_arg(ap, unsigned int); s = conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); if (alternate_form && i_num != 0) { *--s = *fmt; /* 'x' or 'X' */ *--s = '0'; s_len += 2; } break; case 's': s = va_arg(ap, char *); if (s != NULL) { s_len = strlen(s); if (adjust_precision && precision < s_len) s_len = precision; } else { s = S_NULL; s_len = S_NULL_LEN; } pad_char = ' '; break; case 'f': case 'e': case 'E': fp_num = va_arg(ap, double); s = conv_fp(*fmt, fp_num, alternate_form, (adjust_precision == NO) ? FLOAT_DIGITS : precision, &is_negative, &num_buf[1], &s_len); if (is_negative) prefix_char = '-'; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; break; case 'g': case 'G': if (adjust_precision == NO) precision = FLOAT_DIGITS; else if (precision == 0) precision = 1; /* * * We use &num_buf[ 1 ], so that we have room for the sign */ s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1]); if (*s == '-') prefix_char = *s++; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; s_len = strlen(s); if (alternate_form && (q = strchr(s, '.')) == NULL) s[s_len++] = '.'; if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL) *q = 'E'; break; case 'c': char_buf[0] = (char) (va_arg(ap, int)); s = &char_buf[0]; s_len = 1; pad_char = ' '; break; case '%': char_buf[0] = '%'; s = &char_buf[0]; s_len = 1; pad_char = ' '; break; case 'n': *(va_arg(ap, int *)) = cc; break; /* * Always extract the argument as a "char *" pointer. We * should be using "void *" but there are still machines * that don't understand it. * If the pointer size is equal to the size of an unsigned * integer we convert the pointer to a hex number, otherwise * we print "%p" to indicate that we don't handle "%p". */ case 'p': ui_num = (u_wide_int) va_arg(ap, char *); if (sizeof(char *) <= sizeof(u_wide_int)) s = conv_p2(ui_num, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len); else { s = "%p"; s_len = 2; } pad_char = ' '; break; case NUL: /* * The last character of the format string was %. * We ignore it. */ continue; /* * The default case is for unrecognized %'s. * We print % to help the user identify what * option is not understood. * This is also useful in case the user wants to pass * the output of format_converter to another function * that understands some other % (like syslog). * Note that we can't point s inside fmt because the * unknown could be preceded by width etc. */ default: char_buf[0] = '%'; char_buf[1] = *fmt; s = char_buf; s_len = 2; pad_char = ' '; break; } if (prefix_char != NUL) { *--s = prefix_char; s_len++; } if (adjust_width && adjust == RIGHT && min_width > s_len) { if (pad_char == '0' && prefix_char != NUL) { INS_CHAR(*s, sp, bep, cc) s++; s_len--; min_width--; } PAD(min_width, s_len, pad_char); } /* * Print the string s. */ for (i = s_len; i != 0; i--) { INS_CHAR(*s, sp, bep, cc); s++; } if (adjust_width && adjust == LEFT && min_width > s_len) PAD(min_width, s_len, pad_char); } fmt++; } odp->nextb = sp; return (cc); } /* * This is the general purpose conversion function. */ static void strx_printv(int *ccp, char *buf, size_t len, const char *format, va_list ap) { buffy od; int cc; /* * First initialize the descriptor * Notice that if no length is given, we initialize buf_end to the * highest possible address. */ od.buf_end = len ? &buf[len] : (char *) ~0; od.nextb = buf; /* * Do the conversion */ cc = format_converter(&od, format, ap); if (len == 0 || od.nextb <= od.buf_end) *(od.nextb) = '\0'; if (ccp) *ccp = cc; } int ap_snprintf(char *buf, size_t len, const char *format,...) { int cc; va_list ap; va_start(ap, format); strx_printv(&cc, buf, (len - 1), format, ap); va_end(ap); return (cc); } int ap_vsnprintf(char *buf, size_t len, const char *format, va_list ap) { int cc; strx_printv(&cc, buf, (len - 1), format, ap); return (cc); } #endif /* HAVE_SNPRINTF */ kamailio-4.0.4/obsolete/jabber_k/xjab_util.c0000644000000000000000000001406312223032460017501 0ustar rootroot/* * $Id$ * * eXtended JABber module - Jabber connections pool * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "../../dprint.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../timer.h" #include "xjab_util.h" #include "xjab_jcon.h" #include "mdefines.h" /** * init a jc_pool structure * - size : maximum number of the open connection to Jabber * - jlen : maximum size of messages queue * return : pointer to the structure or NULL on error */ xj_jcon_pool xj_jcon_pool_init(int size, int jlen, int ch) { xj_jcon_pool jcp = (xj_jcon_pool)_M_MALLOC(sizeof(t_xj_jcon_pool)); if(jcp == NULL) return NULL; jcp->len = size; jcp->ojc = (xj_jcon*)_M_MALLOC(size*sizeof(xj_jcon)); if(jcp->ojc == NULL) { _M_FREE(jcp); return NULL; } memset( jcp->ojc , 0, size*sizeof(xj_jcon) ); jcp->jmqueue.len = jlen; jcp->jmqueue.size = 0; jcp->jmqueue.cache = (ch>0)?ch:90; jcp->jmqueue.expire = (int*)_M_MALLOC(jlen*sizeof(int)); if(jcp->jmqueue.expire == NULL) { _M_FREE(jcp->ojc); _M_FREE(jcp); return NULL; } jcp->jmqueue.jsm=(xj_sipmsg*)_M_MALLOC(jlen*sizeof(xj_sipmsg)); if(jcp->jmqueue.jsm == NULL) { _M_FREE(jcp->jmqueue.expire); _M_FREE(jcp->ojc); _M_FREE(jcp); return NULL; } jcp->jmqueue.ojc = (xj_jcon*)_M_MALLOC(jlen*sizeof(xj_jcon)); if(jcp->jmqueue.ojc == NULL) { _M_FREE(jcp->jmqueue.expire); _M_FREE(jcp->jmqueue.jsm); _M_FREE(jcp->ojc); _M_FREE(jcp); return NULL; } memset( jcp->jmqueue.expire , 0, jlen*sizeof(int) ); memset( jcp->jmqueue.jsm , 0, jlen*sizeof(xj_sipmsg) ); memset( jcp->jmqueue.ojc , 0, jlen*sizeof(xj_jcon) ); return jcp; } /** * add a new element in messages queue * - jcp : pointer to the Jabber connections pool structure * - _jsm : pointer to the message * - _ojc : pointer to the Jabber connection that will be used for this message * return : 0 on success or <0 on error */ int xj_jcon_pool_add_jmsg(xj_jcon_pool jcp, xj_sipmsg _jsm, xj_jcon _ojc) { int i; if(jcp == NULL) return -1; if(jcp->jmqueue.size == jcp->jmqueue.len) return -2; #ifdef XJ_EXTRA_DEBUG LM_DBG("add msg into the pool\n"); #endif for(i = 0; ijmqueue.len; i++) { if(jcp->jmqueue.jsm[i] == NULL || jcp->jmqueue.ojc[i] == NULL) { jcp->jmqueue.size++; jcp->jmqueue.expire[i] = get_ticks() + jcp->jmqueue.cache; jcp->jmqueue.jsm[i] = _jsm; jcp->jmqueue.ojc[i] = _ojc; return 0; } } return -2; } /** * delete first element from messages queue * - jcp : pointer to the Jabber connections pool structure * return : 0 on success or <0 on error */ int xj_jcon_pool_del_jmsg(xj_jcon_pool jcp, int idx) { if(jcp == NULL) return -1; if(jcp->jmqueue.size <= 0) return -2; jcp->jmqueue.size--; jcp->jmqueue.jsm[idx] = NULL; jcp->jmqueue.ojc[idx] = NULL; return 0; } /** * add a new connection in pool * - jcp : pointer to the Jabber connections pool structure * return : 0 on success or <0 on error */ int xj_jcon_pool_add(xj_jcon_pool jcp, xj_jcon jc) { int i = 0; if(jcp == NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("add connection into the pool\n"); #endif while(i < jcp->len && jcp->ojc[i] != NULL) i++; if(i >= jcp->len) return -1; jcp->ojc[i] = jc; return 0; } /** * get the jabber connection associated with 'id' * - jcp : pointer to the Jabber connections pool structure * - id : id of the Jabber connection * return : pointer to the open connection to Jabber structure or NULL on error */ xj_jcon xj_jcon_pool_get(xj_jcon_pool jcp, xj_jkey jkey) { int i = 0; xj_jcon _ojc; if(jcp==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return NULL; #ifdef XJ_EXTRA_DEBUG LM_DBG("looking for the connection of <%.*s>" " into the pool\n", jkey->id->len, jkey->id->s); #endif while(i < jcp->len) { if((jcp->ojc[i]!=NULL) && jcp->ojc[i]->jkey->hash==jkey->hash && (!strncmp(jcp->ojc[i]->jkey->id->s, jkey->id->s, jkey->id->len))) { _ojc = jcp->ojc[i]; //jcp->ojc[i] = NULL; return _ojc; } i++; } return NULL; } /** * remove the connection associated with 'id' from pool * - jcp : pointer to the Jabber connections pool structure * - id : id of the Jabber connection * return : 0 on success or <0 on error */ int xj_jcon_pool_del(xj_jcon_pool jcp, xj_jkey jkey) { int i = 0; if(jcp==NULL || jkey==NULL || jkey->id==NULL || jkey->id->s==NULL) return -1; #ifdef XJ_EXTRA_DEBUG LM_DBG("removing a connection from the pool\n"); #endif while(i < jcp->len) { if((jcp->ojc[i]!=NULL) && jcp->ojc[i]->jkey->hash==jkey->hash && (!strncmp(jcp->ojc[i]->jkey->id->s,jkey->id->s,jkey->id->len))) { xj_jcon_free(jcp->ojc[i]); jcp->ojc[i] = NULL; break; } i++; } return 0; } /** * free a Jabber connections pool structure * - jcp : pointer to the Jabber connections pool structure */ void xj_jcon_pool_free(xj_jcon_pool jcp) { int i; if(jcp == NULL) return; #ifdef XJ_EXTRA_DEBUG LM_DBG("-----START-----\n"); #endif if(jcp->ojc != NULL) { for(i=0; ilen; i++) { if(jcp->ojc[i] != NULL) xj_jcon_free(jcp->ojc[i]); } _M_FREE(jcp->ojc); } if(jcp->jmqueue.jsm != NULL) _M_FREE(jcp->jmqueue.jsm); if(jcp->jmqueue.ojc != NULL) _M_FREE(jcp->jmqueue.ojc); if(jcp->jmqueue.expire != NULL) _M_FREE(jcp->jmqueue.expire); _M_FREE(jcp); } kamailio-4.0.4/obsolete/jabber_k/Makefile0000644000000000000000000000066312223032460017015 0ustar rootroot# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=jabber.so # extra debug messages DEFS+=-DXJ_EXTRA_DEBUG # -DHAVE_IHTTP # expat.h location DEFS+=-I$(LOCALBASE)/include LIBS=-L$(LOCALBASE)/lib -lexpat DEFS+=-DKAMAILIO_MOD_INTERFACE SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1 include ../../Makefile.modules kamailio-4.0.4/obsolete/jabber_k/mdefines.h0000644000000000000000000000270412223032460017316 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*************************************************************************** mdefines.h - description ------------------- author : Daniel-Constantin MIERLA email : mierla@fokus.fhg.de organization : FhI FOKUS, BERLIN ***************************************************************************/ #ifndef _mdefines_h_ #define _mdefines_h_ #define _M_PRINTF printf #define _M_CALLOC calloc #define _M_REALLOC realloc #define _M_MALLOC pkg_malloc #define _M_FREE pkg_free #define _M_SHM_MALLOC shm_malloc #define _M_SHM_FREE shm_free #endif kamailio-4.0.4/obsolete/diversion/0000755000000000000000000000000012223032460015613 5ustar rootrootkamailio-4.0.4/obsolete/diversion/README0000644000000000000000000001305212223032460016474 0ustar rootroot Diversion Module Jan Janak FhG FOKUS Edited by Jan Janak Copyright © 2004 FhG FOKUS _________________________________________________________ Table of Contents 1. User's Guide 1.1. Overview 1.2. Dependencies 1.2.1. SER Modules 1.2.2. External Libraries or Applications 1.3. Exported Parameters 1.3.1. suffix (string) 1.4. Exported Functions 1.4.1. add_diversion(reason) 1.4.2. Diversion Example 2. Developer's Guide 3. Frequently Asked Questions List of Examples 1-1. suffix usage 1-2. add_diversion usage _________________________________________________________ Chapter 1. User's Guide 1.1. Overview The module implements the Diversion extensions as per draft-levy-sip-diversion-08. The diversion extensions are useful in various scenarios involving call forwarding. Typically one needs to communicate the original recipient of the call to the PSTN gateway and this is what the diversion extensions can be used for. _________________________________________________________ 1.2. Dependencies 1.2.1. SER Modules None. _________________________________________________________ 1.2.2. External Libraries or Applications The following libraries or applications must be installed before running SER with this module loaded: * None. _________________________________________________________ 1.3. Exported Parameters 1.3.1. suffix (string) The suffix to be appended to the end of the header field. You can use the parameter to specify additional parameters to be added to the header field, see the example. Default value is "" (empty string). Example 1-1. suffix usage modparam("diversion", "suffix", ";privacy=full") _________________________________________________________ 1.4. Exported Functions 1.4.1. add_diversion(reason) The function adds a new diversion header field before any other existing Diversion header field in the message (the newly added Diversion header field will become the topmost Diversion header field). The inbound (without any modifications done by the proxy server) Request-URI will be used as the Diversion URI. Meaning of the parameters is as follows: * reason - The reason string to be added as the reason parameter Example 1-2. add_diversion usage ... add_diversion("user-busy"); ... _________________________________________________________ 1.4.2. Diversion Example The following example shows a Diversion header field added to INVITE message. The INVITE message was diverted by the user agent of sip:jiri@iptel.org because the user was talking to someone else and the new destination is sip:jan@iptel.org. INVITE sip:jan@iptel.org SIP/2.0 From: "5060" ;tag=ldgheoihege To: "Jan Janak" Call-ID: adgasdkgjhkjha@1.2.3.4 CSeq: 3 INVITE Diversion: ;reason=user-busy Via: SIP/2.0/UDP 1.2.3.4:5060 Contact: Content-Length: 0 _________________________________________________________ Chapter 2. Developer's Guide According to the specification new Diversion header field should be inserted as the topmost Diversion header field in the message, that means before any other existing Diversion header field in the message. In addition to that, add_diversion function can be called several times and each time it should insert the new Diversion header field as the topmost one. In order to implement this, add_diversion function creates the anchor in data_lump lists as a static variable to ensure that the next call of the function will use the same anchor and would insert new Diversion headers before the one created in the previous execution. To my knowledge this is the only way of inserting the diversion header field before any other created in previous runs of the function. The anchor kept this way is only valid for a single message and we have to invalidate it when another message is being processed. For this reason, the function also stores the id of the message in another static variable and compares the value of that variable with the id of the SIP message being processed. If they differ then the anchor will be invalidated and the function creates a new one. The following code snippet shows the code that invalidates the anchor, new anchor will be created when the anchor variable is set to 0. static inline int add_diversion_helper(struct sip_msg* msg, str* s) { static struct lump* anchor = 0; static int msg_id = 0; if (msg_id != msg->id) { msg_id = msg->id; anchor = 0; } ... } _________________________________________________________ Chapter 3. Frequently Asked Questions 3.1. Where can I find more about SER? 3.2. Where can I post a question about this module? 3.3. How can I report a bug? 3.1. Where can I find more about SER? Take a look at http://iptel.org/ser. 3.2. Where can I post a question about this module? First at all check if your question was already answered on one of our mailing lists: * http://mail.iptel.org/mailman/listinfo/serusers * http://mail.iptel.org/mailman/listinfo/serdev E-mails regarding any stable version should be sent to and e-mail regarding development versions or CVS snapshots should be send to . 3.3. How can I report a bug? Please follow the guidelines provided at: http://iptel.org/ser/bugs kamailio-4.0.4/obsolete/diversion/diversion.c0000644000000000000000000001010312223032460017754 0ustar rootroot/* * $Id$ * * Diversion Header Field Support * * Copyright (C) 2004 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include "../../sr_module.h" #include "../../error.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../data_lump.h" MODULE_VERSION #define DIVERSION_HF "Diversion" #define DIVERSION_HF_LEN (sizeof(DIVERSION_HF) - 1) #define DIVERSION_PREFIX DIVERSION_HF ": <" #define DIVERSION_PREFIX_LEN (sizeof(DIVERSION_PREFIX) - 1) #define DIVERSION_SUFFIX ">;reason=" #define DIVERSION_SUFFIX_LEN (sizeof(DIVERSION_SUFFIX) - 1) str suffix = STR_STATIC_INIT(""); int add_diversion(struct sip_msg* msg, char* r, char* s); /* * Module initialization function prototype */ static int mod_init(void); /* * Exported functions */ static cmd_export_t cmds[] = { {"add_diversion", add_diversion, 1, fixup_var_str_1, REQUEST_ROUTE | FAILURE_ROUTE}, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"suffix", PARAM_STR, &suffix}, {0, 0, 0} }; /* * Module interface */ struct module_exports exports = { "diversion", cmds, /* Exported functions */ 0, /* RPC methods */ params, /* Exported parameters */ mod_init, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0, /* oncancel function */ 0 /* child initialization function */ }; static int mod_init(void) { return 0; } static inline int add_diversion_helper(struct sip_msg* msg, str* s) { char *ptr; static struct lump* anchor = 0; static int msg_id = 0; if (msg_id != msg->id) { msg_id = msg->id; anchor = 0; } if (!msg->diversion && parse_headers(msg, HDR_DIVERSION_F, 0) == -1) { LOG(L_ERR, "add_diversion_helper: Header parsing failed\n"); return -1; } if (msg->diversion) { /* Insert just before the topmost Diversion header */ ptr = msg->diversion->name.s; } else { /* Insert at the end */ ptr = msg->unparsed; } if (!anchor) { anchor = anchor_lump(msg, ptr - msg->buf, 0, 0); if (!anchor) { LOG(L_ERR, "add_diversion_helper: Can't get anchor\n"); return -2; } } if (!insert_new_lump_before(anchor, s->s, s->len, 0)) { LOG(L_ERR, "add_diversion_helper: Can't insert lump\n"); return -3; } return 0; } int add_diversion(struct sip_msg* msg, char* r, char* s) { str div_hf; char *at; str* uri; str reason; if (get_str_fparam(&reason, msg, (fparam_t*)r) < 0) return -1; uri = &msg->first_line.u.request.uri; div_hf.len = DIVERSION_PREFIX_LEN + uri->len + DIVERSION_SUFFIX_LEN + reason.len + CRLF_LEN; div_hf.s = pkg_malloc(div_hf.len); if (!div_hf.s) { LOG(L_ERR, "add_diversion: No memory left\n"); return -1; } at = div_hf.s; memcpy(at, DIVERSION_PREFIX, DIVERSION_PREFIX_LEN); at += DIVERSION_PREFIX_LEN; memcpy(at, uri->s, uri->len); at += uri->len; memcpy(at, DIVERSION_SUFFIX, DIVERSION_SUFFIX_LEN); at += DIVERSION_SUFFIX_LEN; memcpy(at, reason.s, reason.len); at += reason.len; memcpy(at, CRLF, CRLF_LEN); if (add_diversion_helper(msg, &div_hf) < 0) { pkg_free(div_hf.s); return -1; } return 1; } kamailio-4.0.4/obsolete/diversion/Makefile0000644000000000000000000000036712223032460017261 0ustar rootroot# $Id$ # # Diversion Header Field Support2 # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=diversion.so LIBS= DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/auth_radius/0000755000000000000000000000000012223032460016121 5ustar rootrootkamailio-4.0.4/obsolete/auth_radius/authorize.c0000644000000000000000000001604112223032460020301 0ustar rootroot/* * $Id$ * * Digest Authentication - Radius support * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-03-09: Based on authorize.c from radius_auth (janakj) */ #include #include #include "../../mem/mem.h" #include "../../str.h" #include "../../sr_module.h" #include "../../parser/hf.h" #include "../../parser/digest/digest.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" #include "../../dprint.h" #include "../../id.h" #include "../../ut.h" #include "../../modules/auth/api.h" #include "authorize.h" #include "sterman.h" #include "authrad_mod.h" static void attr_name_value(str* name, str* value, VALUE_PAIR* vp) { int i; for (i = 0; i < vp->lvalue; i++) { if (vp->strvalue[i] == ':' || vp->strvalue[i] == '=') { name->s = vp->strvalue; name->len = i; if (i == (vp->lvalue - 1)) { value->s = (char*)0; value->len = 0; } else { value->s = vp->strvalue + i + 1; value->len = vp->lvalue - i - 1; } return; } } name->len = value->len = 0; name->s = value->s = (char*)0; } /* * Generate AVPs from the database result */ static int generate_avps(VALUE_PAIR* received) { int_str name, val; VALUE_PAIR *vp; vp = rc_avpair_get(received, ATTRID(attrs[A_SER_UID].v), VENDOR(attrs[A_SER_UID].v)); if (vp == NULL) { WARN("RADIUS server did not send SER-UID attribute in digest authentication reply\n"); return -1; } val.s.len = vp->lvalue; val.s.s = vp->strvalue; name.s.s = "uid"; name.s.len = 3; if (add_avp(AVP_TRACK_FROM | AVP_CLASS_USER | AVP_NAME_STR | AVP_VAL_STR, name, val) < 0) { ERR("Unable to create UID attribute\n"); return -1; } vp = received; while ((vp = rc_avpair_get(vp, ATTRID(attrs[A_SER_ATTR].v), VENDOR(attrs[A_SER_ATTR].v)))) { attr_name_value(&name.s, &val.s, vp); if (name.s.len == 0) { ERR("Missing attribute name\n"); return -1; } if (add_avp(AVP_TRACK_FROM | AVP_CLASS_USER | AVP_NAME_STR | AVP_VAL_STR, name, val) < 0) { LOG(L_ERR, "generate_avps: Unable to create a new AVP\n"); return -1; } else { DBG("generate_avps: AVP '%.*s'='%.*s' has been added\n", name.s.len, ZSW(name.s.s), val.s.len, ZSW(val.s.s)); } vp = vp->next; } return 0; } /* * Extract URI depending on the request from To or From header */ static inline int get_uri(struct sip_msg* _m, str** _uri) { if ((REQ_LINE(_m).method.len == 8) && (memcmp(REQ_LINE(_m).method.s, "REGISTER", 8) == 0)) { if (!_m->to && ((parse_headers(_m, HDR_TO_F, 0) == -1) || !_m->to)) { LOG(L_ERR, "get_uri(): To header field not found or malformed\n"); return -1; } *_uri = &(get_to(_m)->uri); } else { if (parse_from_header(_m) == -1) { LOG(L_ERR, "get_uri(): Error while parsing headers\n"); return -2; } *_uri = &(get_from(_m)->uri); } return 0; } /* * Authorize digest credentials */ static inline int authenticate(struct sip_msg* msg, str* realm, hdr_types_t hftype) { int res; auth_result_t ret; struct hdr_field* h; auth_body_t* cred; str* uri; struct sip_uri puri; str user, did; VALUE_PAIR* received; cred = 0; ret = -1; user.s = 0; received = NULL; switch(auth_api.pre_auth(msg, realm, hftype, &h, NULL)) { default: BUG("unexpected reply '%d'.\n", auth_api.pre_auth(msg, realm, hftype, &h, NULL)); #ifdef EXTRA_DEBUG abort(); #endif case NONCE_REUSED: LM_DBG("nonce reused"); ret = AUTH_NONCE_REUSED; goto end; case STALE_NONCE: LM_DBG("stale nonce\n"); ret = AUTH_STALE_NONCE; goto end; case NO_CREDENTIALS: LM_DBG("no credentials\n"); ret = AUTH_NO_CREDENTIALS; case ERROR: case BAD_CREDENTIALS: ret = -3; goto end; case NOT_AUTHENTICATED: ret = -1; goto end; case DO_AUTHENTICATION: break; case AUTHENTICATED: ret = 1; goto end; } cred = (auth_body_t*)h->parsed; if (use_did) { if (msg->REQ_METHOD == METHOD_REGISTER) { ret = get_to_did(&did, msg); } else { ret = get_from_did(&did, msg); } if (ret == 0) { did.s = DEFAULT_DID; did.len = sizeof(DEFAULT_DID) - 1; } } else { did.len = 0; did.s = 0; } if (get_uri(msg, &uri) < 0) { LOG(L_ERR, "authorize(): From/To URI not found\n"); ret = -1; goto end; } if (parse_uri(uri->s, uri->len, &puri) < 0) { LOG(L_ERR, "authorize(): Error while parsing From/To URI\n"); ret = -1; goto end; } user.s = (char *)pkg_malloc(puri.user.len); if (user.s == NULL) { LOG(L_ERR, "authorize: No memory left\n"); ret = -1; goto end; } un_escape(&(puri.user), &user); res = radius_authorize_sterman(&received, msg, &cred->digest, &msg->first_line.u.request.method, &user); if (res == 1) { switch(auth_api.post_auth(msg, h)) { case ERROR: case BAD_CREDENTIALS: ret = -2; break; case NOT_AUTHENTICATED: ret = -1; break; case AUTHENTICATED: if (generate_avps(received) < 0) { ret = -1; break; } ret = 1; break; default: ret = -1; break; } } else { ret = -1; } end: if (received) rc_avpair_free(received); if (user.s) pkg_free(user.s); if (ret < 0) { if (auth_api.build_challenge(msg, (cred ? cred->stale : 0), realm, NULL, NULL, hftype) < 0) { ERR("Error while creating challenge\n"); ret = -2; } } return ret; } /* * Authorize using Proxy-Authorize header field */ int radius_proxy_authorize(struct sip_msg* _msg, char* p1, char* p2) { str realm; if (get_str_fparam(&realm, _msg, (fparam_t*)p1) < 0) { ERR("Cannot obtain digest realm from parameter '%s'\n", ((fparam_t*)p1)->orig); return -1; } /* realm parameter is converted to str* in str_fixup */ return authenticate(_msg, &realm, HDR_PROXYAUTH_T); } /* * Authorize using WWW-Authorize header field */ int radius_www_authorize(struct sip_msg* _msg, char* p1, char* p2) { str realm; if (get_str_fparam(&realm, _msg, (fparam_t*)p1) < 0) { ERR("Cannot obtain digest realm from parameter '%s'\n", ((fparam_t*)p1)->orig); return -1; } return authenticate(_msg, &realm, HDR_AUTHORIZATION_T); } kamailio-4.0.4/obsolete/auth_radius/sterman.h0000644000000000000000000000350512223032460017746 0ustar rootroot/* * $Id$ * * Digest Authentication - Radius support * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-03-09: Based on auth_mod.h from radius_authorize (janakj) */ #ifndef STERMAN_H #define STERMAN_H #ifdef RADIUSCLIENT_NG_4 # include #else # include #endif #include "../../str.h" #include "../../parser/digest/digest_parser.h" /* * This function creates and submits radius authentication request as per * draft-sterman-aaa-sip-00.txt. In addition, _user parameter is included * in the request as value of a SER specific attribute type SIP-URI-User, * which can be be used as a check item in the request. Service type of * the request is Authenticate-Only. */ int radius_authorize_sterman(VALUE_PAIR** received, struct sip_msg* _msg, dig_cred_t* _cred, str* _method, str* _user); #endif /* STERMAN_H */ kamailio-4.0.4/obsolete/auth_radius/doc/0000755000000000000000000000000012223032460016666 5ustar rootrootkamailio-4.0.4/obsolete/auth_radius/doc/functions.xml0000644000000000000000000000735312223032460021430 0ustar rootroot
Functions
<function>radius_www_authorize(realm)</function> The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call www_challenge which will challenge the user again. This function will, in fact, perform sanity checks over the received credentials and then pass them along to the radius server which will verify the credentials and return whether they are valid or not. Meaning of the parameter is as follows: realm - Realm is a opaque string that the user agent should present to the user so he can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string "" is used then the server will generate it from the request. In case of REGISTER requests To header field domain will be used (because this header field represents a user being registered), for all other messages From header field domain will be used. <function>radius_www_authorize</function> usage ... if (!radius_www_authorize("iptel.org")) { www_challenge("iptel.org", "1"); }; ...
<function moreinfo="none">radius_proxy_authorize(realm)</function> The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call proxy_challenge which will challenge the user again. This function will, in fact, perform sanity checks over the received credentials and then pass them along to the radius server which will verify the credentials and return whether they are valid or not. Meaning of the parameter is as follows: realm - Realm is a opaque string that the user agent should present to the user so he can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string "" is used then the server will generate it from the request. From header field domain will be used as realm. proxy_authorize usage ... if (!radius_proxy_authorize("")) { proxy_challenge("", "1"); # Realm will be autogenerated }; ...
kamailio-4.0.4/obsolete/auth_radius/doc/auth_radius.xml0000644000000000000000000000451312223032460021723 0ustar rootroot
Jan Janak FhG Fokus jan@iptel.org Juha Heinanen Song Networks jh@song.fi Stelios Sidiroglou-Douskos 2002 2003 FhG FOKUS Auth_radius Module
Overview This module contains functions that are used to perform authentication using a Radius server. Basically the proxy will pass along the credentials to the radius server which will in turn send a reply containing result of the authentication. So basically the whole authentication is done in the Radius server. Before sending the request to the radius server we perform some sanity checks over the credentials to make sure that only well formed credentials will get to the server. We have implemented radius authentication according to draft-sterman-aaa-sip-00. This module requires radiusclient library version 0.5.0 or higher which is available from http://developer.berlios.de/projects/radiusclient-ng/.
Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): auth Generic authentication functions.
kamailio-4.0.4/obsolete/auth_radius/doc/Makefile0000644000000000000000000000013412223032460020324 0ustar rootrootdocs = auth_radius.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/auth_radius/doc/params.xml0000644000000000000000000000443212223032460020676 0ustar rootroot
Parameters
<varname>radius_config</varname> (string) This is the location of the configuration file of radius client libraries. Default value is "/usr/local/etc/radiusclient/radiusclient.conf". <varname>radius_config</varname> parameter usage modparam("auth_radius", "radius_config", "/etc/radiusclient.conf")
<varname>service_type</varname> (integer) This is the value of the Service-Type radius attribute to be used. The default should be fine for most people. See your radius client include files for numbers to be put in this parameter if you need to change it. Default value is "15". <varname>service_type</varname> usage modparam("auth_radius", "service_type", 15)
<varname>use_ruri_flag</varname> (integer) When this parameter is set to the value other than "-1" and the request being authenticated has flag with matching number set via setflag() function, use Request URI instead of uri parameter value from the Authorization / Proxy-Authorization header field to perform RADIUS authentication. This is intended to provide workaround for misbehaving NAT / routers / ALGs that alter request in the transit, breaking authentication. At the time of this writing, certain versions of Linksys WRT54GL are known to do that. Default value is "-1". <varname>use_ruri_flag</varname> usage modparam("auth_radius", "use_ruri_flag", 22)
kamailio-4.0.4/obsolete/auth_radius/authrad_mod.c0000644000000000000000000001276012223032460020562 0ustar rootroot/* * $Id$ * * Digest Authentication - Radius support * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-03-09: Based on auth_mod.c from radius_auth (janakj) * 2003-03-11: New module interface (janakj) * 2003-03-16: flags export parameter added (janakj) * 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei) */ #include #include #include #include "../../sr_module.h" #include "../../error.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../config.h" #include "authrad_mod.h" #include "authorize.h" #ifdef RADIUSCLIENT_NG_4 # include # else # include #endif MODULE_VERSION struct attr attrs[A_MAX]; struct val vals[V_MAX]; void *rh; auth_api_s_t auth_api; static int mod_init(void); /* Module initialization function */ int use_did = 1; int use_ruri_flag = -1; /* * Module parameter variables */ static char* radius_config = "/usr/local/etc/radiusclient/radiusclient.conf"; static int service_type = -1; /* * Exported functions */ static cmd_export_t cmds[] = { {"radius_www_authorize", radius_www_authorize, 1, fixup_var_str_1, REQUEST_ROUTE}, {"radius_proxy_authorize", radius_proxy_authorize, 1, fixup_var_str_1, REQUEST_ROUTE}, {"radius_www_authenticate", radius_www_authorize, 1, fixup_var_str_1, REQUEST_ROUTE}, {"radius_proxy_authenticate", radius_proxy_authorize, 1, fixup_var_str_1, REQUEST_ROUTE}, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"radius_config", PARAM_STRING, &radius_config }, {"service_type", PARAM_INT, &service_type }, {"use_did", PARAM_INT, &use_did }, {"use_ruri_flag", PARAM_INT, &use_ruri_flag }, {0, 0, 0} }; /* * Module interface */ struct module_exports exports = { "auth_radius", cmds, /* Exported functions */ 0, /* RPC methods */ params, /* Exported parameters */ mod_init, /* module initialization function */ 0, /* response function */ 0, /* destroy function */ 0, /* oncancel function */ 0 /* child initialization function */ }; /* * Module initialization function */ static int mod_init(void) { DICT_VENDOR *vend; bind_auth_s_t bind_auth; DBG("auth_radius - Initializing\n"); memset(attrs, 0, sizeof(attrs)); memset(vals, 0, sizeof(vals)); /* RFC2865, RFC2866 */ attrs[A_USER_NAME].n = "User-Name"; attrs[A_SERVICE_TYPE].n = "Service-Type"; /* draft-sterman-aaa-sip-00 */ attrs[A_DIGEST_RESPONSE].n = "Digest-Response"; attrs[A_DIGEST_REALM].n = "Digest-Realm"; attrs[A_DIGEST_NONCE].n = "Digest-Nonce"; attrs[A_DIGEST_METHOD].n = "Digest-Method"; attrs[A_DIGEST_URI].n = "Digest-URI"; attrs[A_DIGEST_QOP].n = "Digest-QOP"; attrs[A_DIGEST_ALGORITHM].n = "Digest-Algorithm"; attrs[A_DIGEST_BODY_DIGEST].n = "Digest-Body-Digest"; attrs[A_DIGEST_CNONCE].n = "Digest-CNonce"; attrs[A_DIGEST_NONCE_COUNT].n = "Digest-Nonce-Count"; attrs[A_DIGEST_USER_NAME].n = "Digest-User-Name"; /* SER-specific */ attrs[A_SER_URI_USER].n = "SER-Uri-User"; attrs[A_SER_ATTR].n = "SER-Attr"; attrs[A_SER_UID].n = "SER-UID"; attrs[A_SER_SERVICE_TYPE].n = "SER-Service-Type"; /* SER-Service-Type */ vals[V_DIGEST_AUTHENTICATION].n = "Digest-Authentication"; attrs[A_CISCO_AVPAIR].n = "Cisco-AVPair"; /* draft-schulzrinne-sipping-radius-accounting-00 */ vals[V_SIP_SESSION].n = "Sip-Session"; if ((rh = rc_read_config(radius_config)) == NULL) { LOG(L_ERR, "auth_radius: Error opening configuration file \n"); return -1; } if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary")) != 0) { LOG(L_ERR, "auth_radius: Error opening dictionary file \n"); return -2; } vend = rc_dict_findvend(rh, "Cisco"); if (vend == NULL) { DBG("auth_radius: No `Cisco' vendor in Radius " "dictionary\n"); attrs[A_CISCO_AVPAIR].n = NULL; } vend = rc_dict_findvend(rh, "iptelorg"); if (vend == NULL) { ERR("RADIUS dictionary is missing required vendor 'iptelorg'\n"); return -1; } bind_auth = (bind_auth_s_t)find_export("bind_auth_s", 0, 0); if (!bind_auth) { LOG(L_ERR, "auth_radius: Unable to find bind_auth function\n"); return -1; } if (bind_auth(&auth_api) < 0) { LOG(L_ERR, "auth_radius: Cannot bind to auth module\n"); return -4; } INIT_AV(rh, attrs, vals, "auth_radius", -5, -6); if (service_type != -1) { vals[V_SIP_SESSION].v = service_type; } return 0; } kamailio-4.0.4/obsolete/auth_radius/README0000644000000000000000000001246312223032460017007 0ustar rootroot1. Auth_radius Module Jan Janak FhG Fokus Juha Heinanen Song Networks Stelios Sidiroglou-Douskos Copyright © 2002, 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. Dependencies 1.3. Parameters 1.3.1. radius_config (string) 1.3.2. service_type (integer) 1.3.3. use_ruri_flag (integer) 1.4. Functions 1.4.1. radius_www_authorize(realm) 1.4.2. radius_proxy_authorize(realm) 1.1. Overview This module contains functions that are used to perform authentication using a Radius server. Basically the proxy will pass along the credentials to the radius server which will in turn send a reply containing result of the authentication. So basically the whole authentication is done in the Radius server. Before sending the request to the radius server we perform some sanity checks over the credentials to make sure that only well formed credentials will get to the server. We have implemented radius authentication according to draft-sterman-aaa-sip-00. This module requires radiusclient library version 0.5.0 or higher which is available from http://developer.berlios.de/projects/radiusclient-ng/. 1.2. Dependencies The module depends on the following modules (in the other words the listed modules must be loaded before this module): * auth. Generic authentication functions. 1.3. Parameters 1.3.1. radius_config (string) This is the location of the configuration file of radius client libraries. Default value is "/usr/local/etc/radiusclient/radiusclient.conf". Example 1. radius_config parameter usage modparam("auth_radius", "radius_config", "/etc/radiusclient.conf") 1.3.2. service_type (integer) This is the value of the Service-Type radius attribute to be used. The default should be fine for most people. See your radius client include files for numbers to be put in this parameter if you need to change it. Default value is "15". Example 2. service_type usage modparam("auth_radius", "service_type", 15) 1.3.3. use_ruri_flag (integer) When this parameter is set to the value other than "-1" and the request being authenticated has flag with matching number set via setflag() function, use Request URI instead of uri parameter value from the Authorization / Proxy-Authorization header field to perform RADIUS authentication. This is intended to provide workaround for misbehaving NAT / routers / ALGs that alter request in the transit, breaking authentication. At the time of this writing, certain versions of Linksys WRT54GL are known to do that. Default value is "-1". Example 3. use_ruri_flag usage modparam("auth_radius", "use_ruri_flag", 22) 1.4. Functions 1.4.1. radius_www_authorize(realm) The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call www_challenge which will challenge the user again. This function will, in fact, perform sanity checks over the received credentials and then pass them along to the radius server which will verify the credentials and return whether they are valid or not. Meaning of the parameter is as follows: * realm - Realm is a opaque string that the user agent should present to the user so he can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string "" is used then the server will generate it from the request. In case of REGISTER requests To header field domain will be used (because this header field represents a user being registered), for all other messages From header field domain will be used. Example 4. radius_www_authorize usage ... if (!radius_www_authorize("iptel.org")) { www_challenge("iptel.org", "1"); }; ... 1.4.2. radius_proxy_authorize(realm) The function verifies credentials according to RFC2617. If the credentials are verified successfully then the function will succeed and mark the credentials as authorized (marked credentials can be later used by some other functions). If the function was unable to verify the credentials for some reason then it will fail and the script should call proxy_challenge which will challenge the user again. This function will, in fact, perform sanity checks over the received credentials and then pass them along to the radius server which will verify the credentials and return whether they are valid or not. Meaning of the parameter is as follows: * realm - Realm is a opaque string that the user agent should present to the user so he can decide what username and password to use. Usually this is domain of the host the server is running on. If an empty string "" is used then the server will generate it from the request. From header field domain will be used as realm. Example 5. proxy_authorize usage ... if (!radius_proxy_authorize("")) { proxy_challenge("", "1"); # Realm will be autogenerated }; ... kamailio-4.0.4/obsolete/auth_radius/sterman.c0000644000000000000000000002150212223032460017736 0ustar rootroot/* * $Id$ * * Digest Authentication - Radius support * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-03-09: Based on digest.c from radius_auth module (janakj) */ #include "../../mem/mem.h" #include "../../dprint.h" #include "../../modules/auth/api.h" #include "../../rad_dict.h" #include "../../usr_avp.h" #include "../../ut.h" #include "sterman.h" #include "authrad_mod.h" #include #include static int add_cisco_vsa(VALUE_PAIR** send, struct sip_msg* msg) { str callid; if (!msg->callid && parse_headers(msg, HDR_CALLID_F, 0) == -1) { LOG(L_ERR, "add_cisco_vsa: Cannot parse Call-ID header field\n"); return -1; } if (!msg->callid) { LOG(L_ERR, "add_cisco_vsa: Call-ID header field not found\n"); return -1; } callid.len = msg->callid->body.len + 8; callid.s = pkg_malloc(callid.len); if (callid.s == NULL) { LOG(L_ERR, "add_cisco_vsa: No memory left\n"); return -1; } memcpy(callid.s, "call-id=", 8); memcpy(callid.s + 8, msg->callid->body.s, msg->callid->body.len); if (rc_avpair_add(rh, send, ATTRID(attrs[A_CISCO_AVPAIR].v), callid.s, callid.len, VENDOR(attrs[A_CISCO_AVPAIR].v)) == 0) { LOG(L_ERR, "add_cisco_vsa: Unable to add Cisco-AVPair attribute\n"); pkg_free(callid.s); return -1; } pkg_free(callid.s); return 0; } /* * This function creates and submits radius authentication request as per * draft-sterman-aaa-sip-00.txt. In addition, _user parameter is included * in the request as value of a SER specific attribute type SIP-URI-User, * which can be be used as a check item in the request. Service type of * the request is Authenticate-Only. */ int radius_authorize_sterman(VALUE_PAIR** received, struct sip_msg* _msg, dig_cred_t* _cred, str* _method, str* _user) { static char msg[4096]; VALUE_PAIR *send; UINT4 service, ser_service_type; str method, user, user_name; str *ruri; int i; send = 0; if (!(_cred && _method && _user)) { LOG(L_ERR, "radius_authorize_sterman(): Invalid parameter value\n"); return -1; } method = *_method; user = *_user; /* * Add all the user digest parameters according to the qop defined. * Most devices tested only offer support for the simplest digest. */ if (_cred->username.domain.len) { if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_USER_NAME].v), _cred->username.whole.s, _cred->username.whole.len, VENDOR(attrs[A_USER_NAME].v))) { LOG(L_ERR, "radius_authorize_sterman(): Unable to add User-Name attribute\n"); goto err; } } else { user_name.len = _cred->username.user.len + _cred->realm.len + 1; user_name.s = pkg_malloc(user_name.len); if (!user_name.s) { LOG(L_ERR, "radius_authorize_sterman(): No memory left\n"); return -3; } memcpy(user_name.s, _cred->username.whole.s, _cred->username.whole.len); user_name.s[_cred->username.whole.len] = '@'; memcpy(user_name.s + _cred->username.whole.len + 1, _cred->realm.s, _cred->realm.len); if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_USER_NAME].v), user_name.s, user_name.len, VENDOR(attrs[A_USER_NAME].v))) { LOG(L_ERR, "sterman(): Unable to add User-Name attribute\n"); pkg_free(user_name.s); goto err; } pkg_free(user_name.s); } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_USER_NAME].v), _cred->username.whole.s, _cred->username.whole.len, VENDOR(attrs[A_DIGEST_USER_NAME].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-User-Name attribute\n"); goto err; } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_REALM].v), _cred->realm.s, _cred->realm.len, VENDOR(attrs[A_DIGEST_REALM].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-Realm attribute\n"); goto err; } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_NONCE].v), _cred->nonce.s, _cred->nonce.len, VENDOR(attrs[A_DIGEST_NONCE].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-Nonce attribute\n"); goto err; } if (use_ruri_flag < 0 || isflagset(_msg, use_ruri_flag) != 1) { ruri = &_cred->uri; } else { ruri = GET_RURI(_msg); } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_URI].v), ruri->s, ruri->len, VENDOR(attrs[A_DIGEST_URI].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-URI attribute\n"); goto err; } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_METHOD].v), method.s, method.len, VENDOR(attrs[A_DIGEST_METHOD].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-Method attribute\n"); goto err; } /* * Add the additional authentication fields according to the QOP. */ if (_cred->qop.qop_parsed == QOP_AUTH) { if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_QOP].v), "auth", 4, VENDOR(attrs[A_DIGEST_QOP].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-QOP attribute\n"); goto err; } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_NONCE_COUNT].v), _cred->nc.s, _cred->nc.len, VENDOR(attrs[A_DIGEST_NONCE_COUNT].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-CNonce-Count attribute\n"); goto err; } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_CNONCE].v), _cred->cnonce.s, _cred->cnonce.len, VENDOR(attrs[A_DIGEST_CNONCE].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-CNonce attribute\n"); goto err; } } else if (_cred->qop.qop_parsed == QOP_AUTHINT) { if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_QOP].v), "auth-int", 8, VENDOR(attrs[A_DIGEST_QOP].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-QOP attribute\n"); goto err; } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_NONCE_COUNT].v), _cred->nc.s, _cred->nc.len, VENDOR(attrs[A_DIGEST_NONCE_COUNT].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-Nonce-Count attribute\n"); goto err; } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_CNONCE].v), _cred->cnonce.s, _cred->cnonce.len, VENDOR(attrs[A_DIGEST_CNONCE].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-CNonce attribute\n"); goto err; } if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_BODY_DIGEST].v), _cred->opaque.s, _cred->opaque.len, VENDOR(attrs[A_DIGEST_BODY_DIGEST].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-Body-Digest attribute\n"); goto err; } } else { /* send nothing for qop == "" */ } /* Add the response... What to calculate against... */ if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_DIGEST_RESPONSE].v), _cred->response.s, _cred->response.len, VENDOR(attrs[A_DIGEST_RESPONSE].v))) { LOG(L_ERR, "sterman(): Unable to add Digest-Response attribute\n"); goto err; } /* Indicate the service type, Authenticate only in our case */ service = vals[V_SIP_SESSION].v; if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_SERVICE_TYPE].v), &service, -1, VENDOR(attrs[A_SERVICE_TYPE].v))) { LOG(L_ERR, "sterman(): Unable to add Service-Type attribute\n"); goto err; } /* Indicate the service type, Authenticate only in our case */ ser_service_type = vals[V_DIGEST_AUTHENTICATION].v; if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_SER_SERVICE_TYPE].v), &ser_service_type, -1, VENDOR(attrs[A_SER_SERVICE_TYPE].v))) { LOG(L_ERR, "sterman(): Unable to add SER-Service-Type attribute\n"); goto err; } /* Add SIP URI as a check item */ if (!rc_avpair_add(rh, &send, ATTRID(attrs[A_SER_URI_USER].v), user.s, user.len, VENDOR(attrs[A_SER_URI_USER].v))) { LOG(L_ERR, "sterman(): Unable to add Sip-URI-User attribute\n"); goto err; } if (attrs[A_CISCO_AVPAIR].n != NULL) { if (add_cisco_vsa(&send, _msg)) { goto err; } } /* Send request */ if ((i = rc_auth(rh, SIP_PORT, send, received, msg)) == OK_RC) { DBG("radius_authorize_sterman(): Success\n"); rc_avpair_free(send); send = 0; return 1; } else { DBG("radius_authorize_sterman(): Failure\n"); goto err; } err: if (send) rc_avpair_free(send); return -1; } kamailio-4.0.4/obsolete/auth_radius/authrad_mod.h0000644000000000000000000000266112223032460020566 0ustar rootroot/* * $Id$ * * Digest Authentication - Radius support * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-03-09: Based on auth_mod.h from radius_authorize (janakj) */ #ifndef AUTHRAD_MOD_H #define AUTHRAD_MOD_H #include "../../modules/auth/api.h" #include "../../rad_dict.h" extern struct attr attrs[]; extern struct val vals[]; extern void *rh; extern int use_did; extern int use_ruri_flag; extern auth_api_s_t auth_api; #endif /* AUTHRAD_MOD_H */ kamailio-4.0.4/obsolete/auth_radius/Makefile0000644000000000000000000000043212223032460017560 0ustar rootroot# $Id$ # # Digest Authentication - Radius support # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs include ../../Makefile.radius auto_gen= NAME=auth_radius.so DEFS+=-DSER_MOD_INTERFACE include ../../Makefile.modules kamailio-4.0.4/obsolete/auth_radius/authorize.h0000644000000000000000000000300212223032460020277 0ustar rootroot/* * $Id$ * * Digest Authentication - Radius support * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2003-03-09: Based on authorize.h from radius_auth (janakj) */ #ifndef AUTHORIZE_H #define AUTHORIZE_H #include "../../parser/msg_parser.h" /* * Authorize using Proxy-Authorization header field */ int radius_proxy_authorize(struct sip_msg* _msg, char* _realm, char* _s2); /* * Authorize using WWW-Authorization header field */ int radius_www_authorize(struct sip_msg* _msg, char* _realm, char* _s2); #endif /* AUTHORIZE_H */ kamailio-4.0.4/obsolete/usrloc/0000755000000000000000000000000012223032460015120 5ustar rootrootkamailio-4.0.4/obsolete/usrloc/urecord.h0000644000000000000000000001020312223032460016730 0ustar rootroot/* * $Id$ * * Usrloc record structure * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2003-03-12 added replication mark support (nils) */ #ifndef URECORD_H #define URECORD_H #include #include #include "hslot.h" #include "../../str.h" #include "../../qvalue.h" #include "ucontact.h" #include "notify.h" struct hslot; /* * Basic hash table element */ typedef struct urecord { str* domain; /* Pointer to domain we belong to */ str uid; /* User id */ ucontact_t* contacts; /* One or more contact fields */ struct hslot* slot; /* Collision slot in the hash table array we belong to */ struct { struct urecord* prev; /* Next item in the linked list */ struct urecord* next; /* Previous item in the linked list */ } d_ll; struct { /* Linked list of all elements in hash table */ struct urecord* prev; /* Previous item in the list */ struct urecord* next; /* Next item in the list */ } s_ll; struct notify_cb* watchers; /* List of watchers */ } urecord_t; /* Create a new record */ int new_urecord(str* _dom, str* _uid, urecord_t** _r); /* Free all memory associated with the element */ void free_urecord(urecord_t* _r); /* * Print an element, for debugging purposes only */ void print_urecord(FILE* _f, urecord_t* _r); /* * Add a new contact */ int mem_insert_ucontact(urecord_t* _r, str* aor, str* _c, time_t _e, qvalue_t _q, str* _cid, int _cs, unsigned int _flags, struct ucontact** _con, str *_ua, str* _recv, struct socket_info* sock, str* _inst, int sid); /* * Remove the contact from lists */ void mem_remove_ucontact(urecord_t* _r, ucontact_t* _c); /* * Remove contact from the list and delete */ void mem_delete_ucontact(urecord_t* _r, ucontact_t* _c); /* * Timer handler */ int timer_urecord(urecord_t* _r); /* ===== Module interface ======== */ /* * Release urecord previously obtained * through get_urecord */ typedef void (*release_urecord_t)(urecord_t* _r); void release_urecord(urecord_t* _r); /* * Create and insert new contact * into urecord with additional replication argument */ typedef int (*insert_ucontact_t)(urecord_t* _r, str* aor, str* _c, time_t _e, qvalue_t _q, str* _cid, int _cs, unsigned int _flags, struct ucontact** _con, str *_ua, str* _recv, struct socket_info* sock, str* _inst, int sid); int insert_ucontact(urecord_t* _r, str* aor, str* _c, time_t _e, qvalue_t _q, str* _cid, int _cs, unsigned int _flags, struct ucontact** _con, str *_ua, str* _recv, struct socket_info* sock, str* _inst, int sid); /* * Delete ucontact from urecord */ typedef int (*delete_ucontact_t)(urecord_t* _r, struct ucontact* _c); int delete_ucontact(urecord_t* _r, struct ucontact* _c); /* * Get pointer to ucontact with given contact */ typedef int (*get_ucontact_t)(urecord_t* _r, str* _c, struct ucontact** _co); int get_ucontact(urecord_t* _r, str* _c, struct ucontact** _co); typedef int (*get_ucontact_by_inst_t)(urecord_t* _r, str* _c, str* _i, struct ucontact** _co); int get_ucontact_by_instance(urecord_t* _r, str* _c, str* _i, struct ucontact** _co); #endif /* URECORD_H */ kamailio-4.0.4/obsolete/usrloc/ul_rpc.h0000644000000000000000000000223112223032460016553 0ustar rootroot/* * $Id$ * * Usrloc module interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _UL_RPC_H #define _UL_RPC_H #include "../../rpc.h" extern rpc_export_t ul_rpc[]; #endif /* _UL_RPC_H */ kamailio-4.0.4/obsolete/usrloc/udomain.h0000644000000000000000000000760512223032460016735 0ustar rootroot/* * $Id$ * * Usrloc domain structure * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2003-03-11 changed to new locking scheme: locking.h (andrei) * 2006-11-23 switched to fixed hash size (andrei) */ #ifndef UDOMAIN_H #define UDOMAIN_H #include #include "../../locking.h" #include "../../str.h" #include "../../lib/srdb2/db.h" #include "urecord.h" #include "hslot.h" /* udomain hash size, for best performance always use a 2^k value * good values 8192: 9% increase over 4096, 16384: 5% inc. over 8192, * 32768 4-5% inc over 16384 */ #define UDOMAIN_HASH_SIZE 16384 struct hslot; /* Hash table slot */ struct urecord; /* Usrloc record */ /* * The structure represents a usrloc domain */ typedef struct udomain { str* name; /* Domain name */ int users; /* Number of registered users */ int expired; /* Number of expired contacts */ int db_cmd_idx; /* Index into db_cmd arrays */ struct hslot* table; /* Hash table - array of collision slots */ struct { /* Linked list of all elements in the domain */ int n; /* Number of element in the linked list */ struct urecord* first; /* First element in the list */ struct urecord* last; /* Last element in the list */ } d_ll; gen_lock_t lock; /* lock variable */ } udomain_t; /* * Create a new domain structure * _n is pointer to str representing * name of the domain, the string is * not copied, it should point to str * structure stored in domain list */ int new_udomain(str* _n, udomain_t** _d); /* * Free all memory allocated for * the domain */ void free_udomain(udomain_t* _d); /* * Just for debugging */ void print_udomain(FILE* _f, udomain_t* _d); /* * Load data from a database */ int preload_udomain(udomain_t* _d); /* * Timer handler for given domain */ int timer_udomain(udomain_t* _d); /* * Insert record into domain */ int mem_insert_urecord(udomain_t* _d, str* _uid, struct urecord** _r); /* * Delete a record */ void mem_delete_urecord(udomain_t* _d, struct urecord* _r); /* * Get lock */ typedef void (*lock_udomain_t)(udomain_t* _d); void lock_udomain(udomain_t* _d); /* * Release lock */ typedef void (*unlock_udomain_t)(udomain_t* _d); void unlock_udomain(udomain_t* _d); /* ===== module interface ======= */ /* * Create and insert a new record */ typedef int (*insert_urecord_t)(udomain_t* _d, str* _uid, struct urecord** _r); int insert_urecord(udomain_t* _d, str* _uid, struct urecord** _r); /* * Obtain a urecord pointer if the urecord exists in domain */ typedef int (*get_urecord_t)(udomain_t* _d, str* _uid, struct urecord** _r); int get_urecord(udomain_t* _d, str* _uid, struct urecord** _r); /* * Delete a urecord from domain */ typedef int (*delete_urecord_t)(udomain_t* _d, str* _uid); int delete_urecord(udomain_t* _d, str* _uid); #endif /* UDOMAIN_H */ kamailio-4.0.4/obsolete/usrloc/usrloc.c0000644000000000000000000001047012223032460016575 0ustar rootroot/* * $Id$ * * Usrloc interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "usrloc.h" #include "../../sr_module.h" #include "ul_mod.h" int bind_usrloc(usrloc_api_t* api) { if (!api) { LOG(L_ERR, "bind_usrloc(): Invalid parameter value\n"); return -1; } api->register_udomain = (register_udomain_t)find_export("ul_register_udomain", 1, 0); if (api->register_udomain == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind register_udomain\n"); return -1; } api->get_all_ucontacts = (get_all_ucontacts_t)find_export("ul_get_all_ucontacts", 1, 0); if (api->get_all_ucontacts == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind get_all_ucontacts\n"); return -1; } api->insert_urecord = (insert_urecord_t)find_export("ul_insert_urecord", 1, 0); if (api->insert_urecord == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind insert_urecord\n"); return -1; } api->delete_urecord = (delete_urecord_t)find_export("ul_delete_urecord", 1, 0); if (api->delete_urecord == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind delete_urecord\n"); return -1; } api->get_urecord = (get_urecord_t)find_export("ul_get_urecord", 1, 0); if (api->get_urecord == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind get_urecord\n"); return -1; } api->lock_udomain = (lock_udomain_t)find_export("ul_lock_udomain", 1, 0); if (api->lock_udomain == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind loc_udomain\n"); return -1; } api->unlock_udomain = (unlock_udomain_t)find_export("ul_unlock_udomain", 1, 0); if (api->unlock_udomain == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind unlock_udomain\n"); return -1; } api->release_urecord = (release_urecord_t)find_export("ul_release_urecord", 1, 0); if (api->release_urecord == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind release_urecord\n"); return -1; } api->insert_ucontact = (insert_ucontact_t)find_export("ul_insert_ucontact", 1, 0); if (api->insert_ucontact == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind insert_ucontact\n"); return -1; } api->delete_ucontact = (delete_ucontact_t)find_export("ul_delete_ucontact", 1, 0); if (api->delete_ucontact == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind delete_ucontact\n"); return -1; } api->get_ucontact = (get_ucontact_t)find_export("ul_get_ucontact", 1, 0); if (api->get_ucontact == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind get_ucontact\n"); return -1; } api->get_ucontact_by_instance = (get_ucontact_by_inst_t)find_export("ul_get_ucontact_by_inst", 1, 0); if (api->get_ucontact_by_instance == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind get_ucontact_by_instance\n"); return -1; } api->update_ucontact = (update_ucontact_t)find_export("ul_update_ucontact", 1, 0); if (api->update_ucontact == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind update_ucontact\n"); return -1; } api->register_watcher = (register_watcher_t) find_export("ul_register_watcher", 1, 0); if (api->register_watcher == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind register_watcher\n"); return -1; } api->unregister_watcher = (unregister_watcher_t) find_export("ul_unregister_watcher", 1, 0); if (api->unregister_watcher == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind unregister_watcher\n"); return -1; } api->register_ulcb = (register_ulcb_t) find_export("ul_register_ulcb", 1, 0); if (api->register_ulcb == 0) { LOG(L_ERR, "bind_usrloc(): Can't bind register_ulcb\n"); return -1; } return 0; } kamailio-4.0.4/obsolete/usrloc/ucontact.c0000644000000000000000000003636412223032460017120 0ustar rootroot/* * $Id$ * * Usrloc contact structure * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2003-03-12 added replication mark and three zombie states (nils) * 2004-03-17 generic callbacks added (bogdan) * 2004-06-07 updated to the new DB api (andrei) * 2005-02-25 incoming socket is saved in ucontact record (bogdan) */ #include "ucontact.h" #include /* memcpy */ #include "../../mem/shm_mem.h" #include "../../ut.h" #include "../../dprint.h" #include "../../lib/srdb2/db.h" #include "../../ip_addr.h" #include "../../globals.h" #include "../../ser_time.h" #include "ul_mod.h" #include "ul_callback.h" #include "reg_avps.h" #include "reg_avps_db.h" /* * Create a new contact structure */ int new_ucontact(str* _dom, str* _uid, str* aor, str* _contact, time_t _e, qvalue_t _q, str* _callid, int _cseq, unsigned int _flags, ucontact_t** _c, str* _ua, str* _recv, struct socket_info* sock, str* _inst, int sid) { *_c = (ucontact_t*)shm_malloc(sizeof(ucontact_t)); if (!(*_c)) { LOG(L_ERR, "new_ucontact(): No memory left\n"); return -1; } memset(*_c, 0, sizeof(ucontact_t)); (*_c)->domain = _dom; (*_c)->uid = _uid; (*_c)->aor.s = (char*)shm_malloc(aor->len); if ((*_c)->aor.s == 0) { LOG(L_ERR, "new_ucontact(): No memory left\n"); goto error; } memcpy((*_c)->aor.s, aor->s, aor->len); (*_c)->aor.len = aor->len; (*_c)->c.s = (char*)shm_malloc(_contact->len); if ((*_c)->c.s == 0) { LOG(L_ERR, "new_ucontact(): No memory left 2\n"); goto error; } memcpy((*_c)->c.s, _contact->s, _contact->len); (*_c)->c.len = _contact->len; (*_c)->expires = _e; (*_c)->q = _q; (*_c)->callid.s = (char*)shm_malloc(_callid->len); if ((*_c)->callid.s == 0) { LOG(L_ERR, "new_ucontact(): No memory left 4\n"); goto error; } memcpy((*_c)->callid.s, _callid->s, _callid->len); (*_c)->callid.len = _callid->len; (*_c)->user_agent.s = (char*)shm_malloc(_ua->len); if ((*_c)->user_agent.s == 0) { LOG(L_ERR, "new_ucontact(): No memory left 8\n"); goto error; } memcpy((*_c)->user_agent.s, _ua->s, _ua->len); (*_c)->user_agent.len = _ua->len; if (_recv) { (*_c)->received.s = (char*)shm_malloc(_recv->len); if ((*_c)->received.s == 0) { LOG(L_ERR, "new_ucontact(): No memory left\n"); goto error; } memcpy((*_c)->received.s, _recv->s, _recv->len); (*_c)->received.len = _recv->len; } else { (*_c)->received.s = 0; (*_c)->received.len = 0; } if(_inst && _inst->len > 0) { (*_c)->instance.s = (char*)shm_malloc(_inst->len); if ((*_c)->instance.s == 0) { LOG(L_ERR, "new_ucontact(): No memory left\n"); goto error; } memcpy((*_c)->instance.s, _inst->s, _inst->len); (*_c)->instance.len = _inst->len; } else { (*_c)->instance.s = 0; (*_c)->instance.len = 0; } (*_c)->server_id = sid; (*_c)->cseq = _cseq; (*_c)->state = CS_NEW; (*_c)->flags = _flags; (*_c)->sock = sock; return 0; error: if (*_c) { if ((*_c)->instance.s) shm_free((*_c)->instance.s); if ((*_c)->received.s) shm_free((*_c)->received.s); if ((*_c)->user_agent.s) shm_free((*_c)->user_agent.s); if ((*_c)->callid.s) shm_free((*_c)->callid.s); if ((*_c)->c.s) shm_free((*_c)->c.s); if ((*_c)->aor.s) shm_free((*_c)->aor.s); shm_free(*_c); } return -1; } /* * Free all memory associated with given contact structure */ void free_ucontact(ucontact_t* _c) { if (!_c) return; if (_c->received.s) shm_free(_c->received.s); if (_c->user_agent.s) shm_free(_c->user_agent.s); if (_c->callid.s) shm_free(_c->callid.s); if (_c->c.s) shm_free(_c->c.s); if (_c->aor.s) shm_free(_c->aor.s); if (_c->instance.s) shm_free(_c->instance.s); shm_free(_c); } /* * Print contact, for debugging purposes only */ void print_ucontact(FILE* _f, ucontact_t* _c) { time_t t = ser_time(0); char* st; switch(_c->state) { case CS_NEW: st = "CS_NEW"; break; case CS_SYNC: st = "CS_SYNC"; break; case CS_DIRTY: st = "CS_DIRTY"; break; default: st = "CS_UNKNOWN"; break; } fprintf(_f, "~~~Contact(%p)~~~\n", _c); fprintf(_f, "domain : '%.*s'\n", _c->domain->len, ZSW(_c->domain->s)); fprintf(_f, "uid : '%.*s'\n", _c->uid->len, ZSW(_c->uid->s)); fprintf(_f, "aor : '%.*s'\n", _c->aor.len, ZSW(_c->aor.s)); fprintf(_f, "Contact : '%.*s'\n", _c->c.len, ZSW(_c->c.s)); fprintf(_f, "Expires : "); if (_c->flags & FL_PERMANENT) { fprintf(_f, "Permanent\n"); } else { if (_c->expires == 0) { fprintf(_f, "Deleted\n"); } else if (t > _c->expires) { fprintf(_f, "Expired\n"); } else { fprintf(_f, "%u\n", (unsigned int)(_c->expires - t)); } } fprintf(_f, "q : %s\n", q2str(_c->q, 0)); fprintf(_f, "Call-ID : '%.*s'\n", _c->callid.len, ZSW(_c->callid.s)); fprintf(_f, "CSeq : %d\n", _c->cseq); fprintf(_f, "User-Agent: '%.*s'\n", _c->user_agent.len, ZSW(_c->user_agent.s)); fprintf(_f, "received : '%.*s'\n", _c->received.len, ZSW(_c->received.s)); fprintf(_f, "instance : '%.*s'\n", _c->instance.len, ZSW(_c->instance.s)); fprintf(_f, "State : %s\n", st); fprintf(_f, "Flags : %u\n", _c->flags); fprintf(_f, "server_id : %d\n", _c->server_id); fprintf(_f, "Sock : %p\n", _c->sock); fprintf(_f, "next : %p\n", _c->next); fprintf(_f, "prev : %p\n", _c->prev); fprintf(_f, "~~~/Contact~~~~\n"); } /* * Update ucontact structure in memory */ int mem_update_ucontact(ucontact_t* _c, str* _u, str* aor, time_t _e, qvalue_t _q, str* _cid, int _cs, unsigned int _set, unsigned int _reset, str* _ua, str* _recv, struct socket_info* sock, str* _inst) { char* ptr; if (_c->aor.len < aor->len) { ptr = (char*)shm_malloc(aor->len); if (ptr == 0) { LOG(L_ERR, "update_ucontact(): No memory left\n"); return -1; } memcpy(ptr, aor->s, aor->len); shm_free(_c->aor.s); _c->aor.s = ptr; } else { memcpy(_c->aor.s, aor->s, aor->len); } _c->aor.len = aor->len; if (_c->c.len < _u->len) { ptr = (char*)shm_malloc(_u->len); if (ptr == 0) { LOG(L_ERR, "update_ucontact(): No memory left\n"); return -1; } memcpy(ptr, _u->s, _u->len); shm_free(_c->c.s); _c->c.s = ptr; } else { memcpy(_c->c.s, _u->s, _u->len); } _c->c.len = _u->len; if (_c->callid.len < _cid->len) { ptr = (char*)shm_malloc(_cid->len); if (ptr == 0) { LOG(L_ERR, "update_ucontact(): No memory left\n"); return -1; } memcpy(ptr, _cid->s, _cid->len); shm_free(_c->callid.s); _c->callid.s = ptr; } else { memcpy(_c->callid.s, _cid->s, _cid->len); } _c->callid.len = _cid->len; if (_c->user_agent.len < _ua->len) { ptr = (char*)shm_malloc(_ua->len); if (ptr == 0) { LOG(L_ERR, "update_ucontact(): No memory left\n"); return -1; } memcpy(ptr, _ua->s, _ua->len); shm_free(_c->user_agent.s); _c->user_agent.s = ptr; } else { memcpy(_c->user_agent.s, _ua->s, _ua->len); } _c->user_agent.len = _ua->len; if (_recv) { if (_c->received.len < _recv->len) { ptr = (char*)shm_malloc(_recv->len); if (ptr == 0) { LOG(L_ERR, "update_ucontact(): No memory left\n"); return -1; } memcpy(ptr, _recv->s, _recv->len); if (_c->received.s) shm_free(_c->received.s); _c->received.s = ptr; /* fixme: The buffer could be in fact bigger than the value * of len and we would then free it and allocate a new one. */ _c->received.len = _recv->len; } else { memcpy(_c->received.s, _recv->s, _recv->len); } _c->received.len = _recv->len; } else { if (_c->received.s) shm_free(_c->received.s); _c->received.s = 0; _c->received.len = 0; } if (_inst) { if (_c->instance.len < _inst->len) { ptr = (char *)shm_malloc(_inst->len); if (ptr == 0) { LOG(L_ERR, "update_ucontact(): No memory left\n"); return -1; } memcpy(ptr, _inst->s, _inst->len); if (_c->instance.s) shm_free(_c->instance.s); _c->instance.s = ptr; } else { memcpy(_c->instance.s, _inst->s, _inst->len); } _c->instance.len = _inst->len; } else { if (_c->instance.s) shm_free(_c->instance.s); _c->instance.s = 0; _c->instance.len = 0; } _c->expires = _e; _c->q = _q; _c->cseq = _cs; _c->flags |= _set; _c->flags &= ~_reset; _c->sock = sock; return 0; } /* ================ State related functions =============== */ /* * Update state of the contact */ void st_update_ucontact(ucontact_t* _c) { switch(_c->state) { case CS_NEW: /* Contact is new and is not in the database yet, * we remain in the same state here because the * contact must be inserted later in the timer */ break; case CS_SYNC: /* For db mode 2 a modified contact needs to be * updated also in the database, so transit into * CS_DIRTY and let the timer to do the update * again. For db mode 1 the db update is already * done and we don't have to change the state. */ if (db_mode == WRITE_BACK) { _c->state = CS_DIRTY; } break; case CS_DIRTY: /* Modification of dirty contact results in * dirty contact again, don't change anything */ break; } } /* * Update state of the contact * Returns 1 if the contact should be * delete from memory immediately, * 0 otherwise */ int st_delete_ucontact(ucontact_t* _c) { switch(_c->state) { case CS_NEW: /* Contact is new and isn't in the database * yet, we can delete it from the memory * safely. */ return 1; case CS_SYNC: case CS_DIRTY: /* Contact is in the database, * we cannot remove it from the memory * directly, but we can set expires to zero * and the timer will take care of deleting * the contact from the memory as well as * from the database */ if (db_mode == WRITE_BACK) { /* Reset permanent flag */ _c->flags &= ~FL_PERMANENT; _c->expires = 0; return 0; } else { /* WRITE_THROUGH, READONLY or NO_DB -- we can * remove it from memory immediately and * the calling function would also remove * it from the database if needed */ return 1; } } return 0; /* Makes gcc happy */ } /* * Called when the timer is about to delete * an expired contact, this routine returns * 1 if the contact should be removed from * the database and 0 otherwise */ int st_expired_ucontact(ucontact_t* _c) { /* There is no need to change contact * state, because the contact will * be deleted anyway */ switch(_c->state) { case CS_NEW: /* Contact is not in the database * yet, remove it from memory only */ return 0; case CS_SYNC: case CS_DIRTY: /* Remove from database here */ if (db_skip_delete) return 0; else return 1; } return 0; /* Makes gcc happy */ } /* * Called when the timer is about flushing the contact, * updates contact state and returns 1 if the contact * should be inserted, 2 if update , 3 if delete * from memory, 4 if delete from database and 0 otherwise */ int st_flush_ucontact(ucontact_t* _c) { switch(_c->state) { case CS_NEW: /* Contact is new and is not in * the database yet so we have * to insert it */ _c->state = CS_SYNC; return 1; case CS_SYNC: /* Contact is synchronized, do * nothing */ return 0; case CS_DIRTY: /* Contact has been modified and * is in the db already so we * have to update it */ _c->state = CS_SYNC; return 2; } return 0; /* Makes gcc happy */ } /* ============== Database related functions ================ */ /* * Insert contact into the database */ int db_store_ucontact(ucontact_t* _c) { str avps; if (_c->flags & FL_MEM) { return 0; } avps.s = NULL; ins_contact[cur_cmd]->vals[0].v.lstr = *_c->uid; ins_contact[cur_cmd]->vals[1].v.lstr.s = _c->c.s; ins_contact[cur_cmd]->vals[1].v.lstr.len = MIN(_c->c.len, 255); ins_contact[cur_cmd]->vals[2].v.time = _c->expires; ins_contact[cur_cmd]->vals[3].v.flt = (float)q2double(_c->q); ins_contact[cur_cmd]->vals[4].v.lstr.s = _c->callid.s; ins_contact[cur_cmd]->vals[4].v.lstr.len = MIN(_c->callid.len, 255); ins_contact[cur_cmd]->vals[5].v.int4 = _c->cseq; ins_contact[cur_cmd]->vals[6].v.bitmap = _c->flags; ins_contact[cur_cmd]->vals[7].v.lstr.s = _c->user_agent.s; ins_contact[cur_cmd]->vals[7].v.lstr.len = MIN(_c->user_agent.len, 64); if (_c->received.s == 0) { ins_contact[cur_cmd]->vals[8].flags |= DB_NULL; } else { ins_contact[cur_cmd]->vals[8].flags &= ~DB_NULL; ins_contact[cur_cmd]->vals[8].v.lstr = _c->received; } if (_c->instance.s == 0) { ins_contact[cur_cmd]->vals[9].flags |= DB_NULL; } else { ins_contact[cur_cmd]->vals[9].flags &= ~DB_NULL; ins_contact[cur_cmd]->vals[9].v.lstr = _c->instance; } ins_contact[cur_cmd]->vals[10].v.lstr.s = _c->aor.s; ins_contact[cur_cmd]->vals[10].v.lstr.len = MIN(_c->aor.len, 255); ins_contact[cur_cmd]->vals[11].v.int4 = _c->server_id; if (use_reg_avps()) { if (serialize_avps(_c->avps, &avps) < 0) { ERR("Error while serializing AVPs\n"); return -1; } ins_contact[cur_cmd]->vals[12].v.lstr = avps; } /* FIXME */ if (db_exec(NULL, ins_contact[cur_cmd]) < 0) { ERR("Error while storing contact in database\n"); if (avps.s) pkg_free(avps.s); return -1; } if (avps.s) pkg_free(avps.s); return 0; } /* * Delete contact from the database */ int db_delete_ucontact(ucontact_t* _c) { if (_c->flags & FL_MEM) { return 0; } del_contact[cur_cmd]->match[0].v.lstr = *_c->uid; del_contact[cur_cmd]->match[1].v.lstr = _c->c; if (db_exec(NULL, del_contact[cur_cmd]) < 0) { ERR("Error while deleting contact from database\n"); return -1; } return 0; } /* * Update ucontact with new values */ int update_ucontact(ucontact_t* _c, str* _u, str* aor, time_t _e, qvalue_t _q, str* _cid, int _cs, unsigned int _set, unsigned int _reset, str* _ua, str* _recv, struct socket_info* sock, str* _inst, int sid) { /* run callbacks for UPDATE event */ if (exists_ulcb_type(UL_CONTACT_UPDATE)) { run_ul_callbacks( UL_CONTACT_UPDATE, _c); } /* we have to update memory in any case, but database directly * only in db_mode 1 */ if (mem_update_ucontact(_c, _u, aor, _e, _q, _cid, _cs, _set, _reset, _ua, _recv, sock, _inst) < 0) { LOG(L_ERR, "update_ucontact(): Error while updating\n"); return -1; } st_update_ucontact(_c); save_reg_avps(_c); if (db_mode == WRITE_THROUGH) { if (db_store_ucontact(_c) < 0) { LOG(L_ERR, "update_ucontact(): Error while updating database\n"); } } return 0; } kamailio-4.0.4/obsolete/usrloc/ucontact.h0000644000000000000000000001400512223032460017111 0ustar rootroot/* * $Id$ * * Usrloc contact structure * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2003-03-12 added replication mark and three zombie states (nils) * 2005-02-25 incoming socket is saved in ucontact record (bogdan) */ #ifndef UCONTACT_H #define UCONTACT_H #include #include #include "../../qvalue.h" #include "../../str.h" #include "../../usr_avp.h" typedef enum cstate { CS_NEW, /* New contact - not flushed yet */ CS_SYNC, /* Synchronized contact with the database */ CS_DIRTY /* Update contact - not flushed yet */ } cstate_t; /* * Flags that can be associated with a Contact */ typedef enum flags { FL_NONE = 0, /* No flags set */ FL_NAT = 1 << 0, /* Contact is behind NAT */ FL_INVITE = 1 << 1, /* Contact supports INVITE and related methods */ FL_N_INVITE = 1 << 2, /* Contact doesn't support INVITE and related methods */ FL_MESSAGE = 1 << 3, /* Contact supports MESSAGE */ FL_N_MESSAGE = 1 << 4, /* Contact doesn't support MESSAGE */ FL_SUBSCRIBE = 1 << 5, /* Contact supports SUBSCRIBE and NOTIFY */ FL_N_SUBSCRIBE = 1 << 6, /* Contact doesn't support SUBSCRIBE and NOTIFY */ FL_PERMANENT = 1 << 7, /* Permanent contact (does not expire) */ FL_MEM = 1 << 8, /* Update memory only -- used for REGISTER replication */ FL_ALL = 0xFFFFFFFF /* All flags set */ } flags_t; typedef struct ucontact { str* domain; /* Pointer to domain name */ str* uid; /* UID of owner of contact*/ str aor; /* Address of record */ str c; /* Contact address */ str received; /* IP, port, and protocol we received the REGISTER from */ struct socket_info* sock; /* Socket to be used when sending SIP messages to this contact */ time_t expires; /* expires parameter */ qvalue_t q; /* q parameter */ str callid; /* Call-ID header field */ int cseq; /* CSeq value */ cstate_t state; /* State of the contact */ unsigned int flags; /* Various flags (NAT, supported methods etc) */ str user_agent; /* User-Agent header field */ str instance; /* sip.instance parameter */ int server_id; /* ID of the server within a cluster responsible for the contact */ struct ucontact* next; /* Next contact in the linked list */ struct ucontact* prev; /* Previous contact in the linked list */ avp_t *avps; } ucontact_t; /* * Valid contact is a contact that either didn't expire yet or is permanent */ #define VALID_CONTACT(c, t) (((c->expires > t) || (c->flags & FL_PERMANENT))) /* * Create a new contact structure */ int new_ucontact(str* _dom, str* _uid, str* aor, str* _contact, time_t _e, qvalue_t _q, str* _callid, int _cseq, unsigned int _flags, ucontact_t** _c, str* _ua, str* _recv, struct socket_info* sock, str* _inst, int sid); /* * Free all memory associated with given contact structure */ void free_ucontact(ucontact_t* _c); /* * Print contact, for debugging purposes only */ void print_ucontact(FILE* _f, ucontact_t* _c); /* * Update existing contact in memory with new values */ int mem_update_ucontact(ucontact_t* _c, str* _u, str* aor, time_t _e, qvalue_t _q, str* _cid, int _cs, unsigned int _set, unsigned int _res, str* _ua, str* _recv, struct socket_info* sock, str* _inst); /* ===== State transition functions - for write back cache scheme ======== */ /* * Update state of the contact if we * are using write-back scheme */ void st_update_ucontact(ucontact_t* _c); /* * Update state of the contact if we * are using write-back scheme * Returns 1 if the contact should be * deleted from memory immediately, * 0 otherwise */ int st_delete_ucontact(ucontact_t* _c); /* * Called when the timer is about to delete * an expired contact, this routine returns * 1 if the contact should be removed from * the database and 0 otherwise */ int st_expired_ucontact(ucontact_t* _c); /* * Called when the timer is about flushing the contact, * updates contact state and returns 1 if the contact * should be inserted, 2 if updated and 0 otherwise */ int st_flush_ucontact(ucontact_t* _c); /* ==== Database related functions ====== */ /* * Insert contact into the database */ int db_store_ucontact(ucontact_t* _c); /* * Delete contact from the database */ int db_delete_ucontact(ucontact_t* _c); /* ====== Module interface ====== */ /* * Update ucontact with new values without replication */ typedef int (*update_ucontact_t)(ucontact_t* _c, str* _u, str* aor, time_t _e, qvalue_t _q, str* _cid, int _cs, unsigned int _set, unsigned int _reset, str* _ua, str* _recv, struct socket_info* sock, str* _inst, int sid); int update_ucontact(ucontact_t* _c, str* _u, str* aor, time_t _e, qvalue_t _q, str* _cid, int _cs, unsigned int _set, unsigned int _reset, str* _ua, str* _recv, struct socket_info* sock, str* _inst, int sid); #endif /* UCONTACT_H */ kamailio-4.0.4/obsolete/usrloc/udomain.c0000644000000000000000000003253712223032460016732 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2003-03-11 changed to the new locking scheme: locking.h (andrei) * 2003-03-12 added replication mark and zombie state (nils) * 2004-06-07 updated to the new DB api (andrei) * 2004-08-23 hash function changed to process characters as unsigned * -> no negative results occur (jku) * 2005-02-25 incoming socket is saved in ucontact record (bogdan) * 2006-11-23 switched to better hash functions and fixed hash size (andrei) * */ #include "udomain.h" #include #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../lib/srdb2/db.h" #include "../../ut.h" #include "../../parser/parse_param.h" #include "../../parser/parse_uri.h" #include "../../resolve.h" #include "../../socket_info.h" #include "ul_mod.h" /* usrloc module parameters */ #include "notify.h" #include "reg_avps.h" #include "reg_avps_db.h" #include "utime.h" #include "../../hashes.h" /* #define HASH_STRING_OPTIMIZE */ /* * Hash function */ static inline int hash_func(udomain_t* _d, unsigned char* _s, int _l) { #ifdef HASH_STRING_OPTIMIZE return get_hash1_raw((char*)_s, _l) % UDOMAIN_HASH_SIZE; #else return get_hash1_raw2((char*)_s, _l) % UDOMAIN_HASH_SIZE; #endif } /* * Add a record to list of all records in a domain */ static inline void udomain_add(udomain_t* _d, urecord_t* _r) { if (_d->d_ll.n == 0) { _d->d_ll.first = _r; _d->d_ll.last = _r; } else { _r->d_ll.prev = _d->d_ll.last; _d->d_ll.last->d_ll.next = _r; _d->d_ll.last = _r; } _d->d_ll.n++; } /* * Remove a record from list of all records in a domain */ static inline void udomain_remove(udomain_t* _d, urecord_t* _r) { if (_d->d_ll.n == 0) return; if (_r->d_ll.prev) { _r->d_ll.prev->d_ll.next = _r->d_ll.next; } else { _d->d_ll.first = _r->d_ll.next; } if (_r->d_ll.next) { _r->d_ll.next->d_ll.prev = _r->d_ll.prev; } else { _d->d_ll.last = _r->d_ll.prev; } _r->d_ll.prev = _r->d_ll.next = 0; _d->d_ll.n--; } /* * Create a new domain structure * _n is pointer to str representing * name of the domain, the string is * not copied, it should point to str * structure stored in domain list */ int new_udomain(str* _n, udomain_t** _d) { int i; /* Must be always in shared memory, since * the cache is accessed from timer which * lives in a separate process */ *_d = (udomain_t*)shm_malloc(sizeof(udomain_t)); if (!(*_d)) { LOG(L_ERR, "new_udomain(): No memory left\n"); return -1; } memset(*_d, 0, sizeof(udomain_t)); (*_d)->table = (hslot_t*)shm_malloc(sizeof(hslot_t) * UDOMAIN_HASH_SIZE); if (!(*_d)->table) { LOG(L_ERR, "new_udomain(): No memory left 2\n"); shm_free(*_d); return -2; } (*_d)->name = _n; for(i = 0; i < UDOMAIN_HASH_SIZE; i++) { if (init_slot(*_d, &((*_d)->table[i])) < 0) { LOG(L_ERR, "new_udomain(): Error while initializing hash table\n"); shm_free((*_d)->table); shm_free(*_d); return -3; } } lock_init(&(*_d)->lock); (*_d)->users = 0; (*_d)->expired = 0; return 0; } /* * Free all memory allocated for * the domain */ void free_udomain(udomain_t* _d) { int i; lock_udomain(_d); if (_d->table) { for(i = 0; i < UDOMAIN_HASH_SIZE; i++) { deinit_slot(_d->table + i); } shm_free(_d->table); } unlock_udomain(_d); lock_destroy(&_d->lock);/* destroy the lock (required for SYSV sems!)*/ shm_free(_d); } /* * Just for debugging */ void print_udomain(FILE* _f, udomain_t* _d) { struct urecord* r; fprintf(_f, "---Domain---\n"); fprintf(_f, "name : '%.*s'\n", _d->name->len, ZSW(_d->name->s)); fprintf(_f, "size : %d\n", UDOMAIN_HASH_SIZE); fprintf(_f, "table: %p\n", _d->table); fprintf(_f, "d_ll {\n"); fprintf(_f, " n : %d\n", _d->d_ll.n); fprintf(_f, " first: %p\n", _d->d_ll.first); fprintf(_f, " last : %p\n", _d->d_ll.last); fprintf(_f, "}\n"); /*fprintf(_f, "lock : %d\n", _d->lock); -- can be a structure --andrei*/ if (_d->d_ll.n > 0) { fprintf(_f, "\n"); r = _d->d_ll.first; while(r) { print_urecord(_f, r); r = r->d_ll.next; } fprintf(_f, "\n"); } fprintf(_f, "---/Domain---\n"); } static struct socket_info* find_socket(str* received) { struct sip_uri puri; param_hooks_t hooks; struct hostent* he; struct ip_addr ip; struct socket_info* si; param_t* params; unsigned short port; char* buf; int error; if (!received) return 0; si = 0; if (parse_uri(received->s, received->len, &puri) < 0) { LOG(L_ERR, "find_socket: Error while parsing received URI\n"); return 0; } if (parse_params(&puri.params, CLASS_URI, &hooks, ¶ms) < 0) { LOG(L_ERR, "find_socket: Error while parsing received URI parameters\n"); return 0; } if (!hooks.uri.dstip || !hooks.uri.dstip->body.s || !hooks.uri.dstip->body.len) goto end; buf = (char*)pkg_malloc(hooks.uri.dstip->body.len + 1); if (!buf) { LOG(L_ERR, "find_socket: No memory left\n"); goto end; } memcpy(buf, hooks.uri.dstip->body.s, hooks.uri.dstip->body.len); buf[hooks.uri.dstip->body.len] = '\0'; he = resolvehost(buf); if (he == 0) { LOG(L_ERR, "find_socket: Unable to resolve '%s'\n", buf); pkg_free(buf); goto end; } pkg_free(buf); if (hooks.uri.dstport && hooks.uri.dstport->body.s && hooks.uri.dstport->body.len) { port = str2s(hooks.uri.dstport->body.s, hooks.uri.dstport->body.len, &error); if (error != 0) { LOG(L_ERR, "find_socket: Unable to convert port number\n"); goto end; } } else { port = 0; } hostent2ip_addr(&ip, he, 0); si = find_si(&ip, port, puri.proto); if (si == 0) { LOG(L_ERR, "find_socket: Unable to find socket, using the default one\n"); goto end; } end: if (params) free_params(params); return si; } int preload_udomain(udomain_t* _d) { db_fld_t columns[] = { {.name = uid_col.s, .type = DB_STR}, {.name = contact_col.s, .type = DB_STR}, {.name = expires_col.s, .type = DB_DATETIME}, {.name = q_col.s, .type = DB_DOUBLE}, {.name = callid_col.s, .type = DB_STR}, {.name = cseq_col.s, .type = DB_INT}, {.name = flags_col.s, .type = DB_BITMAP}, {.name = user_agent_col.s, .type = DB_STR}, {.name = received_col.s, .type = DB_STR}, {.name = instance_col.s, .type = DB_STR}, {.name = aor_col.s, .type = DB_STR}, {.name = server_id_col.s, .type = DB_INT}, {.name = avp_column, .type = DB_STR}, /* Must be the last element in the array */ {.name = NULL} }; db_res_t* res = NULL; db_rec_t* rec; db_cmd_t* get_all = NULL; struct socket_info* sock; str callid, ua, instance, aor; str* receivedp; qvalue_t q; unsigned int flags; urecord_t* r; ucontact_t* c; get_all = db_cmd(DB_GET, db, _d->name->s, columns, NULL, NULL); if (get_all == NULL) { ERR("usrloc: Error while compiling DB_GET command\n"); return -1; } if (db_setopt(get_all, "fetch_all", 0) < 0) { ERR("usrloc: Error while disabling 'fetch_all' database option\n"); } if (db_exec(&res, get_all) < 0) goto error; rec = db_first(res); if (rec == NULL) { DBG("preload_udomain(): Table is empty\n"); db_res_free(res); db_cmd_free(get_all); return 0; } lock_udomain(_d); get_act_time(); for(; rec != NULL; rec = db_next(res)) { /* UID column must never be NULL */ if (rec->fld[0].flags & DB_NULL) { LOG(L_CRIT, "preload_udomain: ERROR: bad uid " "record in table %.*s, skipping...\n", _d->name->len, _d->name->s); continue; } /* Contact column must never be NULL */ if (rec->fld[1].flags & DB_NULL) { LOG(L_CRIT, "ERROR: Bad contact for uid %.*s in table %.*s, skipping\n", rec->fld[0].v.lstr.len, rec->fld[0].v.lstr.s, _d->name->len, _d->name->s); continue; } /* We only skip expired contacts if db_skip_delete is enabled. If * db_skip_delete is disabled then we must load expired contacts * in memory so that the timer can delete them later. */ if (db_skip_delete && (rec->fld[2].v.time < act_time)) { DBG("preload_udomain: Skipping expired contact\n"); continue; } q = double2q(rec->fld[3].v.dbl); if (rec->fld[4].flags & DB_NULL) { callid.s = NULL; callid.len = 0; } else { callid = rec->fld[4].v.lstr; } if (rec->fld[7].flags & DB_NULL) { ua.s = NULL; ua.len = 0; } else { ua = rec->fld[7].v.lstr; } if (rec->fld[8].flags & DB_NULL) { receivedp = 0; sock = 0; } else { receivedp = &rec->fld[8].v.lstr; sock = find_socket(receivedp); } if (rec->fld[9].flags & DB_NULL) { instance.s = NULL; instance.len = 0; } else { instance = rec->fld[9].v.lstr; } if (rec->fld[10].flags & DB_NULL) { aor.s = NULL; aor.len = 0; } else { aor = rec->fld[10].v.lstr; } if (get_urecord(_d, &rec->fld[0].v.lstr, &r) > 0) { if (mem_insert_urecord(_d, &rec->fld[0].v.lstr, &r) < 0) { LOG(L_ERR, "preload_udomain(): Can't create a record\n"); unlock_udomain(_d); goto error; } } flags = rec->fld[6].v.bitmap; if (rec->fld[11].v.int4 != server_id) { /* FIXME: this should not be hardcoded here this way */ /* This is a records from another SIP server instance, mark * it as in memory only because the other SIP server is responsible * for updating the record in database */ flags |= FL_MEM; } if (mem_insert_ucontact(r, &aor, &rec->fld[1].v.lstr, rec->fld[2].v.int4, q, &callid, rec->fld[5].v.int4, flags, &c, &ua, receivedp, sock, &instance, rec->fld[11].v.int4) < 0) { LOG(L_ERR, "preload_udomain(): Error while inserting contact\n"); unlock_udomain(_d); goto error; } if (use_reg_avps() && ((rec->fld[12].flags & DB_NULL) != DB_NULL)) { c->avps = deserialize_avps(&rec->fld[12].v.lstr); } /* We have to do this, because insert_ucontact sets state to CS_NEW * and we have the contact in the database already * we also store zombies in database so we have to restore * the correct state */ c->state = CS_SYNC; } unlock_udomain(_d); db_res_free(res); db_cmd_free(get_all); return 0; error: if (res) db_res_free(res); if (get_all) db_cmd_free(get_all); return -1; } /* * Insert a new record into domain */ int mem_insert_urecord(udomain_t* _d, str* _uid, struct urecord** _r) { int sl; if (new_urecord(_d->name, _uid, _r) < 0) { LOG(L_ERR, "insert_urecord(): Error while creating urecord\n"); return -1; } sl = hash_func(_d, (unsigned char*)_uid->s, _uid->len); slot_add(&_d->table[sl], *_r); udomain_add(_d, *_r); _d->users++; return 0; } /* * Remove a record from domain */ void mem_delete_urecord(udomain_t* _d, struct urecord* _r) { if (_r->watchers == 0) { udomain_remove(_d, _r); slot_rem(_r->slot, _r); free_urecord(_r); _d->users--; /* FIXME */ } } int timer_udomain(udomain_t* _d) { struct urecord* ptr, *t; lock_udomain(_d); ptr = _d->d_ll.first; while(ptr) { if (timer_urecord(ptr) < 0) { LOG(L_ERR, "timer_udomain(): Error in timer_urecord\n"); unlock_udomain(_d); return -1; } /* Remove the entire record * if it is empty */ if (ptr->contacts == 0) { t = ptr; ptr = ptr->d_ll.next; mem_delete_urecord(_d, t); } else { ptr = ptr->d_ll.next; } } unlock_udomain(_d); /* process_del_list(_d->name); */ /* process_ins_list(_d->name); */ return 0; } /* * Get lock */ void lock_udomain(udomain_t* _d) { lock_get(&_d->lock); cur_cmd = _d->db_cmd_idx; } /* * Release lock */ void unlock_udomain(udomain_t* _d) { lock_release(&_d->lock); } /* * Create and insert a new record */ int insert_urecord(udomain_t* _d, str* _uid, struct urecord** _r) { if (mem_insert_urecord(_d, _uid, _r) < 0) { LOG(L_ERR, "insert_urecord(): Error while inserting record\n"); return -1; } return 0; } /* * Obtain a urecord pointer if the urecord exists in domain */ int get_urecord(udomain_t* _d, str* _uid, struct urecord** _r) { int sl, i; urecord_t* r; sl = hash_func(_d, (unsigned char*)_uid->s, _uid->len); r = _d->table[sl].first; for(i = 0; i < _d->table[sl].n; i++) { if ((r->uid.len == _uid->len) && !memcmp(r->uid.s, _uid->s, _uid->len)) { *_r = r; return 0; } r = r->s_ll.next; } return 1; /* Nothing found */ } /* * Delete a urecord from domain */ int delete_urecord(udomain_t* _d, str* _uid) { struct ucontact* c, *t; struct urecord* r; if (get_urecord(_d, _uid, &r) > 0) { return 0; } c = r->contacts; while(c) { t = c; c = c->next; if (delete_ucontact(r, t) < 0) { LOG(L_ERR, "delete_urecord(): Error while deleting contact\n"); return -1; } } release_urecord(r); return 0; } kamailio-4.0.4/obsolete/usrloc/doc/0000755000000000000000000000000012223032460015665 5ustar rootrootkamailio-4.0.4/obsolete/usrloc/doc/functions.xml0000644000000000000000000000047612223032460020426 0ustar rootroot
Functions
kamailio-4.0.4/obsolete/usrloc/doc/usrloc_states_w_zombie.png0000644000000000000000000002241212223032460023161 0ustar rootroot‰PNG  IHDRƒ€º?£PLTE444ÿÿÿÚ«ÖbKGDˆH pHYs  ÒÝ~ütIMEÓ $âRqc IDATxœåMÇyÇ{=FN BÁ›­¬cHèà]x͎რ¿‚%b.HóB‹íQ6ÐÚ€¼ƒÀ9DÁ>ø $ðÕȽG(‚\‚ ˆf<†æb˜ÝAÓc6»ÒUýVO½×°g '}Øíî©é_wõSÿzê©—ñ°û–zÕæ»yâ­Aì×Ä5¾½1íÖ»Ú 1 šýÞVˆSfÿb+ijo…8`ö“­³Ÿ[ f¾ê`SDøX¨bß×ÁQØ1õºâÉfƒÆ2P¤r"FÞŽî++p4nƒ8ÔË%,‚­s¡4_Y€£yļJ@[%f['’úVW%Ì5GëuÅ2–-'âB‘Ê•ØÙ*1² NèÞªE¢Ntqèo›nŸøõÿòQï‡-'ÄÁâŸ^}©»³EâpñÁã_~zk‹Äè|”~zw‹D|ðGé¶I ?ȉÙV‰÷¾Ÿ¿Ç¶ˆ6¥ãì0'¾uu‹ÄáÑ÷Ÿ\j­tØWÆçôh[JÞ8­[«­j‡®-¢±Fn•¸}¯#Ûºgµ}ïâ4 Z3p4U¤r"N´- ªÃ‡A D}k.z >F-õ-Öþ>8¸Æœ[å©wŽC×’”¨Ó‘‰w°Ç~´i¢çqÑ•ÄW¤l¸ÃE2¤HÙ±¿ÃGÉN7LvøH £¸»|´3 6Kœô"/©¿Yb¿¢Ö>v#»Û*"ónàJÌö}&Ñ ÝsÓWbrT‰§3Dô\°IbœU—êo»i€+ñ"«ö>#Ô¢–qÒWbsñÏâhR¢“¸Q½÷¹_LǑȔö¥O*Ëw¶u"±^"âh¥ßE‰LüxâB°›8™K/I¼º_<ÝdsD¦j\’JkXH¬‹¸Ù²¾¤ÁëÂùv‰¬Q.IªèN@4ÀÈv®,‰EåÓ=Ø‘—åŠÞBa:àFd=œ% söÕ‡‘³‚U { p"ÂÖqÙÙY<½8¹r^H^ùàÚNÒµ‰œAe¥,ÖÕ‡ ö‹/«0‡£¸¡u,«×ê¨.DØ»¬ŽKÓ±Õ"4ÕeÕ…]>úiq²U"4ÕeU“°À•Ÿ%"ŽXEu‹ü¤:ÂÆÍ’HÎp¶±ÄUƒ 0ú¨íInqB¶¬K S} ž¶E$ ‰J"£óCZ{XP•E=Ф4ò¨¨Uâ@HT)Œ˜<*ký™”È׋fLG]}dþ™ßq ÊØ¢‰¦“œæÄ=]¿…‚y8‰c¸My/n&žâìøP;ÐFEì Äwg¢_±`Þ7Õ€ŸÙµý;îÄÄëñÄÌ›Š…{ÁŒ ¢f|2ñ³Ý;Æál²÷(ØjvxKÞ´`2š>ÿƒQ{·RlØ$Äs˜íK™úêTŽ†Ë <ŒSÓ = ‰Ä( dCñêsôv~tád–D­§÷™WѸ!D( žÏ’á­6ˆß¾/wk"1„òG½ˆÿî«ÎÄOÌ{¡¼BÕɽoæz÷  ! ^B¡¼)SÛo®yYÌ3â‡ÈÐzµ!&—|$QY–#šzxEÁå—|w¢PwÄ£K¾Ø® ‰š¬>Í‹ÐÍ^êï^ò ãmˆ‹Y_bª$QÓ8Åéé_òóêCïGÚ;Ó¡ÄI[€„y½:›á ? võ¦#eBbÖ‰eÁ ’¨qDR?»:šø“¼Œ^»îJñYz=‘ Œ%‰˜až=üÖh˜k@vÍO´C…%Äþ) ÆQœS¥esxgƒÑ ·Ù—ƒT«sâ=èD“üž%M`JlNOñ yî÷oiÃtâîæj”’aã$Q4§Õ!>À“ÉËÚx’„x‰øNþ$’ö(MäׇY.hË]ráiÛ’fâµýc_6Ú˜&BÍñ)>G{¹àÑt¸wá{ÜÛ l 7M6ÇyÁÈ‹bâ_h€RË™â—ö|$kÓDÌ(ä§Çû×ñ žÍFºa ²Ò}¹“:—™;MÄÔa©Ÿ_]å6u}7âúr§¹Ã$«Ži"öaz8½™ä6›tº1²1H»£4tB±r{Š“7÷—¹Œ÷®ùNDNÉ2IÆÓDl9âä/Ïpvý"ÆñSiŒ&bÇÏ'ÁjÞŸø×‡=7â¯á{Œ"Í$Ö¤rƒžO‡~„³ýS7â´Õ$ÑÄ£À½ôð,þ¹Ÿ×[—§nÄ÷ßÄôHùk0Õ†(|UÿÑ0úµ!Ê<…ªúð…|•‹ÙÁ†Q/ QVÅTÕÇž0Î\N È_dK”X+ï÷=¢iU5†H>. ÿDÈ"%É%Ls ¢ôÞªêC)'’?¦õø˜ŠXF¬LG¸¾šhèÏ‘ìórð¾Ðm.%Ò`Œ©:fˆ¡ìó‹’èÛé[GöDéúkQy}þ:J¢qfC”V2…éL„e1¤Db4Æq¨ Q:Œ£Ð€HXÝ@é«c†(Õ_h@Ÿ/J¢Ë”FyA*ç}˜ˆI=›Ú8hºÙ$>2¹rµõ¸ó03Û·&Ê„5k.‘™‰’ÚOL“ k‚ê]8#²bj?1Mâ#Ë÷Îáèk¢/žcË(¨i9"k¡ "ñ+’À›àˆl:C#€5ÐP ôpDö«ÖÞ£LõÁ«¶ÌYEa >–¾¾b‰sa‚0pYŽÈÞŒaP–8ÃIWý)Ì:H„âaÛÒ!¦½?…R‹,‰ú%ŽV`ÃÕI¨$¼°'&x´‚5tèY‹˜xì]=Åd6 1Åãš¼Û! øŠ«¸³`Ì­© ‰lŽ÷2ˆþ€ëUÝñ9{à9݇vÄΦÓŸaÝˆáˆ«Û ‘}â–ˆƒ §9'ò†ãFL*›p Γ#ø”8Ù Î7Eœ%Ö‚x)ð­ˆw­ˆ`[à TJœ°ñ`ú?·‰1B(ˆŸüMglA\ ßp$&øßà4«’ø¦ñƒûáJLñÙ]p¢$Þ8øØ†øÃûI‰Rç^A ÎàÒšÚõÿúOÈŽÃÚ&¹°~m¯ ¾óM+â4œÒ/ñX"»½"ÕÙ ‰Yåþ…@„>¹šHjuu@±ï•DɪV@߃ßé}†–DŸ•DÚ"þ1øð>ÈÕæ|¿ãÒšãˆáâIÀ~ ~ÈXÛšë#Þз‘9â`ñùrúEð!JD"É÷>×*ÔŸXG‹rF ­˜èøTÆ1+'„4[HÿªBWqG¿ êôXˆ6 %¦€˜’n4Ä&,,×W%ñ„¦¤%xX²å/+•1l>Wlç{Äé—©ZÅ©ÌÞgj‘Ù—V™‘åȇ¬GÝŽB2¼±wÃÓô{Ýè;Û¤uå+¹±séòç ÂJ35õ6·.£}·]ëÄ›I×»BfgG ]¹YÓ £ %#Š÷6ŠyJÝaÏ»²ß)‰³Qwï0‰aW³õ UtË…Õ¯"u ò‹RÆ¥œ1-ˆ·âÁ{þaL^0#ˆ¿¨žÈ.SL®Ê\)(g9#.‰«ÁD&¾Ó\@ÊæÊ#ö§iØ#öØÔJùÛJÓÛñ`ŒIA¼“è`œ—`.&íûà‰á,/l¯c¹¦(½ ÂÇ”QûщÒJ’'fCTÝ[Jˆ©@ô' ±S‘H”úC¼skF‰Eéðn~ûç/]Þ½ü%Q¶ñÄUv§VåøO$ŒK—÷¿Eˆ™w³;éîìkP¤ë“ìð;Õ>•çˆ!RF2HY,P’×ÅŒjAgH”¸t|‡\š]«ë5ê!•³B(ã”0hmæ¬H‰‘ˆÁªöG±aÄDN+Iž˜ùÍòôÔ‚&¶ÄŽ”(f«ÐÍé7€^º÷BDÑ÷ˆMS§P‘Ý"Q$T{…‡4Äz‰;r¢Ð`ˆM S@Ê5v×& ›@l¼²Ô‰8QqÁ&ß o±oKôD“K÷´±¿"BWŽvXŸh[Úkâr±q¨"Æ]¤>?dϳ$ʼŽb3LÞfL©hx²^ÇzD}¶ž2½Åã–ˆÚ~e² U¶@„7ÛšýJ¸)"w©Æ‘ö[#²¡ˆU*Œ ËÿY{DvŠáó4cލ Á˜j+D¶’LSöEÒ²Z k þ½Ø{d<å,e]«¢³¡’µÀ•¬f^ÇŽ¸(lue°œäøNßó^ºËÇ?Èó•.^Ñ-†ç&ËyV–Ž?Ó•RíäÄ/Üæ#’DXKë}Z§¦Ò— àj«‹ñu>€r¹ªcd“ëEÛÊÏIöË’ZW+í®û;óá¢ñåfv~N~“•€‰¨˜ …ÊÿäùnD\d§·¯(Hz¶ÞÈ;¬KgAœÚWŠ(™0$‡ÛˆÑÖr[(€)J†«’Ù—“m3åÔ*˜])ë "òóâùRaFIi«£M  ¥5s"V÷©ˆv‚/"ÛøñÙÆhgù…$•Ú*(ÿ€3§›`Ù¶X¥DÎf‚æŽB6±îÀÚ¾Ðè‡."Š'}¬Æº£$â‘”ˆÔDŠY€û²«;°®î€­bH¤^Hê&x" PA"UšQo‡‡ÌJ\¹1¸±ˆ0Ð(!†ôï &ho\‡d– ¢76vå¹x賤“úÚâʼn\àŸ#fµ;ÞÞ8+¤%æPeg[DÃ2eyÙ©Œ4l‰hZU3®S´5&P¿ìó<ª6I[ãõ«”¥70ªµ¥±†µßÒ.T•FKãW +êe=ü´Ê…–ÆèʦαÛiSù¯Ú!š¶¦À¶9֚ݸ5®4 ×´¢˜©Ü:ûodÕD½R+^(–ŽxÜI«u‰Ú™ –Žˆ;ImέÅò‰dM»ºÈ¢ˆ’ò‰Qg|u[mÌaá ÄÄg"mÌÓ‘TÜ{DÌèƒQ DÉò\y!¿ÚZU¡³ˆ¡‘xÂ×bâP8¯'¾;aîŠmÛá>7RIEÏ뉻¾û ¹1<-³ý礕“…Òµˆ /ÎMÄôNHÞW¥oCZÁJæÒúcD¨HˆÜ˜l+¢Ôq„ÄÙ€ªêˆ¹†+‘Í “ãH‰+&%bˆKbÂõ ±Ò«§øiÜR C–È­ˆÈLì…ThÊ7>^‡ÈäO-Ý2Tz«Ìš;%1²$¦ Ñà8ÒÍ+V[cƒ¹õk”Òí´_.íRô¶ÌÖ!:¬Ø—oÝ û˜!&–Díbv–ý!¬lVÏ¢Ó®Od ;B•.ÑlMfkQ}bɧ‰é^U ¯ð[ ‘·teÜå£AÕ$/ý`Ö]á%Ò‚ˆøâõqÝ÷ê¯Kô«cUÃ;ãZ{É Ò%º\ýœóêXUþåôÆu¡%/!b‰Ü÷x"³.[yFÕþ‡k‡ ·ŒxIÍ…`•Ìì×ARµÿYâü7¨¶/òÞ&ü:Hj¢¸ÖSLÔĉ_G´È{O¼®õ¤&²ÃBŠhÊŽ#–ؿɬ˜uN#ûp=+5Q¶f—ñ²ËP×ìÒ­Þ/[—Ì‚x q>¼t]2)Q¶öšÑïr‰‡×ØcPÀ8"âÔý ñypÁfHþ½É!û=[\O¸Ó¿=1õglÔ|$YCOId?¢þ‘:þ×óº ÜÙL²N Š…õñ¿ò[×zÀ8H›[ QM鿨Æq$9Wn%ï†[ïQI„omn— ÀÜïñkZª‰+nfãÿ“í"‚Õöˆ¯ÆÙ ¾Ž¸°ýuÄ #«d‹ukéˆú…4«-9Böø¹=Ñeõ~fË+Ä‚}\­sSñ¹3ê•íuDý:Dì8Äp]¢®ü³ÄWŠ{bFÏÇx1§DdI\p‘i0& P2ú ¿r!jËÿJyo¨!dú\%_=ÏK%jË?€ð)GcªXù^èˆñ"ÿûÑ<Ï´k-"oÖÓ1f»a’û•Ÿè‰³á¤óîãÞgóÎÁÇÏ´ òkw$c<èx¯¾tÓ?è÷|q~ööÁ½矿}p5üoMäÕ0o îݾüøí9ñþ\Kß¿qð³GóÏ¿v÷j¨oÿ¢ †|íîÁãOo„9ñ“@G ï¿sð³»Ë¯íüch¯büuœ­râ›ÈHD%ñ½¿÷ˆH ¦iMüTGÌ|B|4_¾w×…(:šã$«s5Ò*@pö±œåÇ.Ä@ ÆÙíƒÜrÐýƒû‰Ž˜zçyéXÎ;O{¢(Nã¨Ó9xœ—ŽþÝûz%_ÂåSë³'ŠV=¦n`Hõöîj+^VaqYµDd7Q€YâlDÑsg‰ó ŵÓ×óìýÑÉ+Ú!Š•ÌÚþªånˆZ"Šö Ø$íwh½¡’(¶æÔsw’¦»ío±A¢¾™Ã%>Ÿíï͉­r+¢ÄY€ïÇWÙì7P”UxದèŠQbÔ £5ÑðŠ $†( ˆ€/ë"HlÌÎ`ˆ2£F̾.JÆÞ­¡óˆ!ÊLÌ6(F;•S³ÈLÌ6ÚÉŽ¦0ü+C %Oš]Ø[ÃG­›2SÏCô%'¨ÞÕþæ,ûK0-QjÔ¶¿«‹“:™¸ì˜’(½·æ—zÜyÅ¿Üe“˰Òò”Dc|¬!*Š‘b¾‹’(¤"Jd•l ËS Çnr_AuõˆûNVyœY•Ij¢]ØQRej¢ý2Ëkn•±«‰†.f“×ܪú\M´éPnHvRÙæ&*¿"l¾ì¤²vÕŒn²éx ›¼(_ІhйZäO£ìšÑ :×¥+4NK4è\M”˪R@4DƒÎÕDi^¨-]7.N¯s5Q*«ê Òõ:W¥6¢6Q¯s5QzcjC×õkM”ʪº0ëˆz«‰²‹kK;¢Rý5–(K¥iAh‰ÊR ˆ¾äCMÅ£%ju®"J}GÍô-Q[EVDijrGKÔê\M”}ˆÖ$ju®"Ê|G•ëGÿêt®"ÊÔEW’õDN&«:µÒmü9Y“\gäz¢Í¨NY>èÞ†žhãÏIdUë”Æë¾Zn¡xJ%1µ:WlH<¥U‘ª•þmúŠo­IŒê?ªM&«Úœ1©Î=Ž'-€{gTÐ ©Õ]Hs©$rYNJ°ÞÂMsHÉz •ž’ÈÉê‰xŽDêr•“U“Ñ+•‰H¾ý£s ‘ËñSlRc‘äPiˆœ¬šÞ³6‰ìéˆÜ]žÆÙ1ˆYOMF„¾cfl!‰§8=º£!†à¤wË@0gä—5DÄžËv¯˜¦›‰N’ ’|P® îâñ¾QýÄ4Hâ©ÌúJbˆwŽS g$f(½>’•°rª ¦7å›jqóL®^öÖ©š-ýöY`òTÌÄ < Èj¶×½13qŠÇ¨ùeGžd5½Ô=N31É_å©>E!é¢Ð%5S?ó‘’du5ë!C„ĆˆQv¬&ßñѬï£ÝDò£âJ"ÕN< ŒAR b®sǾêÃÙÏv‹^ b®sÙ>R|ÈÜÊ*»•g©Ñõ æ:—ÞTôð°v’DIjÔ8+b†ž'S±”ÑòÈÊê*JŽgE$ JHJMò$=6jœñ`½~IDATÇÄvi1ß‘9q¾Ÿ˜¹ÅiCŒñì·åDVV÷v‘E«Ú†˜‹÷ßøXJdýË{y±5÷Ùskt„è…±¯÷Ý -z»lˆùN„_• DVVON‘¹GÏ’xŠs!%²²Ú lFõ[gxô¯—¥D6¯Of#³ÆY#<Ùý¦”ˆ˜Ó\&,ze¬ˆiíR¢Ïœˆã§6=OVÄÜŸÝrB„ãE«hŒ1Ï,±Ú#D8&æÈ\9Z/$FH‰ì‰ôo­¢jvÄXÒ˜ D8Ò¨k9´#&\Éá“w­z»íˆ2ñj~©Úl4Ζ¨ºy.¯-4Κ¨ð^¸÷f5sÉ’¨0B.Òi¡qÖDEAh_ˆ¨ˆ˜"pd×»nIT\ÌGvSÞ,‰ò ãJƒÝ( [¢h á¡ìF‚Ø¥ºÊu Y„¸ˆbá^‘N»=¶DQÀüƒ[ŽZ²%аäeÕJãˆBE´ä#v³3퉂!.ù»°Ò8¢PØ–¼¬ZŽ®±& NÓ’‹tÚŽ ²& \r²j§qD!Ó–œ¬Ú޳'ò†±ä"†@îDÞø—\>Úiœ ‘/àKNVmG-Úy[BYµ^gOäb óÙR㜈œÎ-¡¬ZjœQÐ9 «–çD‚Cà¡mGºÛ _  …‹†ìí eœ½ûÑ®.DÎ8@µÕ87"÷@Vm5Î ù¼:´"Ô¹%[Zì‡;¡Î-Á´Ò`3DPä—¬®Ùw#‚ë.Yó´Ö8G"й%+«ÖçHö±dòØ^㉠ ,™ç²×8W"[Ηa³o¯q®DÖX–¾ü|»DöY>gˆöçJd úçÌ>Ú‘µÉÏš]s%â^3¨Éá§6C×%ŽiköFçLœNêÝÆw<±ŸÒãNLÑc·ãŽÓÚŽÄ4'$€˜Óªµï6BÄžÏÎlÄd¸ZfšEø‚DÄ÷ˆü”ꆉœH‚d£Ï˜y=®U—ù‰çKÓ¶CL½._3¡hÃÄ/0ƒÉFm5'ò Åñp£Ïˆ‡;|ïÎ|(tø¶JL<¾ö÷ÑF‰¸ƒ;±p¼‚31z;ñ©Û¿îD!nãÐX“È‘WÌÔý&†¿u¢éÇKÚ'*f•ÿnÉç‹çÕ ó-½­I¾9?ÜùgßóÙNÓãàõ-³ÃKÞ•?büÇû—w»Û Þ}øÖÕ΂ΓoüøÃшéê“ÅO?º ?¹ñÆâÑ6ÞãjõÉò§õƒä“ù×£W¶AŒ âññ_m‹8£ÄûÇÇ7çoüÇVru¾z¸ü—Æó7þáñ6,gøê¥å?ç¥Ã[Í÷÷·R:HŸ\,ðÅ ç ào³î?b³âÅÖ‰àLj¶BÄ¿%âÿEïñÿ#Ñ~¥½u‰üüãÍù«Küx=¢Ð*ß8QXúxãD!º²q"¯jhãD!J¶q"÷P.Ý9ëùhçæ‰°çïÁˆ`:f ¼ÍS'G[ ²¿€ã¹E)ñ›qˆ0>ÅÉ]IEND®B`‚kamailio-4.0.4/obsolete/usrloc/doc/api.xml0000644000000000000000000002575512223032460017176 0ustar rootroot
Usrloc Module API
<function>ul_register_domain(name)</function> The function registers a new domain. Domain is just another name for table used in registrar. The function is called from fixups in registrar. It gets name of the domain as a parameter and returns pointer to a new domain structure. The fixup than 'fixes' the parameter in registrar so that it will pass the pointer instead of the name every time save() or lookup() is called. Some usrloc functions get the pointer as parameter when called. For more details see implementation of save function in registrar. Meaning of the parameters is as follows: const char* name - Name of the domain (also called table) to be registered.
<function>ul_insert_urecord(domain, aor, rec)</function> The function creates a new record structure and inserts it in the specified domain. The record is structure that contains all the contacts for belonging to the specified username. Meaning of the parameters is as follows: udomain_t* domain - Pointer to domain returned by ul_register_udomain. str* aor - Address of Record (aka username) of the new record (at this time the record will contain no contacts yet). urecord_t** rec - The newly created record structure.
<function>ul_delete_urecord(domain, aor)</function> The function deletes all the contacts bound with the given Address Of Record. Meaning of the parameters is as follows: udomain_t* domain - Pointer to domain returned by ul_register_udomain. str* aor - Address of record (aka username) of the record, that should be deleted.
<function>ul_get_urecord(domain, aor)</function> The function returns pointer to record with given Address of Record. Meaning of the parameters is as follows: udomain_t* domain - Pointer to domain returned by ul_register_udomain. str* aor - Address of Record of request record.
<function>ul_lock_udomain(domain)</function> The function lock the specified domain, it means, that no other processes will be able to access during the time. This prevents race conditions. Scope of the lock is the specified domain, that means, that multiple domain can be accessed simultaneously, they don't block each other. Meaning of the parameters is as follows: udomain_t* domain - Domain to be locked.
<function>ul_unlock_udomain(domain)</function> Unlock the specified domain previously locked by ul_lock_udomain. Meaning of the parameters is as follows: udomain_t* domain - Domain to be unlocked.
<function>ul_release_urecord(record)</function> Do some sanity checks - if all contacts have been removed, delete the entire record structure. Meaning of the parameters is as follows: urecord_t* record - Record to be released.
<function> ul_insert_ucontact(record, contact, expires, q, callid, cseq, flags, cont, ua) </function> The function inserts a new contact in the given record with specified parameters. Meaning of the parameters is as follows: urecord_t* record - Record in which the contact should be inserted. str* contact - Contact URI. time_t expires - Expires of the contact in absolute value. float q - q value of the contact. str* callid - Call-ID of the REGISTER message that contained the contact. int cseq - CSeq of the REGISTER message that contained the contact. unsigned int flags - Flags to be set. ucontact_t* cont - Pointer to newly created structure. str* ua - User-Agent of the REGISTER message that contained the contact.
<function>ul_delete_ucontact(record, contact)</function> The function deletes given contact from record. Meaning of the parameters is as follows: urecord_t* record - Record from which the contact should be removed. ucontact_t* contact - Contact to be deleted.
<function>ul_get_ucontact(record, contact)</function> The function tries to find contact with given Contact URI and returns pointer to structure representing the contact. Meaning of the parameters is as follows: urecord_t* record - Record to be searched for the contact. str_t* contact - URI of the request contact.
<function>ul_get_all_ucontacts(buf, len, flags)</function> The function retrieves all contacts of all registered users and returns them in the caller-supplied buffer. If the buffer is too small, the function returns positive value indicating how much additional space would be necessary to accommodate all of them. Please note that the positive return value should be used only as a "hint", as there is no guarantee that during the time between two subsequent calls number of registered contacts will remain the same. If flag parameter is set to non-zero value then only contacts that have the specified flags set will be returned. It is, for example, possible to list only contacts that are behind NAT. Meaning of the parameters is as follows: void* buf - Buffer for returning contacts. int len - Length of the buffer. unsigned int flags - Flags that must be set.
<function> ul_update_ucontact(contact, expires, q, callid, cseq, set, res, ua) </function> The function updates contact with new values. Meaning of the parameters is as follows: ucontact_t* contact - Contact URI. time_t expires - Expires of the contact in absolute value. float q - q value of the contact. str* callid - Call-ID of the REGISTER message that contained the contact. int cseq - CSeq of the REGISTER message that contained the contact. unsigned int set - OR value of flags to be set. unsigned int res - OR value of flags to be reset. str* ua - User-Agent of the REGISTER message that contained the contact.
kamailio-4.0.4/obsolete/usrloc/doc/usrloc.xml0000644000000000000000000000237212223032460017722 0ustar rootroot
Jan Janak FhG FOKUS
jan@iptel.org
2003 FhG FOKUS
Usrloc Module
Overview User location module. The module keeps a user location table and provides access to the table to other modules. The module exports no functions that could be used directly from scripts.
Dependencies The following modules must be loaded before this module: Optionally a database module.
kamailio-4.0.4/obsolete/usrloc/doc/usrloc_states_w_zombie.vsd0000644000000000000000000051600012223032460023172 0ustar rootrootÐÏࡱá>þÿ þÿÿÿ ƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot EntryÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot EntryÿÿÿÿÿÿÿÿÀFðÔÊãëDÀVisioDocumentÿÿÿÿÿÿÿÿ„mSummaryInformation(ÿÿÿÿ ˆðDocumentSummaryInformation8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿþÿÿÿþÿÿÿþÿÿÿýÿÿÿ  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€VisioInformation"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿ þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿÕÍÕœ.“—+,ù®DÕÍÕœ.“—+,ù®L@€HP\l x ÀäNO GmbH ZeichenblätterMaster-Shapes Aktivität-1General.-pfeilDynamischer Verbinder Navigierbar GemeinsamZusammengesetztZusammenges. navigierbarGemeins. navigierbarQualifizierungGemeinsame Qualifiz.Zusammenges. Qualifiz.Zusg. navig. Qualifizierg.Gem. navig. Qualifizierg.Navigierb. Qualifizierg.ProzeduraufrufAsynchroner FlussFlacher FlussTeil-Shape_realisierenWatermark TitleUMLAnfangszustandZustandKontrollfluss ObjektflussAssoziat.-rolle Endzustand¼8Œ€”œ¨´_VPID_ALTERNATENAMESZÎe_VPID_PREVIEWS_PID_LINKBASEäA ‚þÿÿÿýÿÿÿ…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿþÿà…ŸòùOh«‘+'³Ù0XðHP`lx„äüälandoUC:\Programme\Microsoft Office\Visio10\1031\Lösungen\Software\UML-Modell-Diagramm.vstusrloc_statesGTïÿÿÿÿŒ¨w‚‡ EMFLïl@ð
VISIODrawingL¬î¨w©x Ì€?€?ÿÿÿd(Œ î(©x îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿((((((¯¯¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ ÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ××× çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§§§XXXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿXXX§§§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§§§XXX@@@ÿÿÿ@@@ÿÿÿ@@@ÿÿÿ@@@ÿÿÿ@@@ÿÿÿ@@@```ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÉÉ’’’ßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßßß‚‚‚»»»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§§§¹¹¹§§§ ŸŸŸÁÁÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïï```XXXïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷pppHHHçççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···(((ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ TTTŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···(((ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïï```HHHçççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÑÑÑ(((çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïï```HHHçççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿(((ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßß···HHHÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···(((···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷xxx@@@mmmÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿhhh———ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpppHHH```ïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ000‡‡‡(((ÿÿÿ(((ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿ÷÷÷(((ßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ000ÿÿÿ(((ŸŸŸ(((···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷xxx666×××ÿÿÿ```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿ¿¿¿@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxxxÿÿÿçççFFFhhh÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ000‡‡‡ÿÿÿ(((ÿÿÿ···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿÿÿÿhhh———ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇÿÿÿ ÿÿÿ———000ÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡@@@×××ÿÿÿÿÿÿÿÿÿïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿÿÿÿ÷÷÷(((ßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïÿÿÿÿÿÿÿÿÿ×××888xxx÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××888xxx÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ888ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡000ÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡000ÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxxx‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××@@@‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxxx'''ÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷000×××ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏ666hhh÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯HHHïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿(((ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ888ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@§§§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷XXX———ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ```‹‹‹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÑÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹‹‹```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ```ŸŸŸ@@@ÿÿÿ@@@ÿÿÿ@@@ÿÿÿ@@@ÿÿÿpppxxxïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷888ÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡ pppÿÿÿ@@@ÿÿÿ@@@ÿÿÿ@@@ÿÿÿ@@@ŸŸŸ```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯HHHïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···000çççXXX———ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏ888÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···(((HHHÃÃÃAAA———ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷PPPŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ888ÏÏÏÿÿÿÿÿÿÇÇÇ@@@000¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßßßHHH ···ïïïÃÃÃHHHxxxÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿššš¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯ PPP×××ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§§§ÿÿÿ666888ÏÏÏÿÿÿïïïxxx‡‡‡÷÷÷ÿÿÿxxxÿÿÿÿÿÿŸŸŸ```ÿÿÿÿÿÿÿÿÿÿÿÿ]]]ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMMM}}}ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷‡‡‡pppïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿXXXÿÿÿrrr000¢¢¢XXXçççÿÿÿÿÿÿÇÇÇ888ÿÿÿÿÿÿÿÿÿŸŸŸ```ÿÿÿÿÿÿÿÿÿ÷÷÷ ÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ```ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçççhhhÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇÇÇÇÿÿÿÿÿÿÿÿÿ÷÷÷888ÏÏÏÿÿÿÿÿÿÿÿÿŸŸŸ```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ```ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××HHH(((¯¯¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçççhhh———888ÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ```ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···(((pppÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§§§xxxïïïÿÿÿ×××000÷÷÷ÿÿÿ×××000÷÷÷ÿÿÿÿÿÿÏÏÏÿÿÿ```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@ÿÿÿÿÿÿ```ÿÿÿHHHŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸÏÏÏïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ××××××ÿÿÿÿÿÿÿÿÿÿÿÿpppÿÿÿ888ÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ```ÿÿÿÿÿÿÿÿÿ@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ```ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@kkkpppÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸÿÿÿccc```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@ÿÿÿÿÿÿ```ÿÿÿHHHŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···‡‡‡§§§...ØØØHHHÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÃÃïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ```ÿÿÿÿÿÿÿÿÿ@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ```ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿààà<<<  """mmmÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@qqqhhhÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸÿÿÿccc```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@ÿÿÿÿÿÿ```ÿÿÿHHHŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÑÑ???cccÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ```ÿÿÿ@@@¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ```ÿÿÿÿÿÿÿÿÿ@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿ```ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸFFF¢¢¢@@@çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿççç(((ïïïÿÿÿßßß000ïïïÿÿÿÿÿÿçççÿÿÿ```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ```ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ŸŸŸ···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçççeeeÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···xxxßßßÿÿÿÿÿÿÿÿÿXXX§§§ÿÿÿÿÿÿÿÿÿŸŸŸ```ÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ```ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××hhhHHH¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡000÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××hhhHHH§§§§§§XXXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHHH···ÿÿÿÿÿÿÿÿÿŸŸŸ```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ###÷÷÷ÿÿÿÿÿÿÿÿÿ```ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ŸŸŸ888xxxßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ŸŸŸ444UUUßßßÿÿÿÿÿÿÿÿÿÿÿÿççç(((ïïïÿÿÿÿÿÿXXXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@ÿÿÿ@@@ÿÿÿ@@@ÿÿÿ‡‡‡ÿÿÿÿÿÿ¿¿¿www¿¿¿¿¿¿¿¿¿¿¿¿HHHwwwßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ```XXX¯¯¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ```dddhhhPPP···ÿÿÿÿÿÿÿÿÿŸŸŸHHHÿÿÿççç­­­}}}¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿ÿÿÿÿÿÿ@@@ÿÿÿ@@@ÿÿÿ@@@ÿÿÿ@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïï```ÿÿÿÿÿÿÿÿÿçççïïï @@@@@@@@@@@@@@@@@@000888 ‡‡‡ÿÿÿÿÿÿïïï———((( ‡‡‡çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···FFFÿÿÿ÷÷÷ŸŸŸ888 ‡‡‡çççïïïGGGˆˆˆ@@@@@@@@@@@@@@@@@@(((@@@ çççïïïÿÿÿÿÿÿÿÿÿŸŸŸïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»»»gggpppÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïÁÁÁ```XXX···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷(((ßßßÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ```XXX±±±çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡^^^$$$$$$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ```ŸŸŸÿÿÿÿÿÿ···ÿÿÿ@@@ÿÿÿÿÿÿÿÿÿÿÿÿ ‡‡‡çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpppÿÿÿ÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïï———### ÿÿÿÿÿÿÿÿÿÿÿÿ@@@ÿÿÿ···ÿÿÿÿÿÿŸŸŸ```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿ <<<ÿÿÿÿÿÿÿÿÿ¿¿¿ÿÿÿÇÇÇÿÿÿÿÿÿ÷÷÷ïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ888(((ÿÿÿ···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïï÷÷÷ÿÿÿÿÿÿÇÇÇÿÿÿ¿¿¿ÿÿÿÿÿÿÿÿÿŽŽŽ444```@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷‡‡‡vvvŸŸŸ¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿³³³³³³```XXXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷000×××ÿÿÿhhhÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxxxPPP¬¬¬³³³¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿§§§'''nnnÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××```@@@@@@@@@444@@@000vvv÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿhhh 000@@@@@@)))888XXXÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÐÐEEEÿÿÿÿÿÿ×××@@@‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏ888÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ888XXXçççÿÿÿçççhhh+++’’’ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­­­222ÿÿÿÿÿÿÿÿÿ÷÷÷xxxHHHçççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ888ÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ000ÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿççç```888ÇÇÇÿÿÿ÷÷÷‡‡‡(((———ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···(((···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡xxxÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷‡‡‡(((ŸŸŸÿÿÿÿÿÿ¯¯¯(((xxxïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏÿÿÿwww@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïHHHppp÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿÿÿÿßßß(((÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···000xxxïïïÿÿÿÏÏÏ@@@PPPßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ888×××ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿÿÿÿ@@@¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××HHHHHH×××ÿÿÿçççXXX000¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿ÿÿÿ___@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏ000ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿÿÿÿ———hhhÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïxxx(((···‡‡‡÷÷÷ }}}ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷WWWPPPïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿççç(((ïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ———(((iiiÿÿÿ§§§MMMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿ÿÿÿ___@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïÿÿÿÿÿÿ~~~(((ÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßßÿÿÿHHH···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ888hhhWWWÿÿÿ888EEEçççÿÿÿ888ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsss888ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ßßß§§§XXXÿÿÿÿÿÿÿÿÿÿÿÿçççXXX888ÇÇÇ÷÷÷ççç888pppÿÿÿÿÿÿ§§§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïÿÿÿ§§§@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ000ÿÿÿÿÿÿ888çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÊÊÊ000çççÿÿÿÿÿÿ÷÷÷‡‡‡ §§§ÿÿÿ÷÷÷§§§ÿÿÿÿÿÿÿÿÿ÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ(((§§§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ TTTŸŸŸÿÿÿÿÿÿ¯¯¯(((xxx÷÷÷ÿÿÿ§§§ ÷÷÷ÿÿÿÿÿÿÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïXXX```÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ???ÿÿÿïïï$$$MMMßßßÿÿÿÇÇÇ888XXXçççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿<<<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ%%%ÇÇǯ¯¯(((§§§çççXXX888ÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏbbbÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»»»‚‚‚ ×××ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßßß§§§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ888ÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§§§XXXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZZZšššÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ000¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ```ÿÿÿçççÿÿÿ@@@ÿÿÿ@@@ÿÿÿçççÿÿÿ```ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿ XXXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿççç ßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿççç(((×××ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§§§ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿVisio (TM) Drawing m„4äÒöŽwRídêñéòÿÿÿƒÿöòÿóûðøñ‰€êñ€ €ÿÀÀÀæææÿÍÍͳ³³ïššš!€fÿffMMM333Uëðÿòðªëð8èó@úôáúöñêñãøý ëðTZÿ Arialê@çôN¿Wingdzsè@véòNPþuMonotyp¿e Sortúçô mSymbol5Tëð?èóÐ?òÿÚêñ èóY@-1Uëðÿòð ëðèó úôëðöñêñJçô:ëðDëðT1èó€òñEWòñàû-êñhêçôëðPëðT…ëð8ßüŒŒæõ®‘*ëðªëðëðëð  ªëð ëð ëð“ãø/žÀUºöñ_bþ ýbþ…ô¸êñ™ü0z®_Gáz„?¶@æõ ¿4‘D.ägSbª}}},¡g !’†¼êñ•ük5ð|'›ªgÓg’K,K,Ò‡ÀêñéÀüLëð˜/ª&5?¹ÿ\.—Ëåâ? 7àòðïAôgTª&Šå,å,å'€1/C%Â&T$Ë&Â~ ª} A& Ó yàü ¹ ~|~,)@~ª¼Z?l2  }U } }}}U}}}-)¤ÈêñíŽ?ý%Ù_aþžB¤EU¤E¤E¤E¤E¤E¤@ü?N3OEJ©`.žB¤E¤Eª¤E¤E¤E¤EJ¤E¤@„ÌêñO5£ÐêñFü“A«Y œ; (QÏVÏVB(Q§Ôc¨TÇY·Øêñ÷ü S)oS#;oXo' }`Pÿ#‘‹gl‘‡bð6Ñ`e#l ªl l ‘½±~Yð?ˆˆ–?P‰\ ï#ÓàA4n|K,K,K,J/’RëB¾àêñQüxUO ¢°MÁE2„ÐA ¿äêñæ_d4žO°OÂOÔOæOO O_DOVOhIÀè3r»V…»ÿ@L&“Éd2¹%?[Él˜Ã#Ò~˜\ž sU¤‘墖fÄŸt†˜ª¼ÎàòHþ”g‘–Š€¬)m–Š ¬–ŠÕÀ¬€¬€¬ ¤£•Š· }*}}-i¸»@úñ”|³ 5êñ”ãø¹ÌSe'0¤’UÅLYy³?Å?×4¨zð uí2U#¶ê“è• *è• è• è• èz£0¿{Ój¼J¿\¿ˆãøéæõdž·ª/¯Æ!M3ÂÂó¿>Æ>Æ’D€Ø/ê/ü/‡uÂÅ©d>Ì >Çè4¾(QkÀ`ÏrÏãøˆãø€YÇU-%»1áßçÒÃõÝàûá„-(‘gp €Ù» ‚ï .ü ý–1- >^i\÷Ý#êTä*Ȳ…)›’/3ÿâùÂ/Ô/òÏß ?Ñ?ã2XÁF¿߀ϣÖSœÏÀÏÒÇ.ßÉè7ÄÑ\ÅÐß…ôßï2#åmÑ$0çJ…¤ÕmÑ­ ÈÕfïxïŠïœï®ïÀïÒïäïöïÿÁÔmÕtÚàÖò±ércð2åõUjô *û˜øñ¿óU2Çq¬ ¬?«/½&¿Zÿlþ(½&†ôø,ø,ø'ÄÑ+áÌß×”¦ï§ÒxQ>Á3ä6ᎹxQ÷yZé+á@xAW/i/{/<§ÒG¥$ŽBèÿið&®û bF…—©ûq¥/·#"ãÕ/lý*>ÌAܤKè(! ì0G)Q¿ë”E‚Ø1¨Ñæ!ì0œU~_ÖŸ1…`QBô”A¿Üÿîÿw»=†¹9B—´¦ÓÀ¥¼hBû´e+þÉeXE?2PhU¼Q NâJJ¼2C‘©ÐwRQ7ä3"¬Ñß”ÄѼ1:?2àU?a?s?…?ÍßñÙ?}ëð ëðÿ Displaýyëð‚nfUFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðHu¶éò óð¨éòÿžÿïƒÿÏþƒÿ%ÿÿUþüøð1àþ3ü ïÿÿ›ÿÿðÿŽÿ‚ÿðŽÿðÿ÷ðÿVÿðŒGÿðc‹Gic]WRNJ Jf‡ÿmbéòøÿõñ¿¿èóû´?àû´¿Dázÿ®GÁ¿Âõ¿(\¶?€á§z„?ûôäûôéûôØÌ?•HëðÿòðëôëðDBëðëðöñðó ïô$# ä÷?hçôöñ- >TëðßüöñÔE=›ãøÝU@öèóÀ?ŸÒ?@øê¿§İ´?P®6 ÕþÝëð>Zuöð`]¾ëðð?þ$õ2ú u#5à! bñ^•d d£>êñm’™ýòðþ”5 Eþ`;Copyÿright (cÿ) 2001 Mÿicrosoftÿ Corp. Aÿlle Rechÿte vorbehalten.fK`ñl>ú,>Udßüúú9 #J‰ãø™@ WaþY"_%_ ±Š}ø›º¥ þHN ä" ãÍ&‹BŸ„/>›/­/5ç!3ó%Ä€ ?$?Zç!êñ2”ô/“%rA^þ:´2º3þÕØëðHëð„ëð'÷-éò³!Òêñÿ âùýòÿ4 ð)£õâùëðFëð¬ÿ^ÕE «îëðÖ#ëð4náëðK†ëðB ¾ëð WÕvfªëð@ekwo+*ëðôYxf=aòñ_Äîѵf¯rÖ_IëðëðUæõëðëðêñU(ëð)ëðêñ*ëðU+ëðêñ,ëð-ëðUêñ.ëð/ëðêñU0ëð1ëðêñ2ëðU3ëðêñ4ëð5ëðU êñ6ëð7ëð êñU8ëð9ëð êñ:ëðU;ëð êñ<ëð=ëðU êñ>ëð?ëðêñU@ëðAëðêñBëðUCëðêñDëðEëðêñFÐêñ22´rGNëðORëðêñ%]ëðêñe_ëð`ëðòñaëðUbëðêñqëðrëðÜñUFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUôUæõ’h UIvèóì?—ð??èóÀ¨¿  d Üî¤x—þ$ )©)²)Õ00 bûybþ} aþeqÈYkçôƒãøzUëðGu¯éò óð¡éòÿ£ÿßÿÿ€ÿÿÿÿÿ?ï¼ ?ƒÿÿÿ?ÿýÿ?ÿøÿ?ÿðÿûþýÿÿ­ÿÿŠÿø„ˆ€ÿŠÿ†‰ÿ?„ÿðGXFFflGYUð…ˆ‡Qð Düÿý=ëðStelltÿ eine Asÿsoziatioÿn zwischýeei Klaÿssifizieïrungdar.­bãøÔ¿ôô?üü¿ÿm Ó:mÔ¿÷Žã8á?ˆ>?é“>éÙ?ôÿ UGëðÿòð ëðHèóDëðëðéòòñ ïô# ä÷ëðöñêñÒ#hçôöñ@>TBëð(ßüöñE=Jªëðëð ëðê›ãøàAU¬]ëðÜ?¿âÇäêп¿ÔÇÔ¿PÚ6 ‰uŒ` ^²?€A[þÓu#‰uµ 'uöð`ø‰E@äb]Ku B ^¢:xsJ.JL˜A˜êñ—XA¸Œ ¦Å°ºçÈÒ‰õ‰aò )%öñ bGþJ˜ò—"B"7"¯A/Kç!"\&#L'Ýu-…>êñýå‰U2Nè´N{?<¿ŠÚ¢#2‰r\"À¢Ö! Ò!î"‘?ë£ëð“²&þ&¨nÛ&: yc£:öñ“3 þý3 $-2362 ?3 ’"4*02Q?c;¤‰êñ¶¬#¿ÀÇìúÇèÇÐ?$8&ªÕ7¥[#Ð2¥ß<K#Ð2çGÓu#Ð2HÓ'L#¤Jêñ>¬#“Ê#êñüòðÎ"^Ÿa,#c”,%šE ™A„êñ±ì¬#5 0;`UÿML Backgÿround Adÿd-on` /ÿCMD=1001€yk'Aç4RCVÏœþ3%V¤Dþ39V þ3MV  þ3aV •êñ]ÁAä£%?£#AbU›D ¦V ¦V¦Qa¾™êñÜ!¼ J5 8ëðœ¡êñõ¤:ßÔ?AÍÌaÄ¿o@Çq`¼?mI¬*gõ @P">L'%A-[ŠSab Œ\g-2r¾Àþ3ìÀ;A3¡bÃ?÷!,¤R'¯c4ÓRÀbuÊi ¿2ÂK@F÷!£"¬#ýòðÒHqþMt5 En`;ÿCopyright (c) 2û@ÿ Microsoÿft Corp.ÿ Alle Reÿchte vorÿbehaltenû.ec`Viÿs_D112.cïhm!#|p00þe>ha0rãøµE„ ƒm ¼œúó £%„¤"‡›5„  "‡3„ úóɘ8ðQ‡PÐ1Ð1ÌR%  „øR11 'º„9r9lÐ1UdQâùÂ)#‰æŒØQʘ8TÁ&Á&RÀb –þÀb%€þ‹aH+›ØŽDŸðÓ`\Ÿ«‘q€#ÖƒcábG„ ™0ãøWq¼¡c“?¥âƒ¿3}C?™™¸?…ëQ¸ž¬œS§ðQ;#êñ ¹£[#%V9Vs¢ç}¢ç¹£Ôu#%V9V÷!LÁ&¯b2¦,#3!žŠBaþ¡£%¯lì)¯;¯ ®ŸÑg—R/r Xã¯õ¯¿š6¥fì²ã?ëµ:¿L¿þK” ×£p= ïl¼'Ç¥¿·¿É¼s§K#Ä£# VWaVª¨#ЊÆç˜ÂLWaVp •ÐâñŸLªãøFö¹Agì²¾?A-<"¤@ßHqA™6Ö#´Àr7!à Q(-DTû!é¨`ÞÐ0@Aš™–ѹ?©©doÒuÓ|߀0“ß-2uÒ ,3áÓ§U`4  x§ %_—§ °§$_*$égÄ›DeáŠB…E3!-Ñ£%9ÕÕ 2£RÕ¿™ïdßv߈Óß¡ßÖïÅßר‡£èÖ¿£÷߃¦9_7X0臢lÿ7X^ïpï‚å›59Õaí´ dà¿òÿ½ïÈóÀr‚ÑÞ!3|Ù@Žßùéö.ÄßÖÙŽÃèÖÆÃPÿŠÆÜRQ]žÇ1çŽÂÅKX·ÿ Éÿ‚ådE9Õüô?¿K(:çïR‡ÿ@ÖÙ˜ÃèÖÐ锯a__X0è˜Â/_X"˱n›„UáÕAúÄýÁÙP•%ßÓîÁ©.ØÞB{àHqQ–ø”€1¯p &Awnsi˜qn..¨pý]gsSListeÿt die Diÿagramme ïauf,02Shaçpe-601ser¿ binärU0Aÿssoziati?on ent¢v¾RI±ÕBÛOíM5þD gÇt$TÀÜ!µ_é/û-- ¬s_›D±7 WÃQ–¸?Ê4ãõºBwŽáàè;”ð”ð„Á¯p&Sÿemantikf÷ehl\0anzeigenþ±ÿðÀ`.ˆ~??îL2þD…·—BLgÇBrÔQ‚ЀÇ` @Ì2f…YÚ?ì?þ?£H"¾R1F0Oô†Ë_û-<öÁ¯p&Opn1U0GfürG6ˆB29#3å/½Zo±U†E od\1eaŸpiy?°OÂOG008²?Ä:7å»…_zû-¬s%&EŠA_schaf55#3+`-jì_þ_o"oR«31;oÄ6g¨„¥$ úÄ:*s{…1Õ­Ù²T à 1 1²¤%ܲÿAÿAf‚´Ñ´Ñõ‚¿¿7âH…a…a¼"¹t«‚Æt¸‚PÕrÅ‚g‘g‘!Ȣɡ;QƒÂ„ÁÜ’¼Á¼Ájqô¸Öqgpg”7@$Á“³¢.‚þܵDÿAgpèRE3ƒ"±„ð•W$Šc…s§Ü±,œ‡è†äFâ£p>…r„õ…STP&ói¿3ƒ…crÉ£Àr‘£À€ÚAìç÷a÷Ñ7åë…ôr¼ÃÀr„ÞŽÇÆÌ“Ç4J’¼%/$‰Èâ2ˆ‰³‚Éqò8ŠÐH‚ÄràM‚vãUáÔ¦%£%£~!%£ÏÏtàÑ&ˆÉ°2„ 3r…a‚Ðb€Õ€GƒÞrÆt ¨2„õbàër,ÄY€@0KÓ?3ƒnCmuà`uÐ`Îå7äu `„¸´Óˆør̨@KÔߪ”ð` uîÀô¥ý¡;P¿‚‚ ¿Þ«€`ë©äY¹¿‚t¿2»jpq`?¹·«¿ò,Ît@”ñAúÀA(ÍÑ5-.Øp´ÑY€vOˆC.iÛt€”ñ…aúÄÙÕÑ5Žá”ð.Ø f`Û'0·ÇqËÀ¼?Ò€ЕHëðÿòðëôëðD:ëðëðïô Höñ ä÷;hçôZ ,>TëðßüH  9 B›ãøý¤AUAhf¾­¾?A 3¶“ß¿@Çq¾¼?ï@ŸôIÇÏ?@ÔæõÏP6 B‰uvŒ`p| Àû¿À ¢ëð } ëðb£ ëðÿ“b€AšÖþðuöðúÒ$‰u•LøA-Ö,ú‰zb €.`…‹ûC€ Ö‹z3ž­¿.¶>"Ú &!áþ –‰a1$9 "M 'þ„>êñQ‰mU5 u€Kp*( &" &'ƒ¤‰êñz ^'“üòðçô!3ÛnëðòÀ`€ Öéä,3"B% bGþ BϨS% j&?E"_D#B6J"t4C0£É&g\#…þòðýòðw1w15 IþÖ`?Copyrÿight (c)ÿ 2001 Miÿcrosoft ÿCorporatÿion. Alûl ¡2s resÿerved.þQl…ó @Udßü(Œ·%=‰ãø'6þ$U2#9%9%9 åŠP(`\#Ï€‰öê1T#"$zB8…Hi‰A!^h59 %9”ãøË~^-'0¾j&#"!Vþ•HëðÿòðëôëðDœëðöñïô Hïô# ä÷=hçôZ ,>TëðßüH  9 ›ãøý¤AUAØ2}3®“?Ahf¶¿¾¿@Çq¾¼ß?@ŸôIÇÏ?©@æõÆP6 B‰ÍuŒ`p A ùÀûêñÀ ¢íëð  ëðbû£ ëð“b×€AÖþðu”#ú$®‰uLøA-ÔÖ,‰zb €.…‹ûB[€ Öz3ž­´¿.>Õ" &!¶áþ ‰a1$l9 " 'þ„>jêñQ‰U5 uC€Kp( $" &'¤‰êñð$`%Eüòðçô!3ûÝëðòÀ`€ òÖä,E" bSþ êñ6\#S% j&?E"_D#96 ª91£Bg\#þòðýÐòðd1d15 IÖ`?ÿCopyrighÿt (c) 20ÿ01 Microÿsoft Corÿporation. All Ž2ÿs reserv?ed.þl…ó @UdßüŒ·%=å‰ãø'63"aª2#9%9%9 ŠP(`\#Ï9""…‰ö×1Tþ‡B8’H´(3!aåöiA^E9 â9”ãøË^-G'0¾j&#"*Vþ•HëðÿòðëôëðD:ëðëðïô Höñ ä÷;hçôZ ,>TëðßüH  9 B›ãøý¤AUAafÞ­ã?A3µà¿ï@Çq¾¼?@wŸôIÇÏ?@æõjÏP6 B‰uŒs`p Œ Àû¿À ¢ëð } ëðb£ ëðÿ“b€AšÖþðuöðúÒ$‰u•LøA-Ö,ú‰zb €.`…‹ûC€ Ö‹z3ž­¿.¶>"Ú &!áþ –‰a1$9 "M 'þ„>êñQ‰mU5 u€Kp*( &" &'ƒ¤‰êñð`%“üòðçô!3ÛþëðòÀb€å Öä,E" bGþ BϨS% j&?E"_D#96J"tþ$:0£É&g\#…þòðýòðn1n15 IþÖ`?Copyrÿight (c)ÿ 2001 Miÿcrosoft ÿCorporatÿion. Alûl ˜2s resÿerved.þl…ó @Ud !âù(Œ·%=‰ãø'63"Wa2#9%9%•9 ŠP(`\#ωöá1T#’"qB8|Hi„‰A^_59 9”øãøË^-'Q0¾j&#"Vþ•HëðÿòðëôëðD:ëðëðïô Höñ ä÷;hçôZ ,>TëðßüH  9 B›ãø}¤AUA3¬ßà?Aaf¶ã¿ï@Çq¾¼?@7ŸôIÇÏ?½æõµP6 B‰uŒ`9p öñ ÀûŒ¿À ¢ëð } ëðb£ ëðÿ“b€Ao@þðuöðJú$‰WuLøA-.ê,‰zb ƒ€.…‹ûB€- .z3ž­¿Ú. >j" &!á]þ !a1$9 6" 'þ„>êñµQ‰U5 u€¡Kp( $" &'¤‰êñð`%Nüòðçô!3úûëðòÀb—€ .ä,E" bþ BبS%j&?E"_D#Ð96J"þ$:0£É&g\#þòðýòðn1n1ú5 IÖ`?Copÿyright (ÿc) 2001 ÿMicrosofÿt Corporÿation. ïAll ˜2s rÿeserved.Gþl…ó @Ud¡ßüŒ·%=‰ãø'\63"a2#9%U9%9 ŠP(`\#ØØ‰öá1T#"qB$8|Hi‰A^_5Ä9 9”ãøË^-'0¾j&#"Vþ_0HëðÜëðßu-éò†³Ë!þñÿ âùýþó| ð—W}Oâù çôFëðÿl:ÞÞ Ý°ëðÖ#ëðpᎃëðýB9\oá÷EaœbÕyVoïð@+ëðU¼YWo-aGëð_ô7à„o\:ýÖñòt8ààèDþð¬ô³Lûv»t9àÂÏn»×0!s»UFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðHu\éò óðNéòÿ±ÿþƒÿý*û÷ï  þþÿÿöÿð_ÿŽ'+<*9ÿÿ¹ÿ]bÜÿ¨?èóÀÿ¨¿Ház®GÿÁ¿\Âõ(\©³¿èóæ3è3ÔÐ?UHëðÿòðëðLèóDëðëðöñðó öñt ä÷>5h: JT5 ¨AæõM›ãø¹>U­@èóÈ?£ЫÀÚ«¸«£¨?ÝP >u~A`bþèwJ>Ëuöð`aëðà?¶öÙþ >”ó-4 4ø¦þ£JêñmýäòðoþtâùEþ>`;Copyÿright (cÿ) 2001 Mÿicrosoftÿ Corp. Aÿlle Rechÿte vorbehalten.,K`+lJöñ$J…Uh ßüöñ3 M‰zãø >waþ1"a9#Õ@ Š]; À¿û@Ìi"¼?œ>îbõª3"ó‹}: Îçx#ó„)ÜAèú //0/B/jêñTëðßüöñÔE=›ãøÉU@VèóÕ?ŸЧħݸ°¨?P6 µÕþëð[>Ëuöð`]ëðð?[þäúSu#à! SbñJP P)£>êñm™ýòð{éþ€5 E`;ÿCopyrighÿt (c) 20ÿ01 Microÿsoft Corÿp. Alle ÿRechte vÿorbehalt·en.R`ñl*>ëð0>Udd€âùðð9 #ú‰Òãø™@ aUþI"O%O Ši´ä›´¹F: ."úm ã½&‹BŸœÊ‰/¡/F×!3ã+~&?þ//èó(32?á#÷/r8g?&5úRêñ27?ƒ%rAêJþ÷2ý3þÕØëðHëð„ëð'÷‡éòèµ!¥ëðÿ âùýòþ ð)õÕâùçôFëð\~ÕÅ'«ëð÷Ö#ëðŒqáßp(`ëðB} ëðltÕÐ]Uëð@eÌkÑo}+ëðtráÒ]ù:aòñì‚Õ o)¹rÖUFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðHußéò óðÑéòÿÿˆÿŽ»¸ø, Ÿ‡» †,C°}C‚ …,¿…»°ƒX„U„Y RH?+©þ¶ˆøÿø½bãøÄ¿ ™ýñ©õ?æõ˜ýó¿Házÿ®GÅ¿ Çq¿Çq¼?€áÿz„?X°[°·»¿éò€ê35è;ÆÐ?•HëðÿòðëôëðD ëðëðéòòñ öñD ä÷ëð3AhçôZöñ$>Tëð ßüPöñ3 A ›ãø¿[U@èóÕ?£Ы_Ä?Aš™À¹´w©?P6 mÙþu »` èóð?÷©uöðà  )bõ$* *è·>B&H†>êñ#7 P?òð?$*£—mýòð«þô°5 E`;Cÿopyrightÿ (c) 200ÿ1 Microsÿoft Corpÿ. Alle Rÿechte voÿrbehalte[n.,`õl>ëð0>Uddâù@ ! !€ öñH‰ãøÝo aUþy"% Šc´ Ÿ´½øþ ø B^"ÚH çí&‹™£ Î¹/Ö* 13;® 9.?Í/èóX3b?#?58©1—?V5Hêñ2¡/³%RrA$þ'B-Cþ™y!aÜöÕØëðHëð„ëðu÷‡éòW¶!¥ëðÿ âùýòÿ9 ðãW)õâùçôFëðÿdÕS+Ý«ëðÖ#ëðÜr×áþK[ëðBý ëðÄ‚ÕY,î@et…ÕóZoKÄsáÓ[oBBñò”×Õ¡oàrÖUFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðGuóéò óðåéòÿÿˆÿŽ»¸ø, ÿ‡» ƒ» ÿ»»¸ø†»°ß»»°ƒ,†»Ÿ‚ »B-…ÿ»°ƒ°„a„ mgX àK ? +½Êˆøÿøýbéò¾±.n£·Ä¿éò¨?èó¨÷<üÿ §¿ƒÉÿTÁ¨¤Ò¿£ÿ57猴?àÿ®Gáz„?‰¿_ÞŽ´¿èóé®3è¿éòÌÐ?UHëðÿòðëðLèóuDëðëðïô öñ ä÷èój>h: JTP5 AæõM›ãø!MïU@æõ@ ¿3Øþ§?£À¥?¬·´ªP Š>tA` aéòÍþètöðò¹"¿D aýß?þî”èœæ€—éªï>Õuïbþ>uÔ ÷àMÙþ R>[…Œ ŒnPJ¤Mª£Jêñg> Uþòðýòð××âù÷I`?Coÿpyright ÿ(c) 2001ÿ Microsoÿft Corpoÿration. ß All "s ÿreservedO.þlJöñ$J…Uh ßüöñ3 M‰zãøÑ >waþ“"a›#•¢ ŠÅ;ѵ¿ÿ@‹à?‹ý½Ï?>VbU]fô‹}:»*ªPÚ#[ë*r>!AJ/\/n/€/È’/¤/êñ<»-­—¿VÓ/þç'÷Ðù/ ??/?nUGëðÿòðëðHèóDëðëðöñðó t# ä÷ëðihçôöñ$>TëðA ßüöñE=M›ãømûJU@Ñ?£ÿÏ?@ı.n£ßÄ?@þ·?ë@¾·ú§?Pn6 .>·ïï¶JuM`{ öï?þèUîømuöð` ÑÕß)JÙþ VJb}† †)£>êñmýòð±è¿¿5 EJ`;ÿCopyrighÿt (c) 20ÿ01 Microÿsoft Corÿp. Alle ÿRechte vÿorbehalt·en.~`}¾tJêñÝþ+8ëðe… ^3-HM"%=ƒãø#Uø@úó…!¢‘$úólJ"YUdvˆ‰Û,.!ŠŸ@æõ'6²æõþ`G7‹( ?2?D?V>•HëðÿòðëôëðD:ëðëðïô ‘ïô# ä÷;h´çô#$>Tëð Pßü#3 A›ãøAÿU@ı.nÿ£Ä?@¥ F¿%u¨?A¤Aëš™À¹´©?Pª} töð`  Þèóð?þètT#òÅ¥àÿ}µ€A@i¾+V>7uï÷jFLVu Ø÷FÙþ VbŸ¨ ¨)†>êñ#U~ ?¨òð?¢¨£Bg Íþòðýòð ! !5 ýI`?Copyÿright (cÿ) 2001 Mÿicrosoftÿ Corporaÿtion. A÷ll 3"s reÿserved.Sþl> 0>Ud€dâù  9 #êëð‰ãøÍ ®aþÑ"×%ª× ŠÁ`ÍA´½V\Ä’V‚hL·! µFE6‹B@ù*Î?ü)?_13k;09†?%?èó°3º9æõ{?]Aï?®5·!Rêñ2ù/ 5rAê¢þB…Cþ_Hëð°ëðßÇéò»¶—!ëðÿ âùþýò: _ð)*õâùçôýFëðüžÕ/w«ëðÖ#ëð_,táºKqëð÷B ëð<Õ»+0@eį•Õ,o+ëð$/uá-oRaòñ_\³ÕoÚïð÷ÖGëð„¡ÕŸY2¦©òñüŸªÕÿ38þðÖUFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðGuõéò óðçéòÿÿˆÿŽ»¸ø, ÿ‡» ƒ» ÿ»»¸ø†»°Ÿð»»°ƒ,K§ÿ »B-…Lÿïÿð°„aƒÿ mgX K ? ø+¿Ìˆøÿøýbéò¾±.n£·Ä¿éò¨?èó¨÷<øÿ §¿ƒÉÿTÁ¨¤Ò¿8Hÿf,ÍY³?ÿ®Gáz„?(.ÿ3TÄ[³¿7Ö¿Åm4€ì¿èóèÿ¿HNÑ‘\þ»Ð?UGëðÿòðëðDèóúôöñðó # àû® hçôëð$^ëðTëð ßü¨UUæõ›ãøûþU@n¬‹ÛhïÙ?@èóÐ?@ı.n£ħ¯¸?@¾³ú§»?Py Ýuöð` öï?ÛþäuU` ªÍß÷Õþ­ b& Ú&.R­EO>ÊDNX…£”êñm™ýòð­þܲâùE`ÿ;Copyrigÿht (c) 2ÿ001 Micrÿosoft Coÿrp. Alleÿ Rechte ÿvorbehaloten.`u¾B™Ýþ+8ëðe Z/-HöñE€ƒãø#UNHéò!ž$X˜#•HëðÿòðëôëðD:ëðëðïô ‘ïô# ä÷;h´çô#$>Tëð Pßü#3 A›ãøAÿU@ı.n¿£Ä?@þ§»?A¤Aš™À¹®´©?P} têöð`  èóð?Mþèt#ò àÿõµ€A¥@¾+1uï÷Fu µÙþ •bˆ ˆr[> ¦Ó†>êñ#UÀòð;?ÔA‚b/þ£BgÍ­¡ýòð­­5 I`ÿ?Copyrigÿht (c) 2ÿ001 Micrÿosoft Coÿrporatioÿn. All þ3"s reserved.þl> ¦0>Uddâù¦ ¦9 # ‰ãøtÍ@ aþÑ"U×%× ŠÁ`ÍkAÅ¥´½F!LÄFbþX w"   E6 ‹B@ù*Î?V.:_13k;09†?%?èó°3º9æõ{?M Aï?®5 êñ2ù/ 5¥rA‚þB…CóUHëðÿòðëðLèóuDëðëðïô öñ ä÷èój>h: JTP5 AæõM›ãø!MïU@æõ@ ¿3Øþ§?£À¥?¬·´ªP Š>tA` aéòÍþètöðò¹"¿D aýß?þî”èœæ€—éªï>Õuïbþ>uÔ ÷àMÙþ R>[…Œ ŒnPJ¤Mª£Jêñg> Uþòðýòð××âù÷I`?Coÿpyright ÿ(c) 2001ÿ Microsoÿft Corpoÿration. ß All "s ÿreservedO.þlJöñ$J…Uh ßüöñ3 M‰zãøÑ >waþ“"a›#•¢ ŠÅ;ѵ¿ÿ@¤…pqʼÏ?>VbU]f3ò"óe‹}:»*ªPÚ#[ë$l>!AJ/\/ n/€/’/¤/êñ<»-n®õ¬ 1“¿Ó/{þç' š™ÿ1Éù/ ??/?n_Hëð°ëðß'áéòe·—!ëðÿ âùþýòO _ðz*õâùçôýFëð4¿Õ¶6w«ëðÖ#ëðÿŒuáa7ÝqëðB ëðܳgÕÒ]ïð@e_<·ÕÓo+U¯váÔ][aG~ëðÄÃÕ/8úïðÖñò4ÉÕ”ñ9ìðþð¬lÒÕ˜;å©UFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðHuåéò óð×éòÿÿˆÿŽ»¸ø, †»°…;ÿðEWi{@+ü¯¼ˆøÿøý³ëðStelltÿ ein Assÿoziationÿsattribu¿t bzw.øñeû A slistÿe dar, dÿeren Werþ(zur Teiïlungr Mÿenge von Objekt3¿dienen.ioe mi÷òemWÇ übKHýø v¿erbund3sind.mbãøÀ¿èó¨?àûÿ¨¿Ház®GÿÁ¿ …ëQ¸÷­?€áz„?TûôæûôèûôÔÐ?•HëðÿòðëôëðDBëðëðöñðó ïô$# ä÷?hçôöñ- >TëðßüöñÔE=›ãø¿U@VèóÈ?ŸЧÀ§ݸ°¨?P6 Ýëð>-uöð`]ëð¯ð?þäê¦ôu#þàmÕþ b=•F F£>êñm’™ýòðqþv5 Eþ`;Copyÿright (cÿ) 2001 Mÿicrosoftÿ Corp. Aÿlle Rechÿte vorbehalten.>«`=l>ëð4 >Udßüææ9 ¨#êëð‰ãøt™@ aþC"I%I Š_Ú›H¸?ôú ô ¨# $& ã·&‹BŸœÛ#~/¦(›/­!Ñ!3Ý/¼ö/?Ñ!,?¸I?•/m;ê?Êœ?®?&Ñ!)!êñ)2×?;rA@uþDBJCþÕØëðHëð„ëðu÷áéò#¸!¥ëðÿ âùýòÿ? ð¶W*õâùçôFëðÿDÝÕ(>Ý«ëðÖ#ëðôv×áÓKGëðBý ëðäwá?÷£aìÒÕu½oëð@+ëð_œxá¾o9a¾òñäáÕ÷oº„ÖUFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðGëêñ óðóþéòÿˆ?ÿŽ»¸øþ, „»‚ »û°…;ÿ »w „H°ÿðU}Oÿÿ ƒ;ÿÿƒÿðrj \ N A+ËØˆøÿømbãøÒ¿èó¨?àûÿ¨¿¤p= ×£ÿÒ¿ …ëQ¸ÿ­?€®Gáz³„?ûó€êûôèûôÆÐ?•HëðÿòðëôëðD:ëðëðïô ‘ïô# ä÷;h´çôöñ >TëðPßüöñE=›ãø1AoU@èóÄ?Ÿ騧¦¸°¨?PZ6 töð` ÿ rÇqÇá7?þät#î*èóðûÕý©êüàû[>³u/ b¦vuQ ©b%²¸ ¸£´>êñgUþòðýÐòðãã5 IC`?ÿCopyrighÿt (c) 20ÿ01 Microÿsoft Corÿporation. All "ÿs reserv¿ed.þl> ëð0>UddâùR! R!9 #b‰ãøtÝ@ aþ«"U±%± ŠÑ`ÝZô¹v|„Xv¢ˆ"b 5Œ6‹BÜ®ë/?X913E;à&?`?ÿ/èóŠ3”?C3Y?Ô8É?ˆ5bêñ)2™?å%rA²uþYB_Cþ•HëðÿòðëôëðD:ëðëðïô ‘ïô# ä÷;h´çôöñ >TëðPßüöñE=›ãø'AïU@AÌ«?@èó¨§À§¸ª§°§P6 túöð`  :Žã8¿Žãè?þäQt#îèóàûêWÇq(Üû5ðûuë ¦Lu ¶Õþ b…rŽ Žx>k¦¬£>ZêñgUþòðýòðèÙÙ5 Iÿ`?Cÿopyrightÿ (c) 200ÿ1 Microsÿoft Corpÿoration.¿ All "sÿ reserveŸd.þl> 4 >Udßü  9 ¤#ëð¬‰ãøtÓ@ aþ¥"U«%« ŠÇ`ÓH± ?LRHh°… ¥Š"¬ ¦6‹B¨Ð'=3à/8ý/1313??¼X?j?B31Ž?¸«?÷/Ï;‡!á?¸¨ý?O H31¬êñ29O;©rAˆþ¦B¬CþUGëðÿòðëðDèóúôöñðó # àû® hçôëð$^ëðTëð ßü¨UUæõ›ãøÞ„U@èóÕ?ªŸЧÒ§¸°¨»?Py 8R]ëô³õíuöð` èóð?UþäêôôÕuTU&à/Õ[þ b–¥ –£êñmUÉýòðÁþÆâùýE`;Copÿyright (ÿc) 2001 ÿMicrosofÿt Corp. ÿAlle Recÿhte vorbÿehalten.VŽ`¾B»WÝþäe@']C-öñE€ƒãø $#UôHéò•!²¡$¬#_Hëð°ëðßÃáéò‡¸—! ëðÿ âùþýò! _ð+õâùçôýFëð¤ìÕCBw«ëðÖ#ëñ¯yáîKlëðBû ëð¼áÕZÝC@edä×Õ[o+ëð z—á\o;aòñd¯õÕ—oÛïðÖþñò¤ÿÕrEëî©GëðòÕ `À\©UFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðGuþéò óððéòÿÿˆÿŽ»¸ø, ÿ„»‚ »°ý…;ÿ »» „H°U¾Oƒ ƒ;ÿ…rj \ N ðA+ÈÕˆøÿøýbãøÒ¿®Gáïz¨?æõìQ¸ÿ…ë§¿¤p= ×£Ò¿43÷­?€üò„? ×í£­¿éò€ì¿…ëQ¸þç3ÿ¼?Âõ(\Ð?UGëðÿòðëðDèóúôöñðó # àû® hçôëð$^ëðTëð ßü¨UUæõ›ãøÞ„U@èóÙ?ÿ@{®GáúÏÕ?ŸÒ°¸°ìQ¸…ë§?Py ·uöð`½ èóð?þäýuU` æK~W±äß÷Õ[þ b&µ &8RkENOªb>DNjNbb£šêñmUýòðÁþÜÆâùE`ÿ;Copyrigÿht (c) 2ÿ001 Micrÿosoft Coÿrp. Alleÿ Rechte ÿvorbehaloten.`u¾B»Ýþ>eï]C-öñ¤E€ƒãø$#UNHéò•!²¡$b¬#•HëðÿòðëôëðD:ëðëðïô ‘ïô# ä÷;h´çô#$>Tëð Pßü#3 A›ãø'AoU@èóÄ?£騫ª¸´¨?PZ} töð` ÿ rÇqÇá7?þèt#ò*èóðÿÙ©îàÿÆu3`u4 U b)Œå’ ’Ö>ª°†>êñ#U~ ?òðT?Œ’£Bg×…þòðýòð!!5 IþG`?Copyrÿight (c)ÿ 2001 Miÿcrosoft ÿCorporatÿion. Alûl ="s resÿerved.þ)l>°0>Udd€âù°°9 # ‰ºãø× a«þÛ"á%á ŠÒË`×Ç´½’`f\|¬"  ªO6‹BÇ ²?p8:\i13u;6??/?èóº3Ä?s3‰?Hù?H¸5 êñ2É?5©rAŒþ‰BCþ•HëðÿòðëôëðD:ëðëðïô ‘ïô# ä÷;h´çôöñ >TëðPßüöñE=›ãø'AïU@AÌ«?@èó¨§À§¸ª§°§P6 túöð`  :Žã8¿Žãè?þäQt#îèóàûêWÇq(Üû5ðûuë ¦Lu ¶Õþ b…rŽ Žx>k¦¬£>ZêñgUþòðýòðèÙÙ5 Iÿ`?Cÿopyrightÿ (c) 200ÿ1 Microsÿoft Corpÿoration.¿ All "sÿ reserveŸd.þl>¬4 >Udßü¬¬9 ¨# ëð‰ãøtÓ@ aþ¥"U«%« ŠÇ`ÓH± ?LRHh°… ¥G"‹! ¦6‹B¨Ð'=3à/8ý/1313??¼X?j?B31Ž?¸«?÷/Ï; á?¸¨ý?O H31‹!êñ29O;©rAˆþ¦B¬Cþ_Hëð°ëðß';éò2¹—! ëðÿ âùýþñ#¿ ðV+õâùúçôFëðä Öf¯I«ëðÖ%ÿtzáJ[hëðBL÷ï©$Ö-Nî©UFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðG‹êñ óðëðïòÿþúðˆÿŽ»ã¸ø' ¸øÿ»»°»» ¿ƒ» »»=ÿ¹ð@?ƒ» Më EM°ƒß°„»M„ „k \ \U< áJ&ØåˆøÿøýbéòXtÑEÝ·Ñ¿éò¨?èó¸ý<èó¨¿ûäPÿî€Ú¿DNªXXlli£êñmUýòðpÕ©©âùEÿ`;Copyrÿight (c)ÿ 2001 Miÿcrosoft ÿCorp. Alÿle Rechtÿe vorbeh¿alten.`¾BÏtàû 8ëðeUgySƒãø8#UNHéò­!ƹ$lÄ'E¹$XÄ#•HëðÿòðëôëðD:ëðëðïô ‘ïô# ä÷;h´çô#$>Tëð Pßü#3 A›ãøAÿU@ı.nÿ£Ä?@]õ¹¿ÚŠý§?A¤Aëš™À¹´©?Pª} töð`  ÿ,èPäQëá?›þèt#òz¿œ¢#¹üßÿúµ€A@Ò¾+uÊï èóðÿFuT QàÿÙ[þ bˆ¹ ˆr>5 ¦†>êñ#U~ ?òð?‚Uˆ£BgÍþòð¡ýòð ! !5 I`ÿ?Copyrigÿht (c) 2ÿ001 Micrÿosoft Coÿrporatioÿn. All þ3"s reserved.þl> ¦0>Uddâù¦ ¦9 # ‰ãøîÍ aþªÑ"×%× ŠÁ`ÖÍAÅ¥´½BFLÄnFb°X w"   E6‹B@ù*Î?V.:n_13k;09†?%?èó°3º9æõ{?@MAï?®5 êñ2ù/J 5rA‚þB…CþUHëðÿòðëðLèóuDëðëðïô öñ ä÷èój>h: JTP5 AæõM›ãø!MïU@èó€<@ÿ3Øþ§?^£À?@ ®·´»¾§?P ê>tA`  æ¼ÿn&]©œ<þæètöðò½"D_ aýß?î Uܹïí>uïb7þ>u ajëðàMÙþ >)[…Œ ŒP·J¤¦ª£Jêñg>U…þòðýòð××âùûI`?Copÿyright (ÿc) 2001 ÿMicrosofÿt Corporÿation. ïAll "s rÿeserved.§þlJöñ$JUBh ßüöñ3 M‰ãø½Ñ >a»þ“"a›#Ê¢ ŠÅ;ѵ¿@ÿ©…pqʼ?ç>Vbª]f3ò"ó‹2}:»*ÎPÚ#[ë$l>!AJ/\/n/Ѐ/’/¤/êñ<»-q®¿õ¬ 1“¿Ó/þ½ç' š™ÿ1Éù/ ??/?n•HëðÿòðëôëðD:ëðëðïô ‘ïô# ä÷;h´çôöñ >TëðPßüöñE=›ãø'AÿU@^tÑEÿÝÑ?@3¿Øþ§?@èóÀ×?@ ª·°§?UP6 töð` ¼]ëðð?þäùt#î¾"D aëýßûêæ¼n¯&]©Üû¹jïûuëLóLuóàûµÕþ •b…Ž ŽxYhA¦¬Ó£>êñgUþBòðýòðÙÙ5 Iÿÿ`?Copyriÿght (c) ÿ2001 Micÿrosoft Cÿorporatiÿon. Allý "s reseÿrved.þlª>ëð4>Ud€ßüH!H!9 # Jëð‰ãøÓ@ Waþ¥"«%« …ŠÇ`Ó±9L€Rh… ¥g‹! 5¦6‹B±Ð'=3à/8ý/1313?>»X?j?ô31Ž?¸«?÷/Ï; á?Êþ? On31‹!êñ2Í/;¥rAˆþ¦B¬Cþ_HëðÆëðßu;éòÒ¹—! ëðÿ âùýþñ¿ ð¦+õâù úçôFëðL*ÖÅïP«ëðÖ#þëðä{ápQ»wëðB ëðÏ"Öç]ïð@¾eŒ/Öèo+¾ëðü|áé][úaGëðÄ.ÖD¯RƒïðÖñòœÿ5ÖÇSüþð¬TIÖÞU÷õ©Ô=ÖÓW »UFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðGëêñ óðþïòÿˆ?ÿŽ»¸øþ' ¸ø»»°ÿ»» ƒ» û»»=ÿð»°ðî?ƒ» Mÿ ÚE M°ÿr„»ýMƒÿ „ k \ \U< J&üÙæˆøÿøýbéòXtÑEÝ·Ñ¿éò¨?èó¸÷<øÿ §¿ûäÿPî€Ú¿8Hÿf,ÍY³?@ÿ®Gáz„?&.ÿ3TÄ[³¿]îõðwî¿èóè¿Àß.ºè¢‹Ð?UGëðÿòðëðDèóúôöñðó # àû® hçôëð$^ëðTëð ßü¨UUæõ›ãø#þ„U@(ºè¿¢‹îÜ?@èóÐÿ?@^tÑEݽѧ¸?@X³úî§?Py vuöð` öoï?þäuU«` Íß÷¶Õþ bj& &BR·EO©Yl>DRNX™llÓ£êñmUýäòðÕþÚâùEþ`;Copyÿright (cÿ) 2001 Mÿicrosoftÿ Corp. Aÿlle Rechÿte vorbehalten.+`¾BÏjàû8ëðeUg¦yƒãø8#UNHéò­!ƹ$XÄ'E¹$lÄ#•HëðÿòðëôëðD:ëðëðïô ‘ïô# ä÷;h´çô#$>Tëð Pßü#3 A›ãøAÿU@ı.n¿£Ä?@þ§»?A¤Aš™À¹®´©?P} túöð`  ,èPä¿Qëá?þèQt#òèóàÿ}µ€A@i¾+uï2ðÿFu µÙþ •bˆ ˆr[> ¦Ó†>êñ#UÀòð;?ÔA‚b/þ£BgÍ­¡ýòð­­5 I`ÿ?Copyrigÿht (c) 2ÿ001 Micrÿosoft Coÿrporatioÿn. All þ3"s reserved.þl> ¦0>Uddâù¦ ¦9 # ‰ãøtÍ@ aþÑ"U×%× ŠÁ`ÍkAÅ¥´½F!LÄFbXX w"   E6 ‹B@ù*Î?V.:_13k;09†?%?èó°3º9æõ{?M Aï?®5 êñ2ù/ 5¥rA‚þB…Có•HëðÿòðëôëðD:ëðëðïô ‘ïô# ä÷;h´çôöñ >TëðPßüöñE=›ãø'AÿU@^tÑEÿÝÑ?@ 3¿Øþ§?@èóÀ×?@ ª·°§?UP6 töð` ¼]ëðð?þäùt#î¾"D aëýßûêæ¼n¯&]©Üû¹jïûuëLóLuóàûµÕþ •b…Ž Žx[>¦¬Ó£>êñgUþBòðýòðÙÙ5 Iÿÿ`?Copyriÿght (c) ÿ2001 Micÿrosoft Cÿorporatiÿon. Allý "s reseÿrved.þlT>¬4>Udßü@¬¬9 # ëð¥‰ãøÓ@ a«þ¥"«%« ŠBÇ`Ó±9LRÀh… ¥G"‹! ¦6‹B±Ð'=3à/8ý/1313?>»X?j?ô31Ž?¸«?÷/Ï; á?Êþ?On31‹!êñ2Í/;RrAˆþ¦B¬CþUHëðÿòðëðLèóuDëðëðïô öñ ä÷èój>h: JTP5 AæõM›ãø!MïU@èó€<@ÿ 3Øþ§?^£À?@ ®·´»¾§?P ê>tA`  æ¼ÿn&]©œ<þæètöðò½"D_ aýß?î Uܹïí>uïb7þ>u ajëðàMÙþ >)[…Œ ŒP³M¤¦ª£Jêñg>U…þòðýòð××âùûI`?Copÿyright (ÿc) 2001 ÿMicrosofÿt Corporÿation. ïAll "s rÿeserved.§þlJöñ$JUBh ßüöñ3 M‰ãø½Ñ >a»þ“"a›#Ê¢ ŠÅ;ѵ¿@ÿ¤…pqʼ?ç>Vbª]f3ò"ó‹2}:»*ÎPÚ#[ë$l>!AJ/\/n/Ѐ/’/¤/êñ<»-n®¿õ¬ 1“¿Ó/þ½ç' š™ÿ1Éù/ ??/?n_HëðÆëðßÃ;éò¤º—! ëðÿ âùýþñ<¿ ð,õâù úçôFëðDTÖïZ«ëðÖ#þëðd}á;[»{ëðB ëðÔÏIÖ¶]ïð@ªeÜk·o+ëð|¯~á¸]XaGþyXÖ\õyïðÖñòŒ_ÖŸ‰]þ𬤟gÖ—_ª=pÆaö©UFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðGuúéò óðìéòÿÿˆÿŽ»¸ø, ÿ„»° »û°„;ÿÿ » ZHP°…W c P†d\ O A ø+Äшøÿøíbéòøÿõñ¿¿¶éò¨?èó <èó¨ÿ¿¢p= ×£Ðÿ¿\Âõ(\³ÿ?À®Gáz„ý?¿ç¢‹.º÷ˆé¿èóè¿dtÑEÝÉ?éòÐ?UGëðÿòðëðDèóúôöñðó # àû® hçôëð$^ëðTëð ßü¨UUæõ›ãø#þ„U@ÎE]tÓ?@þ÷Ï?@„À?{@éò¸?@øÝ¿§§?Py íuöð` öÞï?þäu®U` úß÷ÚÕþ bª& &BRÝEŠEXN>ªDNXXJN£êñmƒUýòðÕ©©âùûE`;Coÿpyright ÿ(c) 2001ÿ Microsoÿft Corp.ÿ Alle Reÿchte vorÿbehalten­.`¾BnÏÝþ8ëðe ZW-öñE€)ƒãø8#UXHéò©!Ƶ$NÀ#UHëðÿòðëðLèóuDëðëðïô öñ ä÷èój>h: JTP5 AæõM›ãø!M¯U@æõ@€¨?£À´¸´ªèóW¨?P >tA±` aéòþèYtöðò à?îUþïý-­>uïbþF>u PÙKþ >[…Œ¹ ŒPJ5¤ª£Jêñg>U,F,,âù÷I`?Coÿpyright ÿ(c) 2001ÿ Microsoÿft Corpoÿration. ß All "s ÿreservedO.þlJöñ$J…Uh ßüöñ3 M‰zãøÑ >waþ“"a›#•¢ ŠÅ;ѵ¿û@ÌË"¼?\>Vb]fU3ò"ó‹}:»*ÎPÚ#[ë$>!AJ/\/n/€/’/¤/jêñ<»-4ò"“¿Ó/{þç' š™ÿ1Éù/ ??/?•HëðÿòðëôëðD:ëðëðïô ‘ïô# ä÷;h´çôöñ >TëðPßüöñE=›ãø'AïU@AÀû?@¨?@èèó¦èó¸°¨?P*6 töð` ]Þëðð?þät¨#î àûêþªïûý)ÆuëóLu¨óÕþ­ b…Ž ÜŽx>𦬣>êñg‚U(B((5 Iþÿ`?Copyrÿight (c)ÿ 2001 Miÿcrosoft ÿCorporatÿion. Alûl "s resÿerved.þ©l>¬4>Ud€ßü¬¬9 # Jëð‰ãøÓ@ Waþ¥"«%« …ŠÇ`Ó± ?L€Rh… ¥G"‹! 5¦6‹B±Ð'=3à/8ý/1313??¼X?j?ô31©=¸«?÷/Ï; á?Êþ? On31‹!êñ29O;¥rAˆþ¦B¬Cþ_Hëð°ëðß'•éòb»—! ëðÿ âùýþñ ¿ ðP,õâùúçôFëðÜ}ÖDïd«ëðÖ#¾ëðä~áïK^îëðB ëð„pÖwMe@e_ìuÖNo+ëñ¯€áOoSaG¾ëð$€Ö¢o{úïðÖñò ÖïgϩąÖ?ìhê©UFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðGuÃéò óðµéòÿÿˆÿŽ»¸ø,¾4Š»ƒEÿ»»¸ø»»ïŠ XŠ»àUI5ƒ•øŽˆ?øÿømbéòúÿõñ»¿èóû ?àû ¿Ø@§ÿ tÚ¿ …ëÿQ¸¥?€®ŸGáz„?ûó€uêûôèûôÆ?þõòÏ?UGëðÿòðëðDèóúôöñðó # àû® hçôëð$^ëðTëð ßü¨UUæõ›ãøÞ„U@èóÕ?ý@„Ð?@úÖ»?Ÿ°° ?ÝPy uîöð` þï?mþäuU` èóÕà÷Õþ Vb& &í8RVEëðSNU>DNXšN£êñmUýòðÁððâù[ `Eÿ`;Copyrÿight (c)ÿ 2001 Miÿcrosoft ÿCorp. Alÿle Rechtÿe vorbehÿalten.þu¾B»Ýþ>e]C-öñ¤E€ƒãøßUNHéò•!²¡$X¬#UHëðÿòðëðLèóuDëðëðïô öñ ä÷èóÒ>hçôöñ$JTBëð ßüöñ3 M›ãøý'MU@v"uÁ}¬?@èóÿ ?@‚ÝŠà>k‚«¯°¯°UP >tA` ÿ ÊùÜGà7?þìtöðöôèóòâj ÜWGpß ðÍ>uó¦Ru >¶Ýþ >br– –€Jk®´†J êñ#>U‚ KòðKJ–£>êñgÛ…þòðýòð!!âùûI`?Copÿyright (ÿc) 2001 ÿMicrosofÿt Corporÿation. ïAll A"s rÿeserved.§þlJ ,JU‚hßü  „ öñêëð‰ãøÛ ®>aþÛ"á%Êá ŠÏ`Û¹  ?KTZPpX´Á! ®O6‹(°¤Ì K?f8:fi13u?ÄŽ?  ?Ji1Á!êñ2?5¥>rMþ6BTëð ßü9 #›ãø<þAU@ÒÙ¹¿>‚ûˆ?@èó ÿ?@tv®à>­¶¯¹¦ÁP-DTû! °úA-u ÷`u ôäb¯u ô ùk€ÉþhÂËþ"ò€¢÷òý¢J[íuöð` èóà?Æxu#ƒøSb&¥« «nh>ÃMÉ…>êñ=ÿU2Nè´N#{?<Âöñ楷bÁöþõB¡éì\§Á}¬¯±¿°Ët€  ŸõëÜG‹l"¡t–w"…l"r'–$I’$IÒ¿„$Œ/þ$ÉÙ'BÈÔ"Èã/õ%É !7í-Ø+?õ £êñgðþòðýòðw1w1ú5 Ic`?Copÿyright (ÿc) 2001 ÿMicrosofÿt Corporÿation. ïAll ¡2s rÿeserved.Çþl>#Ô!Udóáú#3 A‰zãøð waþ3Ba;CB@Šä:ðÂ9:y'Ãþ‹BZEºsOðŒ‹M_Hëð°ëðßu•éò ¼—!ëðÿ âùýþñM_ ðz§´áúçôýFëð$œÖ€kw«ëðÖ#ëðÿl€á+lÝTëðB ëðL”gÖ]ïð@eÓ4ŸHo+yá«]BaGUžWÖÃ]jïðÖñòÿ¥Ö-nù þð¬T°ÖMpV»UFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZU ëðGuÁéò óð³éòÿÿˆÿŽ»¸ø,þ> ¸ø»»Šÿ »¸ø‹»ë°»Y `Š»°ƒ;l p:‹þ˜ˆøÿø½bÝþ€|=âùxÿ|½Ú@§ tÿÚÀ¿ÒàUèï´±?@N{_? ^µ(¿èó_ì¿ÿ>å3ßÀ?È×ëðÔ?•HëðÿòðëôëðD:ëðëð ïô ‘ïô# ä÷;h´çô#$>Tëð Pßü#3 A›ãø¿ûU@èó°¿@<¾mëð ?£À?¬Ѱ´Å­P} µÙþíuöð` èóà?¦÷u# )bõ$* *è·>B¦H…>êñ32ÿNè´N{?H<£Ceèu«þ£BgþòðýÐòð»»5 I`ÿ?Copyrigÿht (c) 2ÿ001 Micrÿosoft Coÿrporatioÿn. All þås reserved.þl> (>Udèâù¨9 #‰ãø} aþ{"Waƒ#Š Šc £  #<øþ % Ï!‹B¢%þ¿?@j}ÐGïLåA>»/ðÜ Ø$À 0>Xç#3¢%š™Q1©½;P3Ù#<7UGëðÿòðëðDèóúôöñðó # àû® hçôëð$^ëðTëð ßü¨UUæõ›ãøÞ„U@èóØ?o@qPüòðÇ?Ÿ{Ÿ||­=± w=Py Õ±þäºþ86R %Úòñuöð` ÿ[ýñãÇï?­ó <ýuU` ì¦+·àOÕþ­ b…Ž ÒŽ£êñmUýdòð¹þ¾âùˆë`ñE`;ÿCopyrighÿt (c) 20ÿ01 Microÿsoft Corÿp. Alle ÿRechte vÿorbehalt¿en.þ¾B®³Ýþe ëðZ>-§êñYƒãø×UëðH3_0HëðÜëðßÕéò·¼—!ëðÿ âùýþñL_ ð÷´áú çôýFëð¤»ÖMsw«ëðÖ#ëð_œáøKWëðýB9Ô°ÖOtî@9¼¾ÖëPo+ëðd‚áËQoGaòñDÄWÖ˜o¶ïðÖÜÿè¾Ð•ä}Nva©UFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãø÷UIVèóà?—AèóÀ¨u¿—ðŸð?3ßñeêñ—þJ ©)²{þ bY/b)»)…teqYkçôƒ\ãøZUëðHuÏéò óðÁéòÿÿˆÿŽ»¸ø, _Š» ƒ;°C¿‹» »»Oý°U»»Š V\QH?+™þ¦ˆøÿømbãø¸¿èó¨?àûÿ¨¿´Nè´ÿ¹¿h6Ði÷«?@N{?Ôûôè3 ÐÐ?UHëðÿòðëðLèóDëðëðöñðó öñt ä÷>ihçôöñ$JTëð¡ ßüöñ3 M›ãøÿ¶>U@èóÐ?§ ¸\¸¯¨?P >*§þìò ÂûÝJm>uA` èóWð?+2<ÓuöðFàO¶Ýþ >b…JŽ Ž…Jêñ3¡ÿ2Nè´N{?H§?©ì¹Sþ£>êñc¡þòðáýòðÿÿâùEþý`;Copyrÿight (c)ÿ 2001 Miÿcrosoft ÿCorp. Alÿle Rechtÿe vorbehÿalten.þ‰lJ]UhzM‰zãø¡ >waþ·"a¿#Æ Š§§¤Ü#Øë<Bþ<h‹íÞ+Ò@÷/L?tb!Aëð(o'ìâù€1€1„ öñ ¡/³/Å/*?ëð ÁJ;UBÕ_?K%?7?I?[?<3ø?FO%O5_Hëð°ëðß'ïéòC½—!ëðÿ âùýþñt_ ðÎ/µáúçôýFëð4ÎÖOxw«ëðÖ#ëð_Ì‚áúKZëð÷B ëð ÂÖ»Ty@e<¯ÊÖUo+ëð¤/ƒáVo5a›|­òñ¼ÒÖ‹o ÌïðÖUFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð-RTëðßüUÔUæõ’ãøƒUIVèóà?—AèóÀ¨5¿—ðŸð?d Ü\îÿceqPYk‰æõ ‘ëðHuCéò óð5éòÿ¹ÿ߃ÿ €ÿÿÿÿÿŸõƒÿÿÿŽÿðÿ¿ð„‰ÿÿÛÿµbéòéòпèóÐó?ýôüô¿¸È䯲›pÖ?· 4 ?•HëðÿòðëôëðD"ëððó ïô# Eåöëðöñ=hçôµëð,>TëðßüYYæõöñ#Aõ›ãøAU@öèóÐ?«Í;f·ž æ³½ÖþÅP-DTû!»é?>ˆBu¿ `u ø¿äbþð«u ø ý-Bûö .·€ÍþBíuöð` èóà?¦Ju#U"BÌ4$€G7¬YA™Ÿi£>êñcBUþòðáýòðÌÌâùEþ`;Copyrÿight (c)ÿ 2001 Miÿcrosoft ÿCorp. Alÿle Rechtÿe vorbehÿalten.þEB¶ÆÆC,à³à?$‰Ÿm'Bžh"žw/‰%Ÿ µ'-®¿/‰ …‰êñý#Æ0z®Gá£z„éCÆ6×dëð äd¨½»Çl>#h!Udüáú#ü%= ‰,u<>2Šº* ëð/Tëð6Dªêñëð’ãø“UÿI<+ øæÿ@IlÁlÁ׿??èóÀ¬¿Ièóð?¿3ãõ^ßüÿÑëðwÀRêñ•[„@®ÊJÐ[ØmQsUŠ“å‘x ®{:Ÿÿe%ëðPZÑ Õæõƒãø •$H¸u•éò óð‡éòÿµÿ¶\û¶ÿÛ¿ç¶ØOß¶ÿÛwç´\Ïû¾¿ß¿þßÿ‡ÿÿ ÷ÿ÷ÿÿw…ÿ ÷ÿü$3÷„ÿ ÷þ1wwƒÿUÿ1Xÿ>w(Çÿw„.1O†ÿï÷wwVÿÇÿÝbáúlÁüðæ?7Çq¼?Þýüÿþâùà¿¶` ¶÷`ó¿4?@°[°Ë¿UHëðÿòðëðDèóFúôéòòñ ïô # âùöñêñhçôzëð,ëðTëðAßüUUæõ#åëð›âù#Uý@æõIlÁlÁæ?@Çqº‡¼?@´´æõ§Púy …b€UAÒþìò…uL`#)-Òõ$…©.ìò…£tTêñõòV…aô ò ò©„êñ7…U• Ë €lU#ݤ…"êñk´ìæõþòð©®{au!*J!*!*{Ý µ´Ѓ…!‡"ZëbUô  + + +• +  +  &× Vxð 6×ÝL3'2£êñc´B!ýòð!#z EXÿ`;Copyriÿght (c) ÿ2001 Micÿrosoft Cÿorp. Allÿe Rechteÿ vorbeha?lten.z«I Zåöi#ZP;5| «êñ”ãø#´ïÍÍ̓'­2èóà?€†ëðnˆ Zßüˆõ €©ãøð´N1„EBAî{ !…`T¿extbea¡0iÿtung sperrenþ>i3û4S¹At dieý ¨Mbzw. hóebÎCÉBe auefª0 aþ_%¹B  ¢Oëð ý)ëð` Lockþ¨AEdit`ÿGuard(0)·€~Ò£&ëð“´"_4R1:TþËRþqA«Ý~OFá÷ ¡CLösÏchen·G³`1-ÉD«RŸ0gaáOóOªQ[%<UKQ'úQ IQDeletIe0_$“o[_bþYxd]d †iÕØëðHëð„ëðÃ÷ïéò ¾!õëðÿ  0ÿÀOhWûþó†KÓ_4 ÁQâùçôýFëðô;àdwÍëðÖ#ëðÿ¼„á1€Ý{ëðB ëð¬ÌWÔ¬]ëð@e_,ÒÔ­o+ëð_\…á®]1a¾òñTÖÔß] P¥tëð4ïôêñæõYÿ,‹Åb'@"‘H$‰ @ãøôþñþõ ëñù⃯„ ëðA-?¥tëð4ïôêñæõYÿ,‹Åb'@"‘H$‰ @ãøôþñþõ ëðôã_ýˆ ëðA-­Ì3‰üóAéòÜð@A¥tëð4ïôêñæõYÿ,‹Åb'@"‘H$‰ @ãøôþñþõ ëð´#ã_O‰ ëðA-ŸŒãY7þñAéòÜðBC¥tëð4ïôêñæõYÿ,‹Åb'@"‘H$‰ @ãøôþñþõ ëðÔ)ã_£‰ ëðA-Ÿì(ã­7þñAéòÜðDE¥tëð4ïôêñæõYÿ,‹Åb'@"‘H$‰ @ãøôþñþõ ëð¼-ã_÷‰ ëðA-¿ä)ãŠüóAéòÜðF¥tëð4ïôêñûôÿY,‹Åb'@ÿ"‘H$‰ @Òãøêñ ëð$.ãJŠëðA-43P7úõ ¥tëð4ïôêñæõYÿ,‹Åb'@"‘H$‰ @ãøôþñþõ ëð„3ã_˜Š ëðA-ŸÄ#ã¢7þñAéòÜðU*éò ëðëðëðUëðëðGëðHëðIëðJëðKÜð¥tëð4ïôêñ ëðþêñY,‹Åb'ÿ@"‘H$‰ ¥@ãøêñ ëð”ÿ5ãìŠ$êëðC-Ä3‹ ëðAéòU:éòëðëðëðUëðëðëðIëðUJëðKëðNëðOëðPëðQëðRÜð¥tëð4ïôêñëðþêñY,‹Åb'ÿ@"‘H$‰ ¥@ãøêñ ëðÿ7ãb‹1úëðC-ô6ã“ 7ëðAéòU*éòëðëðëðUëðëðIëðJëðKëð]ëð^Üð¥tëð4ïôêñ ëðþêñY,‹Åb'ÿ@"‘H$‰ ¥@ãøêñ ëðÿ8ãå‹$úëðC-ä6ã /ŒëðAéòU*éòëðëðëðUëðëðIëðJëðKëð_ëð`Üð¥tëð4ïôêñ ëðþêñY,‹Åb'ÿ@"‘H$‰ ¥@ãøêñ ëð\ÿ;ã\Œ$ªëðC-Œ3€7ëðAéòUjéòëðëðëðUëðëðIëðJëðUKëðRëð^ëðaëðUbëðcëðdëðeëðUfëðgëðhëðiëôUkëðlëðmëðnëðoëðpÜð¥tëð4ïôêñëðþêñY,‹Åb'ÿ@"‘H$‰ ¥@ãøêñ ëðäÿ<ãÑŒUêëðC-,3& ëðAéòU*éòëðëðëðUëðHëðIëðJëðKëðqëðrÜð¥tëð4ïôêñ ëðþêñY,‹Åb'ÿ@"‘H$‰ ¥@ãøêñ ïñ>ãx$ëð}C-<<ãœ7ëðAéòh@ìÐá„GR@Œÿâ…KR@ôãt…HR@, ãÈ…GR@Œã†GR@tãn†HR@lã†HR@¤ã‡GR@¬ãi‡GR@¬㼇GR@¬ãˆHR@,ãcˆGR@¤"㶈GR@ &ã ‰FR@Ì&ã[‰HR@¬,㯉HR@d.ãŠGR@$/ãVŠBR@Ô0㤊HR@T4ã‹LR@Ô5㙋LR@D7ãŒMR@48ㆌKR@œ;ã,LR@T=ã¢KRHëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<çôÜÿ 0Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( Hëð<Üÿ( @ëð4Üÿ(hE\Ñá RE|ã REä ã REã( REüã5 REäãB REãO REã\ REãi REãv RE¤ムRE,"ã RE#ã RET&㪠RE4,ã· RE-ãÄ RE¬.ãÑ RE\0ãÞ REÜ3ãëREÄ4ãü RED6ã ‘ REŒ7ã‘ REä:ã#‘ REl<ã0‘ R@Ä=ã=‘ RUFëðÿòðëðDèófúôñòÿÿêñ¢ áúöñêñhçôZëðRTëðßü¨UUæõ’ãøƒUÿ?"‘H$‰ ÿ@?Y,‹Åbÿ'@?Çãñx÷<¾?©¿F …?B¡P(¤?»ÜÿHÞðÞýõðdeqPYk‰æõ ‘ëð•NéòÉ4†'" ‚d¢´ÆØêüë4êñ óð&þïò„ÿtÀ€'9K]c™ÿÿŽÿÿ{Ÿ±ÃÕçìù x ÿýAëðUML-Maÿster-Shaÿpe speicÿhert einÿgebettetûe òòodellÿinformationen.­bãøð¿ôô?üü¿¿(\Âõ(?þ ¿Øëõz½^ÿÀ#‘H$‰{À4ÀF"= ÀýÍëð@Øëõz½ÿ^@@#‘H$Ÿ‰@@çôðÕ?Pçô€ëðÏ».ëðëð T1<1= BŠU7=jKØQjUëð¿u` èóàw?þ“u› ¥¼ëð@æõòþ@ç€ä÷ýëðu{` èóð?þ0×u8 ëðuëð ëð1Ûrf;ªZblry 5éëð@èóð?òôðöÓ@ûýùøPãø@ûëðu·` óõþ7[u?K= ÕàJS ëðÕëð 17u©k •7uBm ½Gëð2Çqóð¬ñ?òÿèó?¹\.ÿ—Ëåâ? ¿àÿÿÿïAèóÜðh›¼%àÝ”sC˜”(àP•_Gœü+௕eG‡–.ÿ ÐÏàáßý>¿þÿ äølêò ÿÿëñ.ëñ/DVhzŒž°ÂÔæø .@Rdvˆš¬H¾Ðàý/.ìð¥ìðìðìðªìð ìð ìð ìð ªìð ìðìð(UìðìðìðìðUìðìðìðìðUìðìðìðìðÕìðìðìó!ªìð"ìð#ìð$ìð%ªìð&ìð'ìð(ìð)ªìð*ìð+ìð,ìð-ªìð.ìð/ìð0ìð1ªìð2ìð3ìð4ìð5ªìð6ìð7ìð8ìð9ªìð:ìð;ìð<ìð=®?ìð@ìðAªìðBìðCìðDìðEªìðFìðGìðHìðIªìðJìðKìðLìðM3)?;?M?_?q?ƒ?•?ð§?¹?Ë?Ý?ÿÿRuoó0tîðEn÷0GryÜÿOæö üà5‘þŘÿÑ·õ`û&ãùЮIyjMÃ6 U!01yOX‹OO !à5 OÞêóè_ kc@0ÙãëDÃ"nOÿO_¤MµFà1 _S_¾A–T_}__¡_@ ¤_Ó_ãùbú_ oo0oBoTofoxoŠoœo®oÀoÒoäoöo,>PeéóY ¾A!ýð:ËŸÌÒÿÛH’äÌÍ—ãžúëñV!òC-S:@s÷0emîðÎ!ø1 !ëñp8ŒËoŸîѸLG–!<¾ý0ich÷2s+pÍpzÿpfÿp…e@tå"!f!ëñMÿv_kÕ+æBŸ¹¬CJ.E¢ëñº^!Dó0kuÏpe*ý2a÷0ió0ncT(‡ÿ§y»0 š°FŸb­[×+Ãj!Š5"Pó0sÿp\éó÷ÿˆÇVLéFŽ?ã ¹iÎbùòF!‘P‚Êsn€daÿ&E©FºD³íC ¢Äëñ*†!V‚aý2wó0ÅlÿtkÍp’d•ÿB¶~ TKžBŸfÞ×~!5"SÍr“iM€d…ßýƒ‚ìð!såE"ƃÌqlTðn!&!*…Ýã"ÿ2ÉXM²B€‡“j¹Š!EWmœ¢„}Ÿ="pá‘Ú‘Ìqrü«$®™ü@l]<ÿ|Cª@³úÐIÁ,®!͟ߟèô*…æÿ¼ýå¹(M£]›ôø^µrˆšÿð×Kâ5@ÿœlª]yh[ÖøL¯^¯f‹0öÅnâÿkß@²ycx.GñܼÎt•x¯„="w”ƒ4#®™Hÿ¬µû2y°C”³öz:ç±Ê¯¤Ü¯îŸ‘9¿M"M!¢a*°l›°sÉpem"2!þ®™O..½íGèÿF¦¡ŽžòŸ‘Ÿ„¿–¿¨¿¥»¿-"g oñPan!æ!®™ÿ ƒU™-Hÿ•+ëe"aˆ ÏÏ.ϨAÏ-"â±løñPÊqdÏï  ÿDÕóK¼XîM¤Lf"‘‹ÏÏmœI|¿Ï5"Uý2ƒy €þb¿·Æ@òäÞÒÿC†‰`놑ßß*ß”=ß-"LƒöÉp˜ÑT#®™7ÿÃÔ°ü“ªJš6.‡,áö†ß¤˜ßªß—½ß="z“–öô@ÞÓN«œ¦‘DÿBŽÍA–†ã‡€k1ïïmœ§ð;ïE"¯âÏ}JÿöE¦I€æ?˜–œ°÷g†ï˜ïRªï›½ï"Ròp€÷W«œ¹„V<ÿnrAœ&jhÛzDÞ ÿÿ.ÿ­”Aÿm"S™–v„^átâ‘ðnWÀ#®™EQÿô—O¨îi/Îê•„ÿ–ÿ¨ÿ©«»ÿ…"GÍps¢tê!gã²bÿpldüÝÔòýjhlåDÿäJ£NJ9µß#9ƒ$6‚I%"‰T€^áa}#®™oÿBõdŬF®HI™Y·ù¦¸ÊžÝÑÂÆ{|s®™ÿÛI,üùH›VW'›TÜrø&8t«èH6ÝFÿÄT@”ÜÔî̃’³6!û Ÿšt•zY©’p@iÛð¸dßÿ/Eº“ª‘-H€ câE 7…ß ±E–#/K¶˜›dïÿM—6ªÝ!uGÿ’Ó4lÚ7Ejàh/z/^—dñÊñ`8\ç~4¡C+90 ñ 1ü#`ÑÞÓ¯B¬ó×ÿ²,±TóѳŠL@³Ø¶ê%êñž¡gý0e} YÂìó&‰ÿÙb¸Å¥}Cÿ²gJ½ÌCÀøDŸV›nÖ8ƒr LÿD˜µír°ñè¡•¯é¯ÝÍÿTdð´JˆÀ˜ ·ð ¿¿4¥ÿºxjú4Aÿ…Äwx½cœˆðæøŸ§•¯—ÿ¬eB½Š§Æ™J5¬ê/ü/^Ÿ.S¬ó)ßc?eÒB0oŸ’ØÓþ$‹‘¢Ë=#>ÿAˆ°D‘Tñ~"O4O4¥ $¢ÿó¯C™j|µiO^OpO‚O¬óÿˆI]_Fÿœ5qsÛ~1ø²?Ä?ÊñŒá§¨jÿ$’JTB¦ZÃ9¨TÁ­O¿O1¨z¸ÿ v)H“…?S ùxPœê?ÆOPØO¸íOÑÂd0p ‚ùl—R–9c´vÿ@éG¡èc`Ãçé"6_H_Êñìüÿ> ýFG‚7?†E¦ìn_€_ü’_¬ó ‘açÛSÿNBB+F”ñ<»‚¾_Ð_úuzðÿ–,AŒÄ­ºÇJºÌïoÃäÿ噟Dƒ2LJ+´*α7oIoÛNÕmo3¢z?s ps”;ÿ^Á ôZ@ÿ·ë“Ü(r£øp‚Ô[%s½9çÿÑ$F·«AaqÇP²XÊñôlÿ-µ• JŽª? z!™ °oÂoþš6‰.ò~ -Fÿ–I ÊBÑ<ðìoþo¬óåþæ3ÿ h@Ÿiå`[ì½8J{Òœ4ðëãÒ‡þÿ'0 >Mй?|¢ ö¥~þÊñr…U˜.ï´ÿK TÈg$éñÙ¶È4¥b_jÿâS=HƒléâÖüòŸŸ¬óÿ›ˆó½ø+Lÿ•9ù4g êø>ŸJÊñ f_éÿ!tMžçG Cÿ¾©OMWŸßJÑwŸøSòœŸ¬óI gV³ÿ™šEMýÇÇÙ‚@ÄŸÖŸÊñ@ÿ‚c1hiA ?‡y*ôå-üŸ¯þXuŒ”öË®zÿI‚ÅI5Ý0àé2<¯N¯`¯r ˆÚûÿ¼Ì @¿î•m5ˆ6”„¯–¯Ò`ÿ˜X-6Nß?ëg=C*_¼¯Î¯üà¯ÖYÇ”6WVwÿH‡q¼m®C¨c6ÏHÏZÃäEØÿ¯Á±q~;Ò±Öá”;7ÿÄ•B$ÞN†a?ZÐtÄ)ˆÏšÏþÔ[ßãA•ÆÿG µñ‡2lñT>¿"ßþò…ÇQ¬ÿ¢„G«q]ZhÇêl€v¿ˆ¿XuÀ’ÿ ÇèO•'?¼®„SÖ²¿Ä¿üÖ¿ðÄ{«Æd$NÿE¼¹tˆ§`ÙqÃþ¿PŸê¯Ð»Ïã aáÏËð4“°ÿMÏbD•Iÿà T=µ€ß’ߘfÿ¸¬+‘*¸Bœ?G¯Œ{$MëoÍßüßß«ôˆq55ÞvÿåO³øhÛ ¨á+Îñ ßßUx»£ÿª$ÐâEº#À_—3Hß"ÿT"ÿùr\FÈdNxo ~„èïœï,ïÓAïSâLüÇâ’=÷÷…fÎ?;J“œÈ‹ÿ +S $úëñìðSemÿanti«kìðêòèôúÿ2ÀÅ ŽM‘Rc|UèF[ëñuìðPÿðrs Õsez ›ÿé³ÃIóK¬Â¿¸œœeëñ&rìðV7wo9wtl ch õe t ƪ Kÿ­°D‘Ç-8¯ѨåëñìðDNkuDtòo Eæ±èÿÖW8C€ i/¬`Y‚öõP<ôÐ ßýèôÔ²,±ÿTóѳŠ`/³Ø¶¶u<gedîðŠaî9À½ÿŽ+ýçE´M±ki`ÓŸ¶È ÿLf­ÜE¬¦!ž³Y'?üü °ˆ)@ÿŸB‡ÍŽOV£ãzöÿëñý_ÿ¨¿GAŠïìkÊÊR.@ÿX2c.ÐàNÿžcBÕ5]Èá j|Ž éóÜ1• ìðf‰ o—b¦ÿ%…·‰@­D©Á¢JP€ü’ (¤&qбÿD”Óûªš´7ñûÂ?¶"rVëÿ[A­šîI´Çø‚¯”¯ìÛªÆÿ(G‚Àç‹XÇkǹB¿T¿ ÇðÿRÑ’M–œÅ í(Ò¡Á¯Ó¯üå¯ÇZ XéÿA±Æ2OѬŽqÒ ¿Œ¨¿Ö½¿øGBHØÿÎ¥ J« Ñg÷ô+2ÏDÏÿvdÆü%—J©Àò3äÈ™ÿRø´ÏÆÏØÏ$QDÿ‰Ù8™O€ ¦¢$%üÏ¿ëñÿþùóé Š£Jÿ‘¡9¢É¨ÈønÏ€Ï ÁE ªÿÔìJ­¡~ÙG8‰~ú¿–¿ßýèôÿy8ŒËoîÑ;¸:&JuŒÑábçÒáù^`Õÿ}—áÝC¨ò» à=û‚FïXï²ë ÁïÏRIIñšÓù!þæéŠ;;y‰Óÿ@¶p¦òY‘ÿÿ&ÿˆ9ÿ…ÒB*àbÙÒoàhà áýãì@žõ (ÿ·HNO£tÉ€ÿ’ÿ¤ÿ}·ÿ)OrŒÑw—ÒdàÞïÿ½é{Dÿ¡F¤µY8#Ý´(®;MUgàsÝÒüàzaþ-ðÎÑrw ÐÿM¼1™¬ܑΆ˜ª‡½Í"DøäÑâÿ“ K$µÿØ\K¾L·¬ùGÓS, ?SѲÔ!bàl-ðü7QæéÈ÷ÅÏ,ÿUL‘”Ær£.Ä‚”žçW)ôaS8Ãî!VÍð-UÒáy[Bbá ±¸6³6ßáBÓÐoÙÐÖÜ»ÿakí—tHNBÿÅ‹ý¬k¸Føþ/PË)å¹›=ÿÍPB´[Î1Çd¼D„ïÖ/R­ÿú/ZEŒZÃQy‡g—¾ßÐßâßþ*ómëèl ¾*ÿNƒÇ-q›ö‹ðÐQ…ß—ß+òóõrÿ/s±L¦s©‡[ø_Afïžï°¹7”u/ËOÛàjÝòà]]ÿ’o… F€?.ém³ÅÒ´²„?ü–?,ñ¢gH\ ÿK» G?’Áðb¡7?I?[?-ðܼÿb¡¿yK°˜?aõ¹pûü/?þªÒSϪ€w§GÿŠÊ@:Ó E©øº/Ì/P˃€W Aÿòf@¼ S] ‡fæ¼?Î?¬éVÅÏÿJ‰´0/KA«eìlʸ “öOH_Pôµ@õ?QS“@üˆCÜ»M­É§ç¾ÿÍL»°½ëáÅã•V6OHOVÅŽAÿƒeH–‚“:Sòê€_’_PËÿ²fìLÔú›K’ÌŸFø`mÂüÂONo6Hl@ ÿsCŒ×ÜçÉ$ÃöPrO„O–O*óâñÿ¥Äƒn°J¸ø†º","<_N_R¬½?³_ϲCÉ`aýPþÜ»àȾž&ÿNнß|þÔñNò_oVÅWHì·ÿawE¯zð ‡ÿ{ö¹r¬o¾oÐoÿËdNN…Õ”ÿO„×IFy'ñÃôoï,ñôÅ_>ÿþ¸!B§ÏZ¹& Û¦.o@oRœnÿ]¤C úB©¸?®Á^ßrpo/<-Q8Ü»ÿ.‚÷£X'{C»çÈQ‘¹ó’øn€,ñ†<‘{ÿG@»:-^ë‡ÿ¨'¦¸ÊXÄ=ÿÛ÷8ÞL„5êÃû@û&úPËÿÏæo1£H¼$K,«óó’üpJºŽ»ñà¾ÿ¤HÕy°ÐFC\_*<†°¹8¥ÊKòE]Òfy˜/TŒÿdPbÚ{@ƒá?¾€{az;lŸ~Ÿþ,ñ ‰h§ÅÈÿLkä]¼ôðx¡ßŸñŸSÈzCDÞÿð³AD—pÇŠ-ÇU¤Ÿ¶¯,ñòÿß¡¢;E8G°ol¡-ïÝäüöVÅ .ȹNnÿüGÒ»œônƒ)‘ Ÿ2ŸD¯*ó9įϲLo¢qAÖZÙ—ÿ¹ ?Bšx8Yž…P¿b¿DŸþ*ó_æ·$²ÅÿCŒêêåŸñœš¯¬¯N›>?¡Gÿ#ÈCª¦_Ãl4b¯t¯,ñ„ÿ‘õ*‹X®C˜k¹rŸÙܯüBÏ,ñÿQRJûÎÿlM¡µK®C8?¿&¿„¿*ó3¿BGÒZÿ²ú±*±þ±t'²þœ+ ‡[*ÿO—ñáirñ¾&Ï8Ï L?¥ÖÿMâF­Y¾"Ǩ³ÖhÏzÏ,ñ¨¤ÿŲfäI2?­Gq’ïõÚ¿ì¿üþ¿L¤PžŒ„cûÿFšÓ1>íQñ Ï~ß,ñûЧ8ÿ×ÀÐJœZã‡"ü,õBÜÏîÏßS;ßKòD­Ðz·Ðø°ÑÚž)¬(žOÿ5¤JŽØ¯ì³ÇáöË(ï:ï,ñB]ÿ5±›¿þN‹T? <Ñœ=¨bßtßþ LòĞܡºCÿ±A>Í8 ðÜßîßïÜt¥iždÿ%K„ƒú/Èãó³)‚¨ßºßLÿ9t#2N—U¿ßhÛLŽà`ïsÿ te—nzìðÜÿìðþèô4³,±TóÑÿ³Š`³Øu¶ëñìðGaõò]z3hlìðìðýèô0¿Aÿ°@“ê£Ë»ûûK½*Pos+iñðiinùöHÿü©û U1€Aÿ¥ä>wâREºëñeerksüñþHX:ѳœÿ­H‹¶Õ‚…ï+ë ëñ&ìðV¡4WtwirñðlmÅc=kóðnx }ÿ7•º ìH׿‘½v$ëñúìðDikumâóòt3pèô‰ÿ_·KH³-Cü¯·hû*Sóðm¢ái÷úÿ :%pµe?þD ÿ'šv"ϾAÿ¶Åx `ã¼WøbtH2åÎXšÿ;'A¢‰ÐSÇ,î¹ 2z ~vÿþý¿ÝöH¿Ÿ?ò_ýÕ`2/þëñ€.D—“{óÿI®œ‹ô·µñÙ˜ªHåH°ßÿÍkPK «§6@Îí\Ôæø ‚Úéó>› ìðBùyñòB M–ãyÿ&6F‰ÝŸQƒÇ´Û#/(/z ÎFÿ•"PÁKªª?_FËÅÏ~X/r?þëñËWeFÊÌÐÿ@ŒÊ«7ûÉñÞðëñÛ´Í­ÿ,ÿSF±)Ðø°ÞÓµ/¢/H—ÿŒ¾Ò:•M•Xì©ÒŠ07Ì/èÞ/ð/yOL8ŒË¿oîѸ$«&˜9wBt10púAEÁÛäâÿÁJD»úò‡ÇV?h?rGŒEª•OÏ1SñòróðoüµOèô†ê[°”fÿoLªÎK£W-ÞOðO_„_UìðAõðfYBdBåuõðg§TÂIÕãÿ+oêÜKˆs<ÖPŽÞ`_r_rGýXèôœõ µÿÞB… ¸÷CÍrëñ,QþSscBe£0 M¡0¸Qlð?rAÝ š^<îÿ8N›b÷|äw“>|ObDDòñFaP3øþSèôÿcZ ÔÌDÿ©‰;žàOR,eA÷¨1v¥2äñðÉsõPLg1oDHsAÿNSA…pNœ½?ç%¶::Bæ_ø_úrCIèô­$î‹ÿoþBšPú¦šWÀÒþeGCba p%t÷ðuB4dìðaýFèôýNHúÔ¸ÿEH­·"5F¨U Œ@'RÜ}dg3ùgÝbÂIĨj0ëÿíRIº+ÉYb‡‹ãÜþorGhu3ÿÆ1ºq2G­?töÍܤí®0‚þ”ÍËεÿÞÊI½|û¾Gψ¸ÊrGHèôÿM’pÎß!¶LÿºMbIœJNóðwaìð%èôÿ¾ºàÅnL•³(³¨XÂAPîñ½f¬1Lqb±RlÁPýe—~t5ù'q¿ÿ1I‚#~6ÜÃFý6HrG……éÿ¡ËC•D”l?%W'f+v¨ºý2¿Lüänô‰ÿÿN¡êœœ]¥Ã¸™ìþrGH/$ÿ¥Ù™IŒ?¬H«•þ;vŸˆŸèrG˜"GèôФ:ÿg?<L‚ØjO}Ï45‚ªQE]rðT{ÞasaÂIåêªIÿ=yK¹©¨µ NE?¼ŸÎŸrG ¥ÿIÙƒFÃ#‚Eÿ£Ú¹º¯ð‰éˆ2—°Qƒs/¯êòÂIÿƒ‘ÁE“%œºš´†šb¯üt¯ZtÆÔÅ6ÿÜF½¼ÙHÎK«3óð¥Z¡0m}`i*óð ×0yõðcóð`qþŒ‰V$ Ì„ YÿN·c`ë7yoñ2ŸDŸV0¼ðNÿ(¾_F´D’‡‰¥¼61󯿜ÿg."†£@¯?9_?Ùñ+Ýz¿ü¾ÂI/Ff³{íÿ>H¸3` ¯ãØçº¿Ì¿šŸh QÿªCB‰¤ôqªDÏVÏrGøð½f½d…'ÄÛ"Õÿ°[LµHQóÃeý®1 B>»„ÿvø·¼3Á©N¢Éîa(5‚øÏŒ¿ÂIð¯, ñÿG»Dœë=¼ ã@L5›ÏÝ¿µ–ÿýáµBA½…?=œð—;…*ß<ßþ˜}Â7´?ÁåÿNªuv£Oxyñ8n߀ßhÏs’îÿ•;ÅN¼‹i6ïPïïpÿð) M•?£Åòcÿ*ÿürC…ü¶ç°èÆÿóDŽÎW^}›ãÂÔïæï˜}“[òÿ·’G’, /´½×^ÿpÿrKøýÄíd…oн„ÿÿonN– ´úƒ‡…ïPÜ¢;¾¬í¢µ@ÿͤHgB¬χ"ÑRÔûäÿüöÿ‚ûZzæAÆÿ¿KƒQWWÃáƒãò¤ÿêïÂI¼¸ÿ³{ìC´Bœþ2‹Œž’ßÿ$L-žTVVDÿ•@;ø5@!møÎà˜}ýN„ãÇÿ:¤Iž‚@hs‚$rKH¾ þd…[¨ÜAÕ=ÄÿD·¬ÊcrªúðŒBµaL¿…Ëÿíÿ¨äbHtŽñ‡A$`"\äÂI›ÿBÀ¸!ÝÕA·Eˆ3ÄOŽáœü®6PdR´9ÿ;FŠýÍÈXãTÅ8/J/˜}U|ÿ¿r¢GªÄwe^Ï__/;ÿÿ©î]‡Åu‰Dÿ€¯û÷ÛCÑÀ??rG²-(-¢µÎåÿðzÕ.wA®|îDZ uŸ]?üÿ Œè ™œ,@Œö(õÁYLÍüÓ/—~8ê,/×ÿÝI³¡[C¸ãž²?Ä? /2ï³ÿà*a›D’ÊyÊÂhxíô?OVÿÁëž$Gþ F¾û$i ”7"ø•ÑÿjF§Qñ«¦ãÕ\`Çÿ!éaÀÒO˜#¿”Úõ;¼ÂOüÔOæO§öWCÿxF¾;LþEðãš__¢bËûÎÿJãºEª/ì2v6ܺ@_R_d_bÿ§º’jH§1%fU)÷Ɇ_ü˜_¨¡TÇ ,„ ÿN޹ìá_u²Â_Ô_à{œº=ÿ)˜º I†Ø)K ž`oodÿåÒ±ËÃï@žßisæU>•h~¯¯¢¯…µ¯¿ú@}¥*§í¹ÿM¦NƒFššôGslr¿(¿:¿M¿¨ó”AAl§°i«°eüq²ÊK\ç;m©ÿäJŸ}qD#ð÷–¿¨¿º¿uÍ¿…b•i-Àv“`rÅrp³ þ}yu°%•ÿB«(Ã"úãØÂÏ0Ï–{‹QÏ_²a©Àf`ìÁL~Aqôÿ²Y2 °N©?œžOxb͜ϮÏÒÀÏvÓÏÒN“`c•h‹bdgÂgפzÏ,ÿ!ceì¯Ó@¦ÏDüäkT.ߤ.ß@ß~Sßß²Mó²aê±Ðl¹”e[¯Ð“ÿÃ>q±N»í¤¨=ï·ßÉßSŒÝß_²K@p]ùrõÏ/2s¹íFÿAFŒÐ›±9GUv@*ï<ïNï’aï"…bP@СhpBÊÿà¯bðTHÿ›U$>$‚Ï·hªï¼ïÎïqáï,@%I;ðpë²<ÿsûß¡[ÿ÷ÿ˜L E—˜3ùP†Š,ÿ>ÿp5ë ëñèô•8ŒÿËoîѸ_`&ëñìðÿAufzßählnUgìðìðìðèôÿ…úZ6jŠ¡Eÿ‰ìŒ.™ ª¨úëñìðDokîme#taª_iSnìðæöWNèôe8ÿþ2IQÇL-Qb\yp['ëñ4ÿ'ŒËwÓµ]ÀOyèÛëñ©(ìð"seg#eëdîðsorª_(ed)ìðþ/vÙŠ"Éfÿ@³œ³Ím?UÔëñ&ìðV[raª^wïleckâ[i_l2$·ŸÿÍNŒÍ‡o¤gÆKNìð•S[m+iUJVÿžhhCV¼Mƒ+žÊŽÙjáPSÞd 2rËÿY&¿²M†<›_8"Êê P'êÞs£zI‘æöÿâ,p@ÃÈ©î†UJ\n„ãùº&Ãìþ {ÿî»X*[Mz~ˆ*™#/ü(/L6‡›êCÿ.Gÿ6íûxãC˜¢¾/ëñÐÂ4ÿe©"H§%7Ë‹`j!3EþìðTpBRp©ÿH¦™D\žñQj|ëñ¬hˆ‡ÿÙS N½E7d€°Úì@/éó)#Y/w;2au z/þéóZ{µù€iÿG¨Osùû.ñÇ ?²?2‚×LÜÿ‰àI»û옩ºõ¢/´/L³ÿz2=ØzÖC ¨k0»2g_ä/ü~OëñáÚ‚Iùÿ¬@¬KîëÒâþ?.?@?éó”yÿoŠÄâYF?Çæz]Nh?z;´Ä?éó!Ý?ìðbŠk@oeéBþ?éó ÿe<ƒß"BD«}é¤ù)²íbOütOLƒpß ÛÕÿEAŒr]BˆÞñ,%B,_~?øŸÿaÿ+ ËIQ¯¦Çøn™Ö§OÍ_|ÿÖJb‹CE½2zÒì&Oü8O2dí»þôÝÿ‹F›¹½­À¥€CÜOîO]H_Ta_Âo»`¤x/2§Yÿ5ƒy£K¾U§®¾T  o²oþ_þ°ORÿfþJ¼ËÇòÁÇ9áð_oëñ…ÿú´ÞQ$Dˆ?…ó©ˆKV®_À_þLxÅ#ƒˆô‰ÿ@º 0ûåñÄ(oÒëñ7’è?ÿ£5MŒ0ÞÃZXÏ`oroÐoéó))éofoÍr þé󿇱ìqÿK‘û’_O_ñ1¶ÈLævVTÿ››tG†¾±ígÙˆvøÒëñ€ÿ˜š’Gª‰åaƒ¸[0üB2PŠÀÖ£ùÿÖI•%.ÌÉÝÃ4â2DVéó†½ÿ¡t­ÅºM·b?KõÅ+•`~:_X'mµ†$üŽÆDÒÁ `fÿ@‰ÊÀLÊoáilŸ~ŸŸéóQ|ÄÿH—˜±I­3ÇÄbYH;¸Ÿ ÿx-´¦ºI¬‡Cº·À730ŸüBŸJFä)[ÿN´ÙÂKstñño¶ÈLÊù~¶ÿÒwAO½•òòâÇcø2/ÜŸs%ñŸ„ixåü¯éóuyF@f™ÿŽH‚¢Å̼\ãuž:¿¿ëñšÿŸœcÌC—­±;© ü¯Î¯2ÿè&®ßç)6Eÿ¼á%8=2´ñøø¯ ¿L›wS ôÿaCN§QÃÍÙ<¯N¯`¯† ƒÿJ‚æ “E¨’?ÇJÿ(B…Ça¦Ç‚îßïL^ÿ ªù‚LJ€F?vxÕBÓÕzßêïþ’¼M³‹±Cÿ’=ß“ f_ÀøBßTßëñ]#IÊ/ÿ·™D¦dL‹xÇíá ²ßÄß2öäÿ¯!öS1F¸‚â*jþ3ïEïLWïiï3}ïÑâsßàÉqÐàácß×VI‡ÿŠ4” J´?Œ]¡“|¼ÿÎÿü–Ϧ´¹]ŠKÿE¦œz‡‚Pñ’ÎïàïLDÔÝÿ nKœæ³–ªÜeHÿZÿŒá ÿ~«PúHˆ§µšñqéHÿüÀŒáü£êÿ-D›ÿ{˜C~ó€ÿ’ÿðÿã2  ÒowPjµq0ãÿ;±×WÐ1’Nÿ®:tâõ£øæøŒá iOsÿlÉM‚þí×÷Ç×£yVhØUWÂÿßM>óI§Õ?ù){›™ýXjü|ãû«4¢ÿYM°{ž‘é2ã¦O¤¶ë@¸ÿ(~E5K¥Žï’¸–ÔL¿~G “á`!yµþä¼&»!ú¡ÿDªíÄòH{á¡R/d/v/ãÀ¸‡ÿ‚ç»â˜(Ÿ¢b¯t¯ºxÿ€éua”ñKOä2ý2ÆXêŸüˆ¿ŒáêGütÿeM’NþÜ©`ãT*쯸Œáb«ÿ×Oµ‰E²ËË« ‹»v"¯4¯0õÿ÷+c¬ñÒD®ã¤Èq r¤¯¶¯Jo¿7%¿ Ò&þJ¿é ì „niÿD¨jâæœ¹añ´Ï0Ï0õRýžÛÿщL‡³ß«¤X4ZÏlÏįÈÿ,íL­»H›u’Ëß^l¿ü~¿ëš8VJ—ÿ²@ބžã¾Ã®¿–ߌáÈZ«ÿI-'M»Ïç¿•æ¿þ¯ŽÍü!bà‰8ŒËoîÿѸ`S&Š"àT‘ÐbûÀl½’ýá-øjÿTQ–PL…ýÌßч¸zß(Œßç(å£1ï‘ ál»Ñ€c\éƒÇÿïÖ¢L›&à¸l®¢xïŠïœï©Ž¯ïÁâfýÀe…bdú;YìdEõ¤äÿê=A ^}‰VGF=#ôïÿÿ™+ÿ*CâF‡ðsaÍÀåýWYì?RGZŒ<ÿIF©¯éVRH,¬áß ìðDÿokumentaUi÷ðnìðæöèôÿ¦8ŒËoîÑÿ¸`&*ëñìðl÷òa;ïð«>ìðìðèôÿЗê,uCHžÒÉñ sËëñhòÿ %2ìðUPk{tîðd{]rîðobÅs®| EÍn{ûWK NÙåôsÿ–H¾O˜¨+CRn€ œ¡®7SyÓmçÿ”2ÈLÿƒFTúº+)Šh .tAìð…zÏöÇØ6N ™ÿ;8/a*I¿’%á°z†ø˜ªRøbò†èÿžL”„ª—ùÕëñU`a""i ¨†½Ïýa sfüUhÇb! rìðK ÿ¨iƒ¼Kÿµ1Ùæ@ÊÞh //ªry/ìðÕTYpìðK bµÿƒ¤Fœ?A‚?â[ER šÆ/Ø/Zê/wý/ìðVE0*œ!eÃiO0gI0˜1÷ K ¦~ÿ·ZF—Œ‹ÄMÏX“•ÿÑjF§Qñ«ÿ¦ÕW'ÄÛ"ÿÕ°[LµHQoóeýnÌoÕW˜oÿ3Æ1ºqÿ2G­töÍܤóíntÆÔÿÅ6ÜF½¼ÙHÏÎK3óða  }šÿ^<î8N›b¿÷|ä“>Qžþÿ\:ËŸÌÒÛÿH’äÌÍ—žýúQýNHúÔ¸ÿEH­·"5F¨ÿ oн„ÿoÿnN– ´úƒ…ÃïPða¨}(>º=ÿ)˜º I†Øÿ)K SúÿÎ¥µ÷K¼ÔÑÉtbßðað}Üÿ§öWCÿxF¾;LþEðÿš[¨ÜAÕ=ÿÄD·¬ÊcrªÃúða8$LÛqÿo©ciqH 'ÿ<²PËcÿZ ÔÌD©‰¿;žàOÇQ­ÿ$î‹oþBšPú¦šÀÒNü¸ìðв,±TóïѳŠ.³Ø¶'`8CìSÑÅ×"êòÒïŸêòÓŸ+Ÿ"êòÔCŸUŸêòÕmŸŸ"êòÖ—Ÿ©Ÿêò×ÁŸÓŸ"êòØëŸýŸêòÙ¯'¯"êòÚ?¯Q¯êòÛi¯{¯"êòÜ“¯¥¯êòݽ¯Ï¯"êòÞç¯ù¯êòß¿#¿"êòà;¿M¿êòáe¿w¿–êò3³»aŸ¿ëT4@¹¿Ë¿-Sã¿õ¿êò7 ÏDÏêò87ÏIÏêò9aÏDsÏêò:‹ÏÏêò;µÏDÇÏêò<ßÏñÏêò> ßßêò?3ßEß1c]ßoßþêó'ŒËwÓÿµ]ÀOyèÛ‰e—ßëT!±ßÃßêò"ˆÛßíßêò#ïïêò$ˆ/ïAïêò%Yïkïêò&ˆƒï•ïêò'­ï¿ïêò(ˆ×ïéïêò)ÿÿêò0ˆ+ÿ=ÿêò1Uÿgÿêò2ÿ‘ÿ³³©ÿ»ÿݳÓÿåÿíB5ýÿ-S'91Ã@Qc[Ã{íBp…—zä‡qÏá÷/íAs#5·Yíuw‰íBv¡³m?×íxõ/¯C/1/íBz I/[/s_/í|/¯/"íB}Ç/Ù/íB~ñ/?‚íB?-?ù_Q?íˆo??íB‚™?«?íBƒˆÃ?Õ?íB„í?ÿ?íB…ˆO)OíB†AOSOíB‡ˆkO}OíBˆ•O§OñR‰ˆ¿OÑOñRŠéOûOñR‹ˆ_%_ñRŒ=_O_ñRˆg_y_ñRŽ‘_£_ñRˆ»_Í_bå_÷_b‘ˆo!ob’9oKoº‚“ˆcouoº‚”oŸoº‚•ˆ·oÉoº‚–áoóoº‚—ˆ º‚˜5Gº‚™ˆ_qº‚š‰›º‚›ˆ³Åº‚œÝﺂˆº‚ž1Cº‚Ÿˆ[meB …—eB¡ˆ¯ÁeB¢ÙëeB£ˆŸŸeB¤-Ÿ?ŸeB¥ˆWŸiŸeB¦Ÿ“ŸeB§ˆ«Ÿ½ŸeB¨ÕŸçŸeB«ˆÿŸ¯eB¬)¯;¯eB­S¯e¯eB®}¯¯eBž½¶¯†µ–Ýெµ´ ¿¬Æø5¼4¿†µœõ ÿµÞB… ¸ç÷Ír^¿è&š^<ÿî8N›b÷|ä“>x½œ§œ¡Ƕٿë¿ý¿Ï!Ï3ÏEÏWÏiÏ{ÏϟϱÏÃÏÕÏçÏùÏ ßß/ßAßSßeßw߉ߛ߭߿ßÑßãßõßïï+ï=ïOïaïsï…ï—ï©ï»ïÍïßïñïÿÿ'ÿ9ÿKÿ]ÿoÿÿ“ÿ¥ÿ·ÿÉÿÛÿDZnž £º–m B|£ '¢E BŒÃ l®£ Bœã »E B¬!TÇ@Bq@ëôéöéòëðÿu` væõþu ñ@ëôêòòõþëðu`½ èóð?þ{uþq@ëôêòòöëðÿu` Þèóð?þu q@ëôéöêòëðÿu` væõþuð?þq@ëôéöéòëðÿu` væõþu œ‰ì&àÐAŠ'àÐ"C‹T'à7Ð*C‹¤'àaÐ%C‹ô'à†Ð'C‹D(à­Ð"CU–ëðDëðFëðéò¥ÿÿúñ åöêëðçôhëð,#àëBîðxëðT þëðd)à”Ï»~ëðPlëðœÿ&àÏФëðUùòÕØëðHëð„ëð'÷Iéò¹®õëðÿ ™)ÿª€9l6ûþò€áÿ@GÓ1^xâùúçôFëðÌà^ëðÖ#þëðd“á”»LëðB ë𜯔á\]Ia$¯ÚÖ¥]ëð@«+ëðäk¦]7úaNëð¬"àsoÑUëðVUFëðÿòðëðDèófúôñòÿÿêñP àûöñêñh> ëð/Tëð6Dªêñëð’ãø·UUF?›?èóÀ¬ÿ¿F …B¡P( ¤?¿3ãõßü«3éò­þÚ¶þ bû=bþIÀêñ•[ƒ)@®nÐØ‘uU¶åµx Ò{?>ÿ/e„EZßTêñƒãø.>UëðHÜu‹éò óð}éòÿ­ÿ*ø?ÿÿàÀ ÿÿôÌÄŒÿôÿƒÌÄ‹ÿüû„Ì=L„ÌO?Šÿ…ÌÏK Eô?8Œ0ÿÿ¨ÿýlëðFügt iÿhrem Aktÿivitätsdÿiagramm ÿden Zustÿand eineÿs Objekt_s vorrüòÿon durchÿ beteiliÿgte Ereiÿgnisse h?inzu.ýbéòÇãñx<뾿ôô?üü¿h‹ÿì¿¿pù?@ +ø34àC•HëðÿòðëôëðDŠëðëðéòòñ ˆïô# ä÷ëðöñ=hjçôëð4>Tëð!ßüYY9 ëð#ª›ãøuAÿU@@ ÷ð?³FÇã¯ñx<ÎľÖPn6  ‰uŒ—`þj˜AI˜Œ ˜#,'U DŒ¸D#D‰úþ èóà?þ` '‰"‰‚6FÅTÅ^¦nu#x‰¶éþ ‰b%J& !…>êñ{‰ÿU2Nè´NKk?<@æõŒ+#í2‰rëðòÀÛ¢#ëð [!{®Gáz„?£ú]“;&þ&‰d&õn! ëðb£: !“ þ„‰êñÙA5#5 0‰`ÿUML Bacÿkground ÿAdd-on`ÿ /CMD=10ÿ01€y@ƒþ†êñm!Æ*,#(òð? "! êñJ!°,%?,$A "W b$ q6q6%q1¤•êñ!Ã),#Ñü.2éòu7£%€5#ýòðÚ1þß45 ýE‘"`;Copyÿright (c÷) 20 Micÿrosoft Cÿorp. Allÿe Rechteÿ vorbehaÿlten.þþ‰`Vis_Dÿ112.chm!ÿ#31442þ)l>(>UdúPâùD5„ ‰ãøÃ$w ‰aþ³B¹E¹@Š)(„°YFæõ×nt –þnêŽ(pB]Ù ·(¼ÛD$ÏØ ÎèyðòO…RH _~ntnYžQúVþhèÿ"R—XAQ3®ÛO`]œæõx__#g4þ[bacr0¸_Ê_Uä_ö_šUnP&SaEK+ 7 ©ãøºÔ7êñÚ1Vô@A">C&Seÿmantikfeÿhler anzÿeigenþü‘#„RÀ`.U #„þÚ"à/’ò-24 "!]rFd%€1!W„R q‚,%¸Ô;'{>e!A@÷%&EWqscha]f7@þ5ù3+ƒp¹-ŠRv odPpb ea1@i7Cqƒô/§{ "!g³AdaºªƒêñÉãøZATöñŸ!Ÿ!,"ddº"e!e!3îñîñD2Äp$!’<I,” J9”|IKF”E´)(ƒTyëE@²155*þV‘º%b”èur™\V‘"3=cšœ›!$Ê{“MOV‘D5Wcšñq”ú€U@þ8G¯V«V‘<©CÀ–(q”[c¨ €Â @’Ë5áSŸuA@:St¿ellt d8€Aÿnfangspuÿnkt eineÿs neu erùsÄ¢8€Objekÿts dar. à€“| ¨¯¤¤`IPþÖ¡,Shapes?,Statu4±Å£÷ar,ð£,Wasý,Σ,Uml,Nïeu,Eæ¤,Beginnt€“E=Cú”>C10.0þq~HfCcqOƒOŒ™ãø 1 oÝÂ!…R@ Ä®7AŒ__ò ÆBùƪxç±,%‡NZmXÆ Å¿ÏoBÍžQgÉð?UËsκ%ŒË[X®ÆÏÀÏ›_yïÂ[ÇþÄkÏ}Ä3óµ6n¥ÎC SßËÏwÛ¡fØšÚcÀ«w,•A]qa]qÞýŠ,%-ãøäƒCUs.iAhaϹ=.T7”ãø3Ã/¹°¿'2Çqð¼hC","úöþByüúa0‡•³u´ÿEC”U­:¥Dš™ÕØëðHëð„ëðu÷Iéòžo õëðÿ =ÿÕ’×L®RûþóÖÖï_I™˜ä"âùçôýFëðt<àfÒì$##ëðL•á¿<ÓUëðB« ëðÜY‘]v~aô¯ÔÔß@+U–áË5aòñœ¸×Ô=#:ÖUFëðÿòðëðDèófúôñòÿÿêñP àûöñêñh> ëð/Tëð6Dªêñëð’ãøÀU•F?›?ެ¿ÿF …B¡P(¤…?¿3ãõßüU3éò­þí¶þ bý=bþ aþ”„êñI•@®wЈYØš~sUÀå¾øà€:ÿ/ e„ZÑ %æõ¹ƒãø>UëðGåu›éò óðéòÿ¨ÿ,þü?ø øÿÿÐÿÿüˆÌφÿ«Ïˆ2…2Š:„€BTfA<94ÿÿ´ÿýëðBezeicÿhnet im ÿAktivitäÿtsdiagraÿmm, wennÿ ein Obj÷ekter Bÿedingung entspröðót,#ÿòon aÿusführt »od'aufEÿreignis ÿwartet.ýbãøØ¿¬ÕjµoZ­Æ?ôô?«ýóÿ¿¤p= ×£Øÿ¿ô¶åÉõÇ­??ó¿èóì׿˜™=å3À?ÐÌMÔ?UGëðÿòð ëðDèóúôéòòñ # ¨àûöñêñhçôëð0ëðTëð`âù¨UUæõëðëð*ëðëðëð›y ˆïU@èóØ?@»`f¶Æ?Aèóèÿ?F«ÕjµZ­uÖ¼ØÅÆ?Py -’‰“ú ”êñÿˆŒ”UAööñ µ …tˆ`ÚU€½@þ‚t´ŒŒ…u‰ ¾èóà?þˆ‰  Û"Û,Û6Û@ÛJÛTÛ^ÛhÛrÛš|¬už·H…«_#´_#á]þöñaŽ$– .r b  êñ„ᨰ%€`!Õ öZ ¢"b• $í& ¨!¢" í!„…êñ½¨âù°"þY`UMLÿ Backgroÿund Add-ÿon` /CMÿD=1001€%y“¤‰êñ!9Eyü04 þ£Jêñ€¹#ýòðª1þ¯4ú";…`Visÿ_D112.chÿm!#31449÷þEq"`;Coÿpyright ß(c) 2Y0 Mÿicrosoftÿ Corp. Aÿlle Rechÿte vorbeÿhalten.þ¾‘êñ¹$õàûK8ëðeŒ$Z! ßüŒg5€ ƒãøç#UUHéò•A°%¡DT ¬G5¡Dö¬CÉ8ÊZPˆQTTR T?Bg|`]3_¨zèEæõ†…Tr€dT°"`26A6Ag2 ‹T›2È1È1 ¥TxHáá RI¿T?BJÌT2EKÙTr´­(¨Q¬`P·D@²1Ö"aüWéQ56CTèª1ˆ‹"bç#` éQg5CHöZ5ebATˆng Ûêñð jDd›5õ[Cð?o¶aéQ WöZ¡@dRAmcú÷f†x TsT Œké`éQxøAWhdá!Bë éQ Rëð/ªl‚1 cSá#¿oTëðñ•(l†s\S`|Stÿellt einÿe Bedingÿung dar,¿ währeG0dûerÜq Obje½kÛ~erfüÙp,þÜsAktion ÿausführt» oùqaufürEÿreignis ?wartet'@éQðEð¼ÏC`yShÿapes,StaÃtuƒØrìqƒ,W×as,B…,%ƒ,Uÿml,Zustaçnd,ñtµ€friŸedens×qÂMÿanche,Wiýr÷purchge€/‚€MEcäR˜TdþÉ310.0þq‘mA¬A]YâùmA€mI„H\öñr|™‚ãø‡¹#Ï Í3ˆa¿H€5`‘¬²¬P)Ùð¿ º•?_#±–¡#¨!Œ‘°% \Sž•ئ±–ß²’äŸ['¥·``"¥ ®5˜›ÄI§¿T¯f¯ô”¯¾û’š§ ¤ª¯#¤ g52«Ö²œ¸—?ö¯ÕŸç{¡ ­?º·`«›5˜›!ȧŸ¹Ÿ˜¿êñЯ¿ôŸ¯K¿ ˜›â ÏÏ1Ï€ÀIÏ[ÏmÏ®x2®@? ÑJ¯õ¯n¯æÎáÁ•¯A¿ §nu2¯È¶Jß\ßnßæÎGÁ•ß§ß §?E˯ÝÜì¯þß¿?É.¿?ï¨EdîCÝ…ï—ï©ïÙÉÇïÙï¨ý…ËÕ3¦Àv¿ˆ¿3û3áÏÀ¿Ò¿ä¿HUËÕx À©ÿ»ÿ@3û›3GÏóÿ n$l‘(cG`!D™l‘›5\©E˜º¤7>¡ª1Zf_‘ú`"É3&Semÿantikfehml7nzD€en¢]-Fðq"r‡bÀ`.¡#ICa329?K=2\4öbr6AòÀ€"¨P@¨°%Y¤;]“HQÌ0_ï%Cav"26!¢2Ê5áËÛíÊÌ0&Oýp'en fürm ~‚-AI..'@9æ3/*os%H4ƒbea@i$CvˆBM;8\4&/Ìg5¸G/|í6C`%&ELßschaf$@þ5æ3+Ü -Q:®/À/HÒ/ä/¢1ý/1&,u”@`‘bf@bAN;¤-E˜q(c8J.B­xüœqf@¸€p›ƒcÿàCœ½C.INUHëðÿòðëðLèóuDëðëðïô" ïô# ä÷ëðöñ¤=#hçôöñ(ëðTëðßüöñEªêñJ›ãø–Œ§U@æõ«Aèóèÿ?F«ÕjµZ­«Ö?«P >b·€½@þðúöX>Füý~¿¿ßïç?tA`êp ëðòÀ_b € ²¢+S  ëðö£l“-ëð# ²ù¥q>&8Q<%$ëðÆt#!A\ƒ£,I”“R*œùö öl³ ŒõZûláþ\¯a#$+ š> öþ…‰êñ“þ>U2{®Gágz„?‡½À?A¥D#6>l,¢e#<ëðT$¤?GNT&þ*}*<W£7!“öô›c&þ„>êñˆN#âùE" N°¨pE%ýˆ7"b5$U<6 <6<1i&8¡b€ %:çô”ãø3üô/j'2Çq¢º0¼\"E""¹6þUl>ëð0bUh€hâùã1ã1„C ‰Šãø¢4[@+%*+%+ ŠB(`N#½ˆ G>u6èóþ~Bu”‰Hõ´â2  k°FÁ‹è(4½tO†Gð?’OŽCÊA<½ïO_PWÊAEÖKˆB_ŽOf[ "êñ2dO Eðr24þ ðS=0Û1##$è7 Àßü e M&Iç1¨ClüTëðßü  9 #3öñõ›ãøAUAüæõ«Füý~¿ß§ïç?«ÏP6 vJ>BJ™ö ö# ³÷#%t‰Ï€Ï@þ‰«u3`Q'‰+uL_A-S½Õ# SY‰pú ëðòÀb ·€ S¢9ëð í± £&ëð“F›Íf³ÙlÖ?nŸb €.S…tQBH]ÖðáþŠ«a$$ ‰ ¥b ‡>êñQ‰Uo2ÇqN ¬?M/ü_&èó?¹\.—ËWåâ?¥àòðïÏ”>"0"6!„‰êñ7LG#5 J#$ë i€o@þ JêñIG#>%ø=†¯ •áž/#«b.$,6,1¤"B2G#¥åöüòðçôQþ•Ti8^(Eþ.çô”ãø3«/î‹'_$¼p">""†ä6þj‰‘?£?ãø•âãøSÍ7p$RO!3pBó¿0æõxF d4'6!lJöxAUddâùöö¨€ ‰ãøÍ4¨‰‹$%$%$ Š*;(`G#F¬Š5u#`W6þFR\$QXð$  ï#|R‹Ÿ(@,T¾D_ð?Z_VS’Q3ž_°_Â_aZa’QEñU€3_ oW_/j|P4KoK‰rA0 þ²b¸c-0•HëðÿòðëôëðD:ëðëðïô !ïô#öñä÷ëð3Ò#hçôöñ$>T‚ëð ßüöñ3 A#›zãøzAUAèóoØ?Af­Æ?£¥è«Ö´ªP6 vB>ï­]îøVøJt#` ½ èóà?€Ç@mþ&töð`ëð}uLb?],JuL.A)-Aµ# A?å+J¾jb Ó€.AJuH-5»T5JõÙþ†ajÿ J bþ)„>êñ7JU5 çt÷h€k§@þ JêñF*#p!%6(Añ tB.®#b$¨&¨!iH%ëð^5 Ê{)”ãø3*/ 'o2Çq0¼?8&šñ6þlJî0>Uddâùîî€ ¨öñ ‰ãø4¨…L%% в(`3ÇÌ6£JÁ‚0þà2˜Üò1ñø  X‘"E#B‹h(@Æ:¼Ö?Þð?ô?ð3,A38N¿QOcO²G,A×%8KÓ0œOè?ÀO,A êñ)2Æ?;JrA þLRRS© _HëðÆëðßÃIéò‚më þñÿ ÿi ØÜ ž†÷þó'g¿‡AÔÌßâù úçôFëð4=à÷÷ÚKÖ#þëð´–áÑÛ»TëðB ëðTÿ—á%Ü…>a4æÔªoïð¯@+ëðÜk«êoEaGUåÔÍðoRPòñìÕBäÁþðþ¬,Õçû¬»D'Õ¯é(»UFëðÿòðëðDèófúôñòÿÿêñP àûöñêñh> ëð/Tëð6Dªêñëð’ãø·UUF?›?èóÀ¬ÿ¿F …B¡P( ¤?¿3ãõßü«3éò­þÚ¶þ bû=bþIÀêñ•[ƒ)@®nÐØ‘uU¶åµx Ò{?>ÿ/e„EZßTêñƒãø.>UëðHÜu[éò óðMéòÿ®ÿþ„ÿÿƒÿ¿ƒÿ߯ðßÿÿÿþÿÿíÿðÿ&o„ÿŒ-Ž&9ÿÿÅÿýŒëðFügt dÿem Aktivÿitätsdiaÿgramm eiÿnen Überÿgang von Status ïs zu m aÏnder!hiÿnzu.er Ýs"e 25 pÿerform cÿertain sÿpecifiedÿ actions.õbéòÿôò׿P&“Éd2¹?éòûØ?üô¿Dÿšià¿ä“>éï“>Ñ??9\h¦Á¿m¿èó°<èóØ?<¼•Hëðÿòð ëôëðDŠëðëð éòòñ ˆïô# ä÷ëðöñ=h´çôöñ@>Tëð(PßüöñE=ëð¥ëðëð#›úãølAU@xÿ»Ýn·Ûé?@ÿÞï÷ûý~ï?ý@Aè?@Lÿ&“Éd2É?@º×Ù¹?P6 º‰uöð`u äb€A[]þu  ¥‰ .2˜A˜¨@˜êñ‰íuŒ` èóà?ª$Ycšmu‹Ò‰õ‰aòéööñ b%ÚÚ…>êñý£‰U2Nè´N{?<]éò@AÁ îa"b)b·þ2‰rëðòoÀ¢#ëð ëð{2¢"‘?£ëð“>&þ&!&<U ,£öñ“ §þ‡‰êñQ8$›Çqõ ¬?ô/6ýòð?¹\.—¯Ëåâ?Œàòðïð=De þtêñz8#@îvÁ×?@àʼÂíòw?@ÜÊ„Yƒ7~2X8«!dµ]²2¡ "êñ|8#.&?ÒAs! "bDEFFf! #FU F F F A餕êñ8#„³å#üòðv@«!]Œð?þ‹B Œ %F A1/DB0Ûg tN ˜zs×¥ ÏLœ™êñ¹øþ圌ù/˘¿÷?2rþ"á?@ŸkôI"PÏUÑ?M&Òõ ˆôß?ëþKRÆœñÒ¯Mbä^P*=Lê A-[b}rêñÀŒ† @þ˜7g¡gZKR}Ru €S@`SÆRŒšB g-‹BuŒ` åS&ßþ„êñuß46 ¡~2µg¦X0=`ÿUML Bacÿkground ÿAdd-on`ÿ /CMD=10Ÿ01€yV¡ > ‹f 7!¤æ8#Iýòð²aþ·d5 ‰ÿ`Vis_D1ÿ12.chm!#ÿ31439þEþ!`;Copyrÿight (c)û 2{` Micrÿosoft Coÿrp. Alleÿ Rechte ÿvorbehalten.þ¿ƒ»"(kP@«!ql>œUd gâùI5„ ‰ãøø8#ò0 aþ®ÀraÈsÏpŠ ,(]¼æõìJZæõ_[ÝW‹â(ç{Øþ} ï^\ÝWn‰ha frãøÖ5„ ©ãøº@¬gŒ²a †@A"Ñcÿ&Semantÿikfehlerÿ anzeigeÏnþ¼#”BÀ_`.D` Ub[omm2~d$ *’%€ÇP@æSlðÄ/%Y¬kß‹> 'Óa_E/‘)—Lh’%GÄå%ËžŸ°hþÔ`&Optioÿnen für Shape-A “«../p9îc/BªoÜ6¥ • odbe…a&pi,s)Ÿ;Ÿok8\žó›I5¸ ¯°!Ô`ï%&E‘sch»af,pþ5îc+Ÿ -ºq¯ƒ¯•¯§¯wkǯþiAa¸¬‰=æ7”ãø3,?–€'4¼F#ú²RAggda9Ï4w É­ ‰T¤<<Ò@ddå"!!I2^‘ÄÖ2ÇP ÂmI«Ä÷BJ$¸Ä3KÅÄ£b´,( ³­TŒV@€Aü5X#þÕÁå%áÄè²a|EõÅ^±ÕÁI5=âÊÛ!Àœ—ŒkcÒµðtÕÁÖ5WâÈ øðÄ8¶ßÅÛ‹B”Ð’ÔÒm¶s?ÖðÄa"Ò!¤ÕÁ÷EVßôÀGÑc=ÿStellt eýi:¡Übergaëng#pnHâm Z/ustav°iZã;±G°; dar/pÕÁ3Ô”Á(ïOÑcEBàueßrung,B¢s,ÿEins,Status,XàÀàDâ{àý,sã,Fluss=,Oå,UmlÿãeðÐcÝðJ10.0‡þj•®¿À¿ãø•`ãø®Á'd Æpÿ!3Œòƒó¿Uaéò”ö@¿ik™*ÿ<ÿÞ¸ˆÞ¸#'g¡q½ÿÏÿÞ¸™Þ¸\R4Ví<Ï~CmsK_]]ÝW,ÍÄ’q‘‘€‘>ÍðI5‡Pà¶À“TShow Name.Úð/%aëBàreotypöå%~1fÀû “Ç.BKÈ«!‘€š‚ÿ ÷ä¡aH>?’ÏÜeÕØëðHëð„ëð'÷£éòŠx õëðÿ ¿ßÿBI%.Ø>÷þóí™_C;çâùçôýFëð\>à›ìwÖëðÖ#ëðÿD˜áqí]NëðB ëð¤Yõ¿]‘aüïÖûPî@+¾ëð4™áQJ¤aòñÜGÖUFëðÿòðëðDèófúôñòÿÿêñP àûöñêñh> ëð/Tëð6Dªêñëð’ãø·UUF?›?èóÀ¬ÿ¿F …B¡P( ¤?¿3ãõßü«3éò­þÚ¶þ bû=bþIÀêñ•[ƒ)@®nÐØ‘uU¶åµx Ò{?>ÿ/e„EZßTêñƒãø.>UëðHÜuféò óðXéòÿ®ÿþ„ÿÿƒÿ¿ƒÿ߯ñŒcßÿÿÿþÿÿíÿðÿ&ÿ„ÿ ðÿš8-Ž&DÿÿÅÿýeëðFügt dÿem Aktivÿitätsdiaÿgramm eiÿn Objektÿ hinzu, ÷dasöðn Flÿuss zu u?nd von øôÿonsstatuÿs aufzeigt.µbéòÿôò׿æõöéòØ?æõDÿšià¿9\Ïh¦Á? È¿ûöèó •Hëðÿòð ëôëðDŠëðëðéòòñ ˆïô# ä÷ëðöñ=h´çôöñ<>Tëð$PßüöñE=ëðEëðëð# ›ãøýlAU@x»ÿÝn·Ûé?@àÿï÷ûý~ï?@¾Aè?@æõ@ª×ÕP6 ‰u¿ `u ¿äbþ«u  -‰ ">·€Ýþ‰–Öþ˜Aј @‰uvŒ` èóà?jÕq{…u”"‰D4€EWh[óÞ bú¦{…>êñ­‰Uÿ2Nè´N{U?éòÖ@2A¶ €AW]"çbe$ "b·þ2‰rëðòoÀ¢#ëð ëð»2ž"‘?£[“Ž:&þ&‹& ª#£ !“ ~ #bþ‡‰ºêñQ4$Çqû ¬Ù?ú/ 6òð?ÿ¹\.—Ëåâ?Jàòðïg = "Mþêñ¶4#§@îv½Ã¼¾í-òÃh—u„‰7p„2p“?¥5qÑ7"¼3„2 Û?¿0‰ êñ|4#*&?ÈÎA "! "b $EFEFb! #UEF EF OFOF¥ OA¤•êñ4#‹Cü 1*#[&ð?pH[ óy$© ! "!†Bt™N˜zsW R ’\œ™êñµà È÷¿2r2á?@Ÿ[ôIdPÏ?ZTÑç¤=…‹…»ȯ?þ*/L|A.@brêñŸÀŒöð@Pj …¿Ru€.W… ah"òW[u` þË!Ïœ`'þ„êñu!D6 ¡kÑ2èX0/`ÿUML Bacÿkground ÿAdd-on`ÿ /CMD=1001€yßV V7 Êf £d ’4#ýòðñaþöd5 þ‰`Vis_Dÿ112.chm!ÿ#31444þýE`;Copyÿright (c÷) 2º` Micÿrosoft Cÿorp. Allÿe Rechteÿ vorbehaÿlten.þl8>hUdPâùO5„ ‰ãø4#|4@öñaþÆrWaÎsÕpŠ((:X4×9_Ea[¨S"Db‹è(íuçÕØA‹¶0þn‰Œ aâeæõ…9 ©ãøº€ëgŒñaó@AËÿ`&Semanÿtikfehleÿr anzeigŸenþ¸#ÖB¿À`.Ü$`$h”bšo¬m2½d{$h’‹%€ `Yˆ3ÖB¶+%¸ëkÑ‹>¼Œ!p%&E‘sïchafkpþ5Ê-s+--¼šü† o]dú€beaepiks Ÿ-Ÿ®oQ›gGiAá^E9 (G”²ãø3coF ' 4¼,B#ÉE€ggöñdaá¯CÉ© ‰T Îddë"Œ!Œ!O2Z¡Z¡B `H²§BIIS´9RJ`´3Km´Ò ´((²“T@Vu@ÂA>EYþ}±ë%‰´èña¾Eµ¡}±O5=Š¹Â¼!„7Œ¶3 ÂÑ2ÖB}±Eó#ç¿AE8hÏwË}±§E¼s7Ƙ´]"Î!f#}±9Uá]¶¿œ°PqDSt¿ellt dâFÿluss einÿes Objekßts zuí“voýnôÂm AktiÿonszustaŽ darnp}±3MôÇÏœ°>s4Shaÿpes,StatÇus, ÐZÐæÃarÕ,ûÃ,ïÂ,Ó,UCml§³âes»½sÿ10.0þj•V¯h¯ãø•ãøV±fdl±¦ï!3$âó¿”aéò,æP@þik™ÂßÔß"ãøˆãø#fg,‘a‘ÞýO5.æõþ ±Show Name.Ìâ+%ÎaÝåäÀ?reotypèäë%pk°íÿƒ.Bßhü[a8 •‡9%ÿïN¬Ór•S1æ·ÕØëðHëð„ëðu÷£éòz õëðÿ ¨ÿvªâ ÷çôTlïK¯†½(DâùçôFþëðDMàÑö»ÖëðÖ#ëðœÿ™á§÷VîëðB ëð šáëý]laÄñÖ÷iø@U+ëð|kj5a¾òñ”OàŸý:ÖUFëðÿòðëðDèófúôñòÿÿêñP àûöñêñhçôëð½(ëðTëðPßüUUæõBëðµ’ãø»UF?ú£ ¿´¿F …B¡P(¤?ÇŒÜXîâù?éò ßbþ&b¯þ$‰ëð FZAdD·:êñ÷@æõs…Aæõ™…„.&b.ÆdS«ÓÚÚ‰Ú&. ÔT™Ó ., Ú ÚÅÀB«À?Ê„-Е.ا.‹(sUÂÍ!åË& í/€:ÿO/„‰êñ5u($âù:@Re„AZx €bêñƒãøÀUëðGò"u¯éò óð¡éòÿ©ÿøÿÿûÿƒÿ*ûÿÿ÷»ÿÿóõõ ýþÿÿøÿÿÿþƒÿþ(Èÿð…Šú<<‹ÿÿëðÿHM‚ØNLdH„ÿÓAðA…÷ŽBÿÿ…ÿýcëðFügt Iÿhrem Kolÿlaboratiÿonsdiagrÿamm die ÿBeziehunÿg zwischûen!ei Kl¿assifirösrþðen-Sÿhapes hinzu.]bÜÿ¸?ôü¿èóïè¿03Ï?€ÿè´N{?0-Í ú÷UGëðÿòðëðDèóúôéòòñ # ¨àûöñêñhçôëð4^ëðTëðßü¨UUæõëðëðªëðëð›ãøìþ„U@ßï÷ûý~ï?@ð²¯?ñ?@èóÈÀõÁ¸ÉP-D_Tû!ù?…u `u üäbþôWu ü -…)ÿún2€Ñþ…mÊþ…uˆ»` èóà?^)uŒi"…8(ó€KûbT\­³ ³¦‰Ù „ÿÿ$UDó ð­€óòòòŠðÒOC# È ˜Õ]&H"Vщ dëð•òeŒ"êñ¶ä…®à±° ¿ò?‡$‰ÑÙ'ÐÔ"Ðã/õ%Ñ!7í-Hv'+?õ ¤…êñU¨#v†ü ÜŸ"­ b¿;þ… tNü SzsK¢2 ¨< ‰êñ´q4Ÿ%[Ÿ$„­ý³ bþt‰ U?" öñC!C­C!C!!C+!C5!C˜COC?!P)G’0@­A„²êñ[q5z 0#`ÿUML Bacÿkground ÿAdd-on`ÿ /CMD=1001€yK"v"ØJ@íF £%€¨#ýòðQþT¦KýE `;Copyÿright (c÷) 2Ý@ Micÿrosoft Cÿorp. Allÿe Rechteÿ vorbehaÿlten.þþ…`Vis_Dÿ112.chm!ÿ#31454þ)¾•êñq4EzA9’$„!úñ],ßü!!|Ž Uöñ!­)ƒãø÷3UUHéòaŸ%+döñ6gh5+d!6gÐ5+d­6g—E+d!6gS%+d!!6g˜U+d+!6gx+d5!6gE+d˜6g!'h?!6g­'hO6cgøT5!`]tdsêñUÉãø­øRT+!c¥tŸ"ôôT QÐ2``}2dd ^æt˜R‹! ‹!x•!•!Ef „(rI„ErI'„ëRJ4„†!!KA„+!×2Ÿ%µ žsj3 #û3œüRîfV¨V (vEvëUƒ ?‚ˆ‚L‚ˆ‚‰u Uïù& &U&!&+&Ø3ìšYc…T@mŠPþ„Ø1Ð5=^pŠÔ'êñð ˜ûQ—…’’—E%”èQ]`\Ø1S% uR›4”VüRÝúŸ ¯¯'¥OÔ–D’˜U’U—4”9ªG£x1R™¦‘Tëð¡ßüYY9 ©#öñ›ãøí‰Um@èó¸?³³¥È»à»ºP6 J[‰tŒ` ½ èóð?þøjþ à‰uÿÆ!u#6þ ^‰bW^™ ^øöñAUöñ >•|S‡>êñ‘­2æõì³Åòð?¹\.—Ëåâ?MàòðïA;ÄUþú,,'~Xbþ…‰úêñƒ­Nè´GN{?<¼Œ£Õ6‰p ëðò·À¢#ëð Â®—"‘?£“W&Sþ* "€*Ž! ëðê@ £X“^þ¤•êñÃ!°èó_Ù4@ ð&î°Ñ¤¢@‰ tÕB€î@Wb•`J1œ‘êñ"üA° Fi4F“£á»—#Á»ç V÷uLA-Ãò>3+"¶2b €I.@1,2.æ;DŠBL0‰+töð`Xtëð:CSêñz¥Ã»2L@1@ C{б'êñï ÀFx0Ñh4_ª?þ„B7ç)¤ïv†eGŠ—€K@1l> ,>UdÊ1âù \5„ T#‰ãø6 ‰waþ4RaTëð ßüYY9 +¨#Cëð›ãøZÞAUAèó¸?Þ·è?A3ÊÓ?©@æõÉÃÑP6 -þ‰u `uý äbëu  €_@þ‰ÒËþ"‰€¢%‰ ¢Zk‰uu+`hà?ˆãu#“©b6µ» »2 A ª@$Ùã†ã‡>êñ!´2ðæõ$/6&#?¹\._—Ëåâ?hàòðAï·")5'(q,•q,q'…‰êñ7þ#0z®Gázƒ„?èóÒ;AµJ»»êñx±­Ì 2¬¿À Ï¿Ë+‰t ulÉä €·W@þQsp cëðwòÀ ¢5ëðÓ ØÁ£9“tìYtLŠ4b [€.%S3!ýb>1B7S3$,@S3ž0Ùá7Ø íØë?ý0Ü2"Ý3Gõ? í=Oý0) êñ<#%'œ@) tB64´+b½ÁA¤•êñI&#h üòðçô }rêñ ÀþœB±Ü$_6_í Ó’’1Aü@S3‰ ¥ðœt¦‰ >1”hÆY)íþ GçâBŸC)ñÆç„êñª2UÝþb'l>#íUdçâù#3 òˆ‰ãøŒD î‰aþµba‚Z_:³`(×Ë1‹Š(:#·Ë(!_z$/‹¬(úeÊŠS+}¬(µaaåöjA^„E9 .”G•ãøS9gÆ$8ð6`Êó¿0æõ†6ïD"á'i f®ÀÊåö”ãø39oi '/2Çq€¼Æ"f' œ†þk•I[ãøˆãø Tëð ßüYY9 +¨#Cëð›ãøZÞAUAèó¸?Þ·è?A3ÊÓ?©@æõÉÃÑP6 -þ‰u `uý äbëu  €_@þ‰ÒËþ"‰€¢%‰ ¢Zk‰uu+`hà?ˆãu#“©b6µ» »2YAYª@$Ùã†ã‡>êñ!´2ðæõ$/6&#?¹\._—Ëåâ?hàòðAï·")5'(q,•q,q'…‰êñ7þ#0z®Gázƒ„?èóÒ;AµJ»»êñ‚±­Ì 2¬¿À Ï¿Ë+‰t ulÉä €·W@þQsp cëðÿòÀb ¢M5ëð ØÁ£9k“t ëðtLŠ4ßb €.%"S3!ýb>1B7S3$,S3ž0Ùá72ØYíØë?ý0]"Ý3Gõ?ˆ  Bí=Oý0 êñ<#%'@Î) tB64´b•½ËA¤•êñ&¤#h üòðçô r¾êñ ÀþœB±Ü._@_í Ó’1IAü@S3‰¥ðœt¦‰>1P”hÐY)íþ GçâBXC)ñç#„êñ.3UÝþÓŠ'\Gl>#íUdçâù#3 ˆ¹‰ãø–D1ïaþÉba:Z_: (×.Ë1Š(:#·†Ë(š_z$/‹@¬(uÊ'”S?}¬(H1aåöjA^¸ŽE9 žG•ãøS¸CgÆ$!Êó¿0æõ*†6ùD"á'¡ifÂÔåö”ãø3üCoi '2ÇqÒ±€¼Æ"f'°†þk•]oãøˆãø Fg•HëðÿòðëôëðDŠëðëððó "ïôïô#ä÷ëðöñª=hçôëð8>Tëð ßüYY9 +¨#Cëð›ãøZÞAUAèó¸?Þ·è?A3ÊÓ?©@æõÉÃÑP6 -þ‰u `uý äbëu  €_@þ‰ÒËþ"‰€¢%‰ ¢Zk‰uu+`hà?ˆãu#“©b6µ» »6 >Ùª@$Ùã†ã‡>êñ!´2ðæõ$/6&#?¹\._—Ëåâ?hàòðAï·")5'(q,•q,q'…‰êñ7þ#0z®Gázƒ„?èóÒ;AµJ»»êñ‚±­Ì 2¬¿À Ï¿Ë+‰t ulÉä €·W@þQsp cëðÿòÀb ¢M5ëð ØÁ£9³“tµtLŠ4bo €.%S3!ýb>1B7S3$,S3ž0Ùá7ØÙíØë?ý0]"Ý3Gõ? DYBí=Oý0  êñ<#%'@)ç tB64´bJ½ËA¤•êñ&#Rh üòðçô rêñ_ ÀþœB±€Ü._@_í Ó’1¤Aü@S3‰¥ðœt¦‰>1”(hÐY)íþ Gç€âBá2C)ñç„êñ.3UÝþÓ'Å\Gl>#íUdçâù#3 ˆ‰ôãø–DCa]þÉbaZ_:pà0(×Ë1Š(1:#·Ë(š_z$/‹¬(uÊ'”S?}j‰A^ŽE9 .žG•ãøSCgÆ$îè!Êó¿0@æõ†6ùD"á'ö( ÙTåöif¦¸åöå”ãø3Coi '2—Çq±€¼Æ"f'†°†þk•]oãøˆãø Fg•HëðÿòðëôëðDŠëðëððó "ïôïô#ä÷ëðöñª=hçôëð8>Tëð ßüYY9 +¨#Cëð›ãø¢ÞAUAèó¸?Þ·è?A3ÊÓ?©@æõÉÃÑP6 -þ‰u `uý äbëu  €_@þ‰ÒËþ"‰€¢%‰ ¢Zk‰uu+`hà?ˆãu#“©b6µ» »2µAµª@$ÙãÊãíöñç€&)#íD/=%‡>êñY!´2æõl/~&þ]#?¹\.—Ëå+â?hàòðï·]"¨)}'(¹,¹,Ò¹'…‰êñ7f#0z®Gáz„?èóPÒ;Aµ»»©êñ‚±ÌQ2¬u¿À Ï¿+‰™t uÉíä €·@þQêsp cëðòÀ¿b ¢5ëð iØÁ£9“tîtLÒ4b [€.%›3!ýb†1Š7›3$,@›3æ0Ù)GصíØ3OB ]A"Ý{G=O IE‘í…OB  êñ<Âf#]%['@) t¹B~4´b½’Q¤•êñ&f#h ü¸òðçôE!rêñ /ÀþœB±Ü@v_ˆ_í ÓÚ1ARü@›3‰¥ðœt¦‰†1”hi)íþRGçâÀLB(2LC)ñç„„êñOf#ÝþÓ'ˆ¤M;/))l>#íUdçâù#3 ˆ¹‰ãøÞD¤Aïaþ5ra:Z§: (×.AŠZ(:f#·†8â_Â$/‹@ô(zuÊ“ÜS«}j‰ÂA^ÖE9 æGÅ•ãøS‹g4T!Êó¿0æõz†6AT]")7>8¤Aaåö¡i[f$åö”ãø3ü‹o± '2ÇqÒ¼2®'–þk•ÉÛãøˆãø Žg•HëðÿòðëôëðDŠëðëððó "ïôïô#ä÷ëðöñª=hçôëð8>Tëð ßüYY9 +¨#Cëð›ãøZÞAUAèó¸?Þ·è?A3ÊÓ?©@æõÉÃÑP6 -þ‰u `uý äbëu  €_@þ‰ÒËþ"‰€¢%‰ ¢Zk‰uu+`hà?ˆãu#“©b6µ» »2@Ùª@$Ùã†ã‡>êñ!´2ðæõ$/6&#?¹\._—Ëåâ?hàòðAï·")5'(q,•q,q'…‰êñ7þ#0z®Gázƒ„?èóÒ;AµJ»»êñ‚±­Ì 2¬¿À Ï¿Ë+‰t ulÉä €·W@þQsp cëðÿòÀb ¢M5ëð ØÁ£9³“tçtLŠ4bo €.%S3!ýb>1B7S3$,S3ž0Ùá7ØÙíØë?ý0]"Ý3Gõ? DµBí=Oý0  êñ<#%'@)ç tB64´bJ½ËA¤•êñ&#Rh üòðçô rêñ_ ÀþœB±€Ü._@_í Ó’1¤Aü@S3‰¥ðœt¦‰>1”hÐY GçâBá2C¨)ñç)íþ„êñ.3UÝþÓ'Å\Gl>#íUdçâù#3 ˆ‰¼ãø–D ‰a»þÉbaZŽ_: (×Ë1‹Š(:#·Ë(!š_z$/‹¬(uÊ'”S?}¬(ÉaaåöjA^ŽE9 .žG•ãøSCgÆ$î!Êó¿0@æõ*†6ùD"á'if(ÂÔåö”ãø3Coi ¿'2Çq±€¼4Æ"f'°†þk•]oãøˆãø Fg•HëðÿòðëôëðDŠëðëððó "ïôïô#ä÷ëðöñª=hçôëð8>Tëð ßüYY9 +¨#Cëð›ãøZÞAUAèó¸?Þ·è?A3ÊÓ?©@æõÉÃÑP6 -þ‰u `uý äbëu  €_@þ‰ÒËþ"‰€¢%‰ ¢Zk‰uu+`hà?ˆãu#“©b6µ» »2@ÙÙU$ÙããC‡>êñ!´2æõø$/6&#?¹\.—¯Ëåâ?hàòðï ·")5'(q,Jq,q'…‰êñ7#ÿ0z®Gáz„A?èóÒ;Aµ»¥»êñ‚±ÌÖ 2¬¿À Ï¿e+‰t u¶Éä €·@«þQsp cëðòÿÀb ¢5¦ëð ØÁ£9“¹ttLŠ4bo €.%S3!ýb>1B7S3$,S3ž0Ùá7ØÙíØë?ý0]"Ý3Gõ? DCí=Oý0  êñ<#%'@)ç tB64´bJ½ËA¤•êñ&#Rh üòðçô rêñ ÀþœB ±Ü@._@_í Ó’1ARü@S3‰¥ðœt¦‰>1”hÐY GÙâBæC)ŒñÙ„êñ.3U(ÝþÓ'\Gl>#íUdÙâù#ä3 ˆ‰ãø–DÝ ‰aþ¹bauZ_: (\×Ë1Š(:# ·Ë(š_z$/‹¬(þeÊ”S/}¬(¹aaåöjAp^ŽE9 žG•ãøqS3gÆ$ô!Êó¿0æõ†6ùD"Bá'if²Äåö”ãøù33oi '2Ç¥q¡€¼Æ"f' †þ!k•M_ãøˆãø 6g•HëðÿòðëôëðDŠëðëððó "ïôïô#ä÷ëðöñª=hçôëð8>Tëð ßüYY9 +¨#Cëð›ãøZÞAUAèó¸?Þ·è?A3ÊÓ?©@æõÉÃÑP6 -þ‰u `uý äbëu  €_@þ‰ÒËþ"‰€¢%‰ ¢Zk‰uu+`hà?ˆãu#“©b6µ» »2@Ùª@$Ùã†ã‡>êñ!´2ðæõ$/6&#?¹\._—Ëåâ?hàòðAï·")5'(q,•q,q'…‰êñ7þ#0z®Gázƒ„?èóÒ;AµJ»»êñ‚±­Ì 2¬¿À Ï¿Ë+‰t ulÉä €·W@þQsp cëðÿòÀb ¢M5ëð ØÁ£9k“tëðtLŠ4ßb €.%"S3!ýb>1B7S3$,S3ž0Ùá72ØÙíØë?ý0]"Ý3Gõ?ˆ çBí=Oý0 êñ<#%'@Î) tB64´b•½ËA¤•êñ&¤#h üòðçô r>êñ ÀþœB ±€Ü._@_í Ó’1¤Aü@S3‰¥ðœt¦‰>1”hÐY GçâBá2C)ñç„êñ.3QUÝþÓ'\Gl>#íUdçâùÈ#3 ˆ‰ãø–D» ‰aþ¹bëaZ_: ¸(×Ë1Š(:#·Ë(š_z$/‹¬(þeÊ”S/} 1¯%¹aaåöjAá^ŽE9 žG•âãøS3gÆ$ô!Êó¿0æõ†6ùD„"á'if²Äåö”òãø33oi '2KÇq¡€¼Æ"f' †Cþk•M_ãøˆãø 6g•HëðÿòðëôëðDŠëðëððó "ïôïô#ä÷ëðöñª=hçôëð8>Tëð ßüYY9 +¨#Cëð›ãøZÞAUAèó¸?Þ·è?A3ÊÓ?©@æõÉÃÑP6 -þ‰u `uý äbëu  €_@þ‰ÒËþ"‰€¢%‰ ¢Zk‰uu+`hà?ˆãu#“©b6µ» »6>Ùª@$Ùã†ã‡>êñ!´2ðæõ$/6&#?¹\._—Ëåâ?hàòðAï·")5'(q,•q,q'…‰êñ7þ#0z®Gázƒ„?èóÒ;AµJ»»êñF±­Ì 2¬¿À Ï¿Ë+‰t ulÉä €·G@þsÀS3!ýb>1B7S3$,BS3.Ù¥7ØÙíØ¯?Á0]"Ý÷7¹? Ç3‘íOÁ0¤êñ&¤#h üòðçô r>êñ Àþ •êñ…<#%'@) stB64´b½ÈAœB ±ÜòO_(í ÓLAü©@S3‰¥ðœt¦‰>1”h”YÎ7çâÈ2¥2È3)ñFç„êñ.3UÝþÓ' Gl>#íUdçâù#3 òˆ‰ãø“D î‰aþ}baÖZp cëðòÀb (×1Š(:#·Ë(C^_z$/‹¬(ÂeÊÛoXSómÙ¯%}a$aåöjA^RE\9 ›G•ãøS÷WÜÆ$¸!Êó¿0æõÞv6nD"á'iPÇVvˆåö”ãø3÷_~i '2Çqe€i¼Æ"f'd†þk•#ãøˆãø úWUHëðÿòðëðDèóŽúôöñòñ  ïô# àûöñêñh>,^ëðTëð6”Dêñëðëð#êëð›ãøp„U@èóÈ?Aæõ°Ð?@ÃÁ¯ÀæõÿQ(-DTû!ùU¿…tˆ`öñßb€°@þÅ…°ÿYV¹uLô„-¥ˆ ÿ+…×Aš™N¹(b g€.4uó6û u#`bCÿµ…Ý?ÿ R…‘°· ·m$‰ÕQ ZÔãÏÕ1ßøã‡êñ„!¤2æõ*/<&#?ÿ¹\.—Ëåâ?/ àòðï°"©…<&þq"w,Jw,w'„…êñ7$#¸âù"ã€*@Ëþ‰ÕÔ ‰êñ<$#%'ƒq" tBô°ú°%<1œêñ¡Ë$¹]?Py ›w] .êñð?šŒ­=¨?¾9C€"Óã›uÖ;ú2 'M›u] î90œ“tô±tëðPCSŒôêñz¥_„b@@þiÖëðZN5| .7”ãø3¾/o ¿'2ÇqÒ@¼5?h+ÑFþj…~OOãø•ãøIºGÙB=_6êñ!3]Ró¿FAéòeV=0òðåök‰ûO _*ãøˆãøºJK8<\ÜöUHëðÿòðëðDèóžúôïô  ïô# àûöñêñhçô ½,ëðTëðPßü  æõëðëðÔ#öñ›ãøp„ëU@æõA¨ Ð?@­ÃÁ?¹0ÄQÿ(-DTû!ù¿ª…tˆ` bºS€°@þ…ذÿY¹uLªô„-ˆ ôÿ+…Aš™úN¹(b €.l4uóbý Su#{…Ý+?ÿ …û°Õ· ·$‰Õ ZÔãÏÕßøãC‡êñ!¤2æõø*/<&#?¹\.—ÿËåâ? à¢òðï°"Õ;'þªq"w,w,w'„„…êñ7$#âù"㻀*@þ‰LÕÔ ‰êñ<$#á%'ƒq" tB¤ô°|°<1œBB¡Ë¹]?Py â›w] êñð?šŒ­=¨?¾3¡¯6ÕÓ㛣uÖ;ú2 '›iu] î90œtô’±tëðPCSôêñzq¥_„b@@þiÖÂëðZE| 7å”ãø3¾/o '2·ÇqÒ@¼?h+†ÑFþj…~OOãø•ÂãøIºGÙB=_êñ!3]Ró¿FAéòeV=0òðBåök‰ûO _ãøˆãøºJ_ÊHëðvïðßãéò†×Ùëðÿ ÿºÞÎëý˜ùÏêñ7‡¿ÛImJE*âùúçôFëðÄbà6ºDïðÖ#ëðäÿšáz„îëðB ëðœ›áëþ]naäöÖïl@û+ëðœámÜ-BGëð¼f7à—õPòñÿdጠû>ªÌ…àÊù ^»ä•àNï‘»”¨à¿ß‹»Üÿ¸àj®þ»üËàû»DÜà§ï‡»íàß.#Š» þà¸&a»ÿ á*ùjþð¬¼+áƒ,[]UFëðÿòðëðDèófúôñòÿÿêñP àûöñêñh> ëð/Tëð6Dªêñëð’ãø·UUF?›?èóÀ¬ÿ¿F …B¡P( ¤?¿3ãõßü«3éò­þÚ¶þ bû=bþIÀêñ•[ƒ)@®nÐØ‘uU¶åµx Ò{?>ÿ/e„EZßTêñƒãø.>UëðHÜuõéò óðçéòÿ¡ÿBø?ÿÿçÿÏÿÿß÷ÿÿ¸ß;ÿÿ` þÀ ¤ÿÿüÌÌŒÿýüÌ‹ÿÏ„þV‰ÿüÿôÌÿÄÿü‰ÿÏôƒÌÄÿψhü„ÌÿχHûÿLwOü‡ÿüÿ…Ìt‡~Prrk_ŠVŠN ŒH÷ÿýyëðFügt iÿhrem Aktÿivitätsdÿiagramm ÿden Zustÿand eineÿs Objektÿs nach Bÿeendigunõgrü÷ hinÿzu.y inÿ the encÏlosi7te.ýbéòÇãñx<뾿ôô?üü¿Å_lêeÀ? ÿ¿ð÷ûý~¿÷ÿ¿@ øï¿Àßï5Þ¿<à¿UHëðÿòð ëðLèóDëðëðéòòñ ïô# ä÷ëðöñêñ¥>hçô 4JTëðßü  G #Uëðëðëð›ãøýÅMU@àïÿ÷ûý~ï?@ÿ@ ð?FÇãñx<ÎÈu¾ÚP  >»uA`þ’‘L M A  # 0+A ! +HX+YÀpAp#pÕ> èóà?—þˆ‘? µ&µ:ÉRÝf@ùt"€"ŠšSu#¤>í[þ >b[%d&¥ d!…JêñY>Uÿ2{®Gáz„•?H@9 #N"v&þ&>rëðßòÀ¢ëð  ^ëðb£^!“d 'þ†>êñ#‰#‚ PKòðK^"d! ‘êñž!´€%K€$M®^" b\$ O6JO6O1¤•êñ!õ)"€#ü 2éòS7„™êñéAõ-~$0>`ÿUML Backÿground Aÿdd-on` ÿ/CMD=100o1€y(0þ£Jêñ€‰#ýòð AþDú¸;EÃ"`;Copÿyright (ïc) 2ï0 Miÿcrosoft ÿCorp. Alÿle Rechtÿe vorbehÿalten.þý>`Vis_ÿD112.chmÿ!#31441SþlJ˜(JUh N!âù˜u5ˆ ‰ãøúõ$Daþ*åBëEë@Š}(*4ÿF ƒÁ`0˜ÖÚš š™R4Q¹­š>-é(ý¼ TÎf³Ùl6EËÚ?fVí¥ð$\‹ÍÌ«Qì:_©šD š>#ªWN"¥ [ l< >"“VQS3_U^FÀÝUŠ_%_7_À_¢×_vjö_oeŒAA”O¦O¸OÊO+6åDíOÿO3`æõ_€¡š‚oN_/dÓÜ ÎXo‰d¢Æ_§o ‰Åo0×oo]Wy~FÈÝæõïœ%¢ÛA?Þ‡[m…n‘A¥eP5 ì%I©ãøºG êñ A0–LM"pCÿ&Semantiÿkfehler ÿanzeigençþÃ#Ê‚À`o.]#`[ ’É2Ï?á=2ò4^"4d!â’È%€õ1N!Mð‘‘€%¸Kð¬›JÉ!s@%&E¾Ü‘schafi@+þ5+C+ -—ª×–w odÕbeac@iiCöŸ¯ã?,«^"d!g•öñhe?¿C ÉãøñcTÑ!Ñ!€"hhì"É!É!3îñîñu2I ¦²©2I±´ý2J$¾´ KË´m’´}(£]T¥€E@15G+þÛ±ì%ç´è¤•÷¹á¡Û±33è¹ Ìd!˜¥aR¨?Û±u5Wb@Êö´²#ŒÆþ8‘ÂÏÑËÛ±©5ÛCEÆö´]3€±0@Âèý5ù1Ï”pCŠStÿellt dasß letz^@Voßrkomm½ ei¿nes ErÛnçiss]ÐÒ m Eýi›¡ließungszustaù þÉ¢die BeeÏndig{ÐYÒr ÿAktivitäst n߀Ôdarl@¤Û±€s!ßú°dpCZÿShapes,SÏtatuùÑ@Ôr,ú`Õ,¡Ö,Endeß,Uml,®×enÿ,Fertigs?Ñ{Ð,PÖÃm•oCÍýpC10.0þq™ùgg™ãøè#™?±1Ë‚èä Œ7MÒäF*èæM#!ñæ/¨Ãá€%‡Åzäx„ñæèå¿úï5ýƒCùð?1ûOþì%hûÒxŠöñïœÿ¥Ëò7÷ÚôGÿYô"5ÏåÜ‚üˆ÷— /§ÿS &–áøv ?ð0§,ú4·áâ‘àâ‘D½‚€%-5x¥al°ÈŸÚ“.iËÔ·á:‘ä«é³å‚275Ò›…?÷:° '_2Çqá¼—"­'àþBj˜N!àÿx$Q yC¢Iÿª-0ãxä×b_Hëð°ëðß'ýéòWq× ëðÿ ÿ¶Ã×· Äïýâ½a Dã¿R>“S×ãå…š/ã¿? /Lÿã@˜(/êñïðýñøñëðUëðëðëðUëðÿòð2ëðPèóüúôñò (þàûð?"‘H$ÿ‰ @½^¯×ë‡u@ÜÿâùöñêñFêçôëðDëðTeêññòy hàûaêñhm/TëðßüÀtÀæõÅëðõ’ãø·ôUFY,‹Åb'@F#ÿ @FÇãñx÷<¾? ¿F …?B¡P(¤?2ž8UgÞýÿ¿Ïsóï$ô$|³›ìêñ‘2†Øð?êßëϱ‘óšÅmuó` æõþ‘4÷š-"u÷7)éÀðêñûï@L&“ɯd2¹?…-É–(¹?\.—ËåÒ¨(†. sUÎ!åÌ&!#â/z „ô"êñ5v$Þý¡³B9gì÷ËÈj?7É*ãøôPó ›4É©L¨4""´ÆôT8&þòð¸1m%3§Té_ÉÔ3&q„ãÐéòi!VÛqoÿ©ciqH '?<²PËBj(ÏWPMeôëð”Å|ßüMAMAç ÷¥^ðÀëð ²ëð ›4 0 ëðTÁ4Ïëðëðëð©ëô-!]4ëðªëðëðëð4ƒãø A ïHéòôAÉ@Tó Wm%T÷G V5TŒAEWãTž1EW„AüHÄ1EW°TÄAEWŒAüHÔAEW˜AüHäA WœAüHèA W AüHìA Wž1üH“1 W¨AüHMA W@¬AüH8! W°AüH!ZDÄ1üH"ZÏT#Z@¼AüH¡ WÀAüH%Z@ÄAüHü1 WÈAüH'Z@îñüH W-!üH)ZDÔAüH*Zg5T+ZDÜAüH,ZàAüH-ZDäAüH.ZèAüH/ZDìAüH0Z“1üH1V‡1ã<ï2@Ä1 AEQbïYYóNëð­OôSëðT‰DZ1V›4WëðXÁ4kYëðZÁD[ëð-\ÑDqëðrëð¿qË_ëð`ëðÜqs²ëðtëðùquëðv,ëðwëðxëð3ËyëðzëðP{YDmåeWŸiŸ{ŸŸŸŸ±ŸnDïèAȨã #&ãø©ãø‚ý°òð¥8&ë0×aÿ`UML-Dÿiagramm ÿ&einfüge¿n...þ AÉN£U¡3û`4  Backÿground Aÿdd-on`ÿ/CMD=201€7@þ QïaÂTï-!òñ„AB?ˆ¦¸÷8!ÈàUæõó¹ôÅæõEÒ7 ÷?@2™†!EÎ(X¶'?* 2m%¿q<ÉàAYèAÀ®¶ö ïÀÒäö™ãøP æõ A*ÄÐ5èóÁÉÅ!Î2Ç*Å¿<Ím%TËa¸vÆ3ÏÄ5dUÍnÌtÇC>Ǩ²çB4²½¨²ßT1÷\?n?€?lÅ3˜AÔAÔAÉÕ±QÓÙDÜAH" Q Qí²lE‰RIÍÔ°JÚÔÃRKçÔàRÚ9CÈ0áè¡Ð6>’0 &{4ÿ683D949-ÿ23C3-458ÿ2-A3DA-Bÿ9BAAFF08?9E9}þ{†€Ï³ñ™æõRµRµÉ¯áñ"A‰U.D!3k?ÿPêK¤WWP”˜Z QEeµÀA»¯Í¯ß»í­¡Ò´TeÀhÙ¹ï,¿×@Pÿê9Ž«@@lÁlÁæûà + 3ɼA”¿ j(Kq2€ ïëð€!ÉüïÄ1 b€‚*À@HâÉUäÞ/æ@Çq6¼?‹ð‹òòïÿÿó°Am%àáÒ°nÆktivitätÅ’÷óý¯º" ïý™èA Ô1Ñ8&Éý‘ÉÊQÑÆEQAQŒAÇìsAQÔAäïöïöGÃUñ¥Ê¿àÜ¿ ¿L"Yÿ>¸jµZß­V«@*Äè?WF«ÕÛÖ)ÅØëÆt»ƒ¤ÔA6„ýS>ÃŽ„¹ÎÃÃÿˆ 12äA²²ãB²²“1²³ˆ~&­·×ìA²³¦&ˆ…q²³º&ùqn#Î&’Èá ÕñpÕŽQñ™—@EAX5&ÿ8ÿò¼ôOUƒ _TI(_T š¡m3‡·ó»ãT¹Û«3è?íSÿÿAQi1@„EwpÕ^2@±QD‰RR)Dé¢EÑ”EQßܪàUxÙ†…ÓŽß¡ßåD1ÕëBìA‰RÃÕé¢u±u±åÒÐÕòÒÝÕýRêÕb÷ßü ïäCE7092ÿ4D-21DF-4CB6-BA6Pÿ6249119C031BFàQÃU¬PÝð?â˜ïPÿÿæŠþq…Oÿ¶²¡-+$öš¡Ta¹4ŽPÝ©Ta.9ù¹ü9åÑòÑ/!Ž7aÉÚÄõótÏ ß™*+Ü›(HÏeÀÿ*ÏÏoè™g¸Ï>gÎ/ZetcpÕ‚küëÏýÏÃíµ‚kAÈo,oDÅœå k⎠>gj/Zeÿtcé¥ k À@A²ÑšoïoLE +ÓxÿÿÁs9Ží‰¶Qcº/)ÕíÁš‚kêLJæoDÅ/#%ŸIŽHŸDÅRh SÐpM_Õ¦/)ÕꟓûQ¬‰é£p¾ŸÐŸÕ~/Œ)Õa¯ºÕÐñÇìHÖA Q’_ù©¢ ¾k÷<¶ã<¿¢ÿV½ÿÏó¶ÑIª›¯:åÑÆ¯Ò¯W{#?gÚ¦ø'¿´‰‰güý~¿ßïçÚ~xÈr¿„¿Æÿï/ÊZ\æ^/!nl2 Q­¿ÿð¬­òÑÜ¿è¿ú¿?01?È_BÏhAfÁÒ™fè¿ÕóÀ֌ϴÿ°ÏVàËÏâ©éÊÇQnNewëUÇìnA ïVO`w‰›­¥ß@ ~ÿ@ @@1™L&“Éâô+‘/(/:/… ïÐòtše+t&%†Ìö‰#†àöqÑn#ôö.†æ/,ï ?jß|ߎßR?&¢æAm?œ4}ñŠ?œ4œ1§?¨TsÚ? ë]OJ}ò,ER9OKO]OoOO“O¥O·OÉOàÛOíOÿO_#Z41DÿCA85B-3DÿD5-44C4-ÿB7AC-CA6ÿ372AAFA1!FTT4uc[TW_i_þ{^K0À:üÿH˜­×Ïéƒ+nTÑïãï}X©‘ä( ƒñN5¤&yd;1zeG?Y8ã;1C%v?ˆ?'¥?·>œQ!¤_¶_$?Ú_ì_þZ<~ÓåýSzoŒo̰ogŸÔo‹Ÿµe4u!Œ=tÏI¯Õ÷Õø ~Sl_~_)…w‰‚O­éNÐî_œ–1…©NJ_XC¨ºpoð»þŒÁø™—k5Ÿ _Éhôÿ…K+ƒbŸtŸ*˜ŸªŸ>_P_l‡àÿ]uÀS¥ìE,¯â܈Ñ !—¯¿°!‹é¼¯©ïà¯Ïgñï>Ï6¿H¿Z¿†Ï˜Ïÿð—ör“C€2{®Gáz„K! P°dÀ?ÒC$ÑöÀ’ŽÒ¿µWßÙëýŸPÏbÏRWŸiŸ{Ÿ¼ÏÎÏAïòÏ¥:!Ο¾ “3߯¯iß;¯yŸß±ßÃßÕßç߀ùß˯ï/ï¿»%úÈSyncediâ7rï„ä7ïÄÏ!¯”¿E¯øW¯üƒñÿ@@Ž=ñV«0ÿBÿTûƒ%¼©­*Ï<œ³ò†Õ†evev&ÌïfÖÖ!ïfêÖ"ÌïfþÖ'¯Væ1¯V&æ%â MÏD‹¿ß@¯¿ŒA‚ÿ5«C€ Á™ ±áD7…ÿ>ùVA"AÞá ±BTÞáe ±r„–¨ºÌÞ€ð//&/8/J/\+CÿE19FA90-÷B5Av BF7-ÿBCD4-00Dÿ1C97462Dà/Ÿ/±/Ã/Õ/PnøÿÈÍÉÑaC¹ ?šCdÊW6ø/ ??.?Í?ß?d?e·?š?›¾?¿Ñô?OO*Oÿÿÿ”SxOŠO·ÁHoZoß/~o½?¢o÷O={ÖßæeZ_B?$E_vàï]uÑ_Àw{”^–·_Cu¼OÿÿècJoÌCÀÒ?<|þßæe¿o†S7…UGœo:~êßE6!!@!~àt!¯É£ÍÏÇñÏ…ïß'ß!Ÿ3ŸEŸ§¯¹¯“ߟŸŸ±ŸÂäÞá/¿~òŸ¢w¿ÆØêüp¯‚¯ DVhܯî¯aÏ¿Âäù‘¼Ü}T¿óŸŠ¿)Ÿ—ïÀ¿Ò¿ä¿ö¿Ï€ϹŸ>ÏPÏïŸõ•|ÈDirtyÂÝ–ϨÄݳϱ¿®ÿŸ‚¯3ŸEŸúŒ7b÷?@y½^¯×ëPßbßû«q©š!¿`©Òˆlñã›Æã¯ÆãÃÆ,ìVׯÖÂ>ï;¿bïy¯ñ¿¯ªïûb@-Åïºä±âïºäüï'ÝÌß.ÿïÝÝ-±¬Ñ`ÿrú±ƒö¬Ñÿ¢ÿ´ÿÆÿØÿêÿüÿ 2ðDVhz 22DB·C42™0D”Cû5B48-13ÿ51F365FDƒ6E¬¾ÐâôÿkŽ¡€@‹ߟīže(:Lëý‚Õ¸LÜp‚/$/6/H/Z/l/ÔO/YO´/ÙOØ/.o'õ”_P?œú4×Ï5ø03y?‹?¯?Á?DV÷7›Ï5²/03¨õ[IO•/ö8ÃÏ %)?ýSÒOäO”_`?,_„?hmå5¢_Û Veé>o O'zoŒoN/°l¯Ï %¬OýSñe5‰O®hÒÞñl΀Žñœ¹‘º¿µÞ¿6ßÏÏ!3•Ÿ§Ÿ€Ï{ŸsÔa¯7_à[_e¯_‘_£_µ_^ŸpŸøOý_o!oÊŸÜŸO¿¯sÔ¬Ñuoð:]B¯¬o¾ox¯âoHß®¯À¯Ò¯ä¯ö¯¿r,¿>¿¨ü’µj¸Zombie Newz²–±ƒ¿•´–± ¿oŸ_ïÈo;ìoþoúÀ\ @@@ @@ÿ–L&“Éd@ÿ@C©ä8BìèXý“ÜXÏjÏ|ÏŽÊÆFMÆF&ÇFm¦•ñ ÊC¦/ÇF•¦”¢ïÏ€ôß2¯ŸV[ßèB$sßh×ßh×­ßÏT“Œ¿áß­»‘0±M±j±ï#êM±4æj±AïSïeïwï‰ï›ï­ï¿ïÑïãïõïøÿÿ+ûD4C60474-36CEðß6DC-B;ð-Dÿ948CE4B33F3]ÿoÿÿ“ÿ¥ÿÿT›S/ w¡Mÿ»ÕÓtë1²ÇÿÙÿëÿýÿœ®3ÔÓQÛß^ÿßï”5ï¸@ÊÜî$ŒÈT¬?^.ã¢m¯“¯S1*ò0ÿBÿS5pòVˆNO¬’õýœÒ»ÿ*,§: _g+ÕûÌÿÿhüä%’õò¾«ª1åj|h[5oD™÷t?øÿ¤Nb¨ÀLb¨š?˜þ½?þÿá<üO$Eáý–=O¾¤E¯¾E/U3gE‚Xô§);åãÞL@Jdf/x/2³0±æ¡LF—ÑZ™J_xŸ½ç`‰˜Ÿð¯¼ŸÎŸÄ_úWüŸˆ?@µf(o:oLo^llà„ÏáîÃUWÅ ÙÏáÙ·ïÉã‰ú®ê±ŠøØÓ#˜ø!Ç‹ÍÈÓ#Qš7¥KÓµ»N¯­Ca5»EšüùEfÕ”O"Æ'Am5B39BM?¿_ÁrOQfˆOW(¯OÛ÷(áM±Ö‡`ì-šo?:?a_^?p?*«/¸?Ê?Ü?„– Ÿº@2³Ë±æ¡áÞtÑë.û-oà§€e-_oo_c~8¥ˆ†ÚW›…ÕoÇÀùoëý/}˱#ŸSyncedJV/OAOÞeOwO‰O³_Å_¿I>œÚO@ŒûO°Z›(_8žGGÄ­Áú\‡³JÅ1‘†1o€)ÇMoÄqoƒo~8Ëÿÿ@@z½^¯¯×ë@÷è­ØDä÷:Šãjñ3[tÕÑS%9¶5&5&3!_ö-¶"_öA¶ÁU!bóU¶T²U (‹ûÔ¬ÀeŸxP¯ÐñŒƒ1¨ÇÁœ`˜ÑáÛ³ûÕé´dÁô·ÐÅé´ô³‘ÓÉ'”àé‘JÏ«£¯†¯ÿÿé‘˜Ñ ÁÉ‚t¸oÓ A‘‘ ÁR°ÄgŠÔæ#“ÖÖÏ:§Á4¥†MÓM!¥h‚Ô… B Á ÁYâÔÑÔÑo¢;Ô–88Ù:$:PIbÔ<¢JoÔÖ‰K|ÔM"´Êø AÐèñýƒ$Ë]³`&ÿ{84BDD06ÿF-6FFF-4ÿE6E-9620ÿ-B4FA838ÿ5EF50}þDŒÑYåÓEæ¨ÒŒÑ¬PêÛð?úÓB“þœÃ\UŒÿG‘°ØpÑfÉãnÌäL¡4Öâ.á_©L¡o©ø‰©àÈ1Ñœ`Ë@›åñÖ›„ˆáœ…”ãQ›ïáLjáÍõÃïÕït¸òïþqMµ±@ı±U!_©µ±µ¹øæƒP<¡ÖM!! 4¥U$º­µÇœ…Þ¨gÄ¥q /U¿-%3G#êå³û¯¦ ÿæõwµ³þ »o»/¥óo¥öûÈÂÿÔÿ€'-¿-%0G#•öûâ'A¿-%§G#ì…þöþ@ Ñ4 ¿G/Cêñå³ûV¶ù—<¥mj,ŽæõzÒ˜/?ñ¼/æõ‡Ò­ CxÁÓ¥ó£ñÝìƒ2?`D?+¿0%¡?@Á±±hú^¥ ¥èmÏ(¯š‡D¯°¿h¯z¯úx4¥Ñ€¯¨€O:çoLç£ËC#þ™c2{®Gáz1„˜ÑÀ@÷$À?ߡߤ@Á Áö_é?o O>o1OCOUOgO˜oªov?¯OÁOÓO:Lt»Á'_éÐì=|^_p_¶”_Æ¿ì´ü †’F†1oL*opO”¥·€D/irtyOòƒT@Á0õ”†Qߣ·oÿÿça {©U•q~YQïcïtÿzù`ÍQ ƒÁÿ @Fú|>ŸÿÏg@A¡À#o“é»û£GA£º‚ýë£GPÏîoq•{ýã˜Ñˆ-ìaÛuö``u#À`äbu ól£ðú„€‚p@þÁ3˜ñˆ€‘é‘Sö‚ñ‚ü9“a’’J”.áñq¶Wbìauß `®äà?g“n–¦x’uç`‚™ÆxZz „7óú?F¨ß`@A õ@ÿ@FÛív»Ýn@êbl˜m—n–ì“ðr÷Ÿ ª“— ¯ÒRí’;¯M©)ìbpça¸‹ñàƒ§€£ð@z3‘}¢ƒ¯•¯ì‚!pá֤է¯Õ¯ç®i˜Óˆz}r.–QK¶è^¢"#K¶éOU0zœþJ‰F î`”²l@’tçgï²öovQ‰Å¾–K`ŠÆx*Ê\‰Fex’~™DÐþ‹Ôh` Å˃¿ˆ#Ïv!þ"áüvU.äÀÛùÜÿõNžùãü@daâÙÕxtimer Oò5Õù /ASew‰›‰È“ÿ÷?F‹Åb±ÿX@Fàï÷û§ý~ï£GˆÔßÒˆ?-DTû!ùäöŸŸ,Ÿ>ŸPŸb™ÁÁrŸ@„>柯ŸpÝÑ•FwÖ? …B¡Pé—>æ=ç>æ¤ÃïÕîcçðïV¦ ÿùϤ­µµ¡Töë¯Sÿeÿ~§~qµ¥ªò’ÿ«£ªÿ¿'¿9¿ÁÅ7ó,# ÀÆs¿…¿®UÒТ°`¦¿¸¿Ê¿Ü¿î¿Ît6WÏ,Ï>͈ßórÏ„ÄÿR¾ûØ\FÿŽÿÌI&ãŠø›É„Ã×vinsert»ÃÂÏÔÏæÏøÏ ß`ß.ß@ßRßdßA8É•ÿr¹\.—Ë@ÿAX*•J¥RñÀˆZ#áÒˆ®ßÀßÒßäßößïï,ï’Bï„Ð6mïïÁ'³â@þG'½^¯×ëõê–676¤”?¦>47Á?V¦Ü?î9Ϥça˜ ´¦)B»ÿ$O6O~£§Á±¥wFcOvOàÿòÿ06ìFëG‘ASe =y‹¯ÁÏå€÷ ¯QZ.Ä_B„Ä@ÿW‹õñuO©f›|úük~×vdelete»Ã“¥·ÉÛíÿ/#/5/G$ôO/a/s/…/—/©/»/Í/ß/ñ/b™ca?„àv>?P?Ášp r<p„=àvßwàv¤ewއ’V¦­¿‰pHéa¨Eú‚ŒOžCúﬢyp´¦L’4ŸLCLŸ¶OÈOÚOcebfЂ½–r¿%_–¿I_[_m__‘_£_µ_Ç_Ù_ë_‘¯o€Èÿîi”ÑÅ›ýBÿŠÄÝ´ª,¢K¯P¯b¯t¯†¯˜¯ª¯PòÃÈøfÿä¯T:¼ö^ÚÿšD²ÉœÿÈÇ€P¶ ¹¬A¸timer]c°ñ8¿J¿\¿€n¿€¿’¿¤¿¶¿È¿ }L¿¿¡2ñ¸~ý¾{Ï!ÃV9ÏKÏ]ÏoÏÏ“Ï9°ñµÏ%=øàÏòÏA#@3ý†·ÈÈ$×Aþ#¿@FÍÔŒ5€‚„/.¦3/Ȇ€N/`)A”du•—&.ï–/¨/qCBá Gèí"Õ/è/Sïeïwã^6 ]7_Æï6]`éïûï ÿÿ1ÿCÿUÿ¤ÿyÿDðÞ˜ÿªÿ¼ÿ8ÈøšÿDAN¦«ÑÏflŒÞÿðÿ!);M_q ƒ•§ì"vT,~œSCyP'9K]oÔA…ö}Qf°Â‚ƒ](D…íUQfPgQf‚„ÖoènvgȆ@0yA”Ð!u•gvþ/f@xðƒ4a#•¹v¥¸#?5?G?rf¦ã.†-‡ÓF„?–?Øí¸?Ê?Ü?î?OO$OsOHOí…œ^Ÿ…OþÇôð#nì>J€ÿC¬™+I*¦Ž ¿¿‡ûQØOêOüO_ _2_D_V_h_z_Ó?‘_£_µ_Ç_Ù_ë_ý_o!o3o¥ÉÌ¡UoÇÍ"¶€o’oT­´k`¡Æm"¶!·"¶SÔ§¿¹¾G·Ô¿™Öï¿ɲxÅ(<ÂÎàs<Ïï›rôÕŠÆvÏŽs ŽÏø ¥•¤–¦ãÿƵïg©ŸûÏg;ðœÐ©ZWe@u§šBð†¨?“11@.hÿzÿŒÿžÿ°ÿÂÿÔÿDjôa÷ÿàgmÃF"4F @@FÛív»Ýn\`¡h ÃFÂGÃFÄ´H_Z^èGu_ Æ_¢Y`xeÅ@ÙVpŽsÝ_Hƒ‰/bo‚/oš¬¾F5¼S!Ó fE6ö/xÜ!./@/R/d/v/ˆ/š/¬/€¾/cq CHx÷/g”èÿ½™½ ~IGl´yˆæ5sOï^–updanå%G?Y?k?}??¡?³?Å?×?üé?+óÐ@FªÕÿjµZ-@Ag¿çäbHeúà‚÷†¨ŽÎG3$Ñï9OKO]OoOO“O¥O¹ÇO8½”–òO_úZ T ÿ@FÑ?h4Fž¶”–“—”–Ä´¯+®¹—F¯ Æa¯s©TIqŒ®¢@o‚®¯wÏlÂfȲè¯û¯eowo‰oµ–Óq¶„¢Ôq¶&ßÙoëoýo!3EWi{3ÂàñƒåˆIÏÇÙtïÿÌ;ND¶w”¹iØðaïÊÀ&*§$ÿ,aÌ¡½:oLo€^o//)/ÃM/ŽÝÊét:Nç?w'H­V«Õjåˆ(á“Õš/¬/¾/Ð/â/ô/`??*?ןÏqX¶ñÕ€˜ûºUx¶ô_Éó|¿áÿpÌøÊ¶¶¿É¿o+o=oOb6¡lc?Æ>Ç5¢‹¼Ÿo±o'·FQÑoãoõo`ó± 1CU|6¡u‡‚Ý6¡­ÿPO9"pašÿ@©ì Ô¹ÓVÐâô*<N¾qƒ•§¹Ë€Ý%Ÿ7Ÿ^4BÿA31C633-ÿ1171-473ÿ2-AD74-Fÿ6CDDCA4ED6EtŸ†Ÿ˜ŸªŸ¼ŸÎŸàŸòŸ¯Òÿ=K\1:¯aD\1W¯S{¯¯Ÿ¯±¯øÃ¯Õ¯üJÄb±X,ÿ@F§Óétÿ:@FõofxÀŠœ®ÀÒäŸÏÿçóù|@FŸ·°I&fˆÛdéþ¦ÈO×Þ- X£! 2jµh±G YsaNvÊM_¯êÓ§÷ /0Ì a.#†††Q/ @îlu/‡/ýgDª/¼/ãÎsaã/õ/??+?R× aàK?]?X aƒ?PžÅÿm_Ò—L§?\%f^¦?¸?Ê?Ü?î?OO$OÜnGOYOkO}OO¡O³OÅO×OðéOûO _T293DßBA9C-°8-ÿ490B-818ÿ6-D8294B?200C53J_\_n_€_’_¤_¶_È_Ú_¨¿U§MUºéòëð ëð ëðUëðëðëðëðUëðëðFëðGëðUHëðIëðJëðKëðULëðMëðNëðOëðUPëðQëðRëðSëðUTëðUëðVëðWëðUXëðYëðZëð[ëðU\ëð_ëð`ëðqëðUrëðsëðtëðuëðUvëðwëðxëðyëðzëð{ëð|Üð¥tëð4ïôêñ.æõYÿ,‹Åb'@"‘H$‰ @ãøéêñ ëð¼ã¿ys™ëðCþ-dõât !AéòÜð@ÐátIRHëð<Üÿ( EtÐá‹t R\”3ÓQA2ÓÉ\rªhs@?ÄìÑ]t.PD íјt.PU1ëðÿòðëð(èóëð ÜÿöñöñæõUëðëðO"D&aU=èóòñQÿÿJf àû)êñh"Tëðy‡êñ5¼ãø+U-Þ¾¨ É…"&Q--ëð,H*9(TY€öñg©Œž°éò› ãøEQîð/,õ%çôõðUML ÿBackgrouÿnd Add-oÿn/CMD=1Õ%ëðëðñðUMÿL Backgrÿound Addÿ-on/CMD=2 :/L²ÔÕu(C/|²Ôýu+C/üëÖa/ðÖa/<êÖa/|óÖa/ôöÖa/´øÖa/túÖa/4üÖa/ôýÖa/l×a/ì×a/¬×a/l×a/, ×a õëðëðFührÿungslinieGuideDasDokTheDocõëðëðKontÿrollflussUMLõëðëðGestÿure FormatõëðëðGestÿik-FormatõëðëðStatÿic Structure-1õëðëðVisiÿo Extendÿed DataõëðëðAnfaÿngszustandõëðëðUMLDÿiagramTypeUML NormalVerbinderConnectorSchwarzBlack fillWeiße LinieWhite lineõëðëðGeneÿral.-pfeilõëðëðGeneÿralizatiÿon ArrowõëðëðDynaÿmischer ÿVerbinderõëðëðDynaÿmic Conn?ectorUMLShapeTypeõëðëðUMLOÿbjectGUIDUMLErrorBeginAngleEndAngleõëðëðUMLPÿresOption1õëðëðUMLSÿuppressO?ptionNavigabilityUMLVersionDX1DY1DX3DY3end1_nameend1_mpend2_nameend2_mpNavigierbarNavigableGemeinsamSharedõëðëðZusaÿmmengesetztCompositeõëðëðZusaÿmmenges.ÿ navigierbarõëðëðCompÿosite NaÿvigableõëðëðGemeÿins. navÿigierbarõëðëðSharÿed NavigableõëðëðQualÿifizierungQualifierõëðëðGemeÿinsame Qÿualifiz.õëðëðSharÿed QualifierõëðëðZusaÿmmenges.ÿ Qualifiz.õëðëðCompÿosite QuÿalifierõëðëðZusg¿. naviùðQÿualifizierg.õ"ëðëðCompÿosite Naÿvigable ÿQualifierõëðëðGem.ÿ navig. ÿQualifiz?ierg.õëðëðSharÿed Navigÿable QualifierõëðëðNaviÿgierb. Qualifizûðg.õëðëðNaviÿgable QuÿalifierõëðëðProzÿeduraufrufõëðëðProcÿedure CallõëðëðAsynÿchroner ?FlussõëðëðAsynÿchronous? FlowõëðëðFlacÿher FlussFlat FlowõëðëðTeilÿ-Shape_rÿealisierenõëðëðrealÿ_subshapeõëðëðWateÿrmark TitleõëðëðInitÿial StateõëðëðUMLPÿresOptionsõëðëðvisDÿescriptionvisKeywordsvisVersionUMLPageGUIDAktivität-1ZustandStateõëðëðUMLAÿutoLockTÿextEditTransitionsNameZustand.7State.7Zustand.11State.11Zustand.15State.15Zustand.19State.19Zustand.23State.23Control FlowõëðëðUMLPÿresOption2ObjektflussObject FlowõëðëðAssoÿziat.-rolle õëðëðAssoÿciation ?Role MessageCountCosAngleSinAngleBehaviorM1M2M3M4M5M6M7M8E2E1EndzustandFinal StateõëðëðObjeÿktfluss.45õëðëðObjeÿct Flow.45õëðëðObjeÿktfluss.46õëðëðObjeÿct Flow.46õëðëðObjeÿktfluss.47õëðëðObjeÿct Flow.47õëðëðObjeÿktfluss.48õëðëðObjeÿct Flow.48õëðëðObjeÿktfluss.49õëðëðObjeÿct Flow.49² ÿÿÿÿ}}3ÜzÒ¨wG3ôOÒ¿w E3ÀÒÉw E3œòÒÔw E3ÓßwG3\ ÓöwE3ÄÓþwG3ÄÓxG3<)Ó-xG3\)ÓIxG3|)ÓfxG3”)Ó~xG3¬)Ó–xE3Ä)Ó¥xE3Ü)Ó³xE3DÓÁx E3ô)ÓÍxE3 *ÓÜxE3$*ÓìxE3<*ÓûxG3T*ÓyG3t*Ó2y G3”*ÓRyG3´*ÓmyE3Ì*Ó~yG3ä*Ó•y E3ü*Ó¢yE3+Ó±y E3,+Ó¾yG3D+ÓÖyG3d+ÓñyE3|+ÓzE3DÓzE3”+ÓzE3¤+Ó!zE3´+Ó)zE3Ä+Ó1zE3Ü+Ó?z E3ì+ÓKzE3,ÓYz E3,ÓezE3,,ÓuzE3D,ÓƒzE3\,Ó‘z E3l,ÓœzG3„,ÓµzE3œ,ÓÃz#G3Ä,ÓæzG3ä,Ó{G3-Ó"{G3$-Ó<{G3<-ÓT{E3T-Ób{G3t-Ó{G3”-Ó›{!G3´-Ó¼{G3Ô-ÓÙ{$G3ü-Óý{)G3$.Ó&|$G3L.ÓJ|%G3t.Óo|"G3œ.Ó‘|G3¼.Ó®|G3Ô.ÓÆ|G3ì.ÓÞ|G3 /Óù|G3,/Ó}G3D/Ó+}E3\/Ó9}!G3|/ÓZ}G3”/Óq}G3¬/ÓŠ}G3Ä/Ó¡}G3Ü/Ó¹}G30ÓÑ}E30Óá}E340Óð}E3L0Ó~E3ô/Ó~ E3d0Ó~ E3t0Ó&~G3”0ÓC~E3¬0ÓS~ E3¼0Ó\~E3Ô0Ój~ E3ä0Óv~E3ü0Ó…~ E31Ó’~E3,1Ó¡~ E3D1Ó®~E3\1Ó½~ E3t1ÓÊ~E3Œ1ÓÙ~ E3¤1Óæ~E3¼1Ó÷~G3Ô1ÓE3ì1ÓE32Ó/G3$2ÓIG3D2ÓdE3\2Óu E3t2Ó‚ E3Œ2Ó E3¤2ÓœE3´2Ó£E3Ä2ÓªE3Ô2Ó±E3ä2Ó¸E3ô2Ó¿E33ÓÆE33ÓÍE3$3ÓÔE343ÓÛE3,káâE3DkáñE3ôaã€G3 bã€G3Œ7Ñ1€G3|™ÑI€G34Òa€G3LÒy€G3dzÒ‘€G3{Ò©€G3 }âÁ€G3ŒâÙ€G  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|U&åöëð ëð ëðUëðëðëðëðÜð¥tëð4ïôêñ ëðþêñY,‹Åb'ÿ@"‘H$‰ ¥@ãøêñ ëðTÿQãÏ‹úëðC-?ãí 7ëðAéòUéòëðëðëðÜð%tëð4ïôêñÜÿÝþéêñ ëð\ùâ¿?ŒëðCž-õâP7"AéòJ@d>ãó‹LR@üþâRŒ6RHëð<Üÿ( Hëð<Üÿ( JE¬Lª² REDÿâ¿ R{Äëð¸óðœæõ÷óëð *ëð ëðgëð"Üÿ4ðFXßü÷ø ñ(†hèó(ט¯ÿÛwâ@(-îïñCv­5üyí´§ˆŒ÷5ßüWxåÒåöçôëð_„ìÒ:ëðTëðWBçôŽëð éòê ®ëðœ)ªsÎãëðÒ&ï¯2ÕFóð!¾ëð *ªgóðÿõ>ëðÀÝ3ßfÁëðF}ëð™#3'Õsëð$ÿœ9w±óðR$çôwM;w=U/7ài¥PMêñé…¥)ëñ²Ôß·@šëðRý'ëð\îÒÆtWh»1ëðìé_.u§=.ëðÿt«Ô(vÝ€óðP2ëðE·Òñ Þ ?þëðÔшŒë*"DÑkÌ6%ÿ-!8ªåö`kamailio-4.0.4/obsolete/usrloc/doc/Makefile0000644000000000000000000000012712223032460017325 0ustar rootrootdocs = usrloc.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/usrloc/doc/params.xml0000644000000000000000000001712112223032460017674 0ustar rootroot
Parameters
<varname>user_column</varname> (string) Name of column containing usernames. Default value is "username". Set <varname>user_column</varname> parameter ... modparam("usrloc", "user_column", "username") ...
<varname>contact_column</varname> (string) Name of column containing contacts. Default value is "contact". Set <varname>contact_column</varname> parameter ... modparam("usrloc", "contact_column", "contact") ...
<varname>expires_column</varname> (string) Name of column containing expires value. Default value is "expires". Set <varname>expires_column</varname> parameter ... modparam("usrloc", "expires_column", "expires") ...
<varname>q_column</varname> (string) Name of column containing q values. Default value is "q". Set <varname>q_column</varname> parameter ... modparam("usrloc", "q_column", "q") ...
<varname>callid_column</varname> (string) Name of column containing callid values. Default value is "callid". Set <varname>callid_column</varname> parameter ... modparam("usrloc", "callid_column", "callid") ...
<varname>cseq_column</varname> (string) Name of column containing cseq numbers. Default value is "cseq". Set <varname>cseq_column</varname> parameter ... modparam("usrloc", "cseq_column", "cseq") ...
<varname>method_column</varname> (string) Name of column containing supported methods. Default value is "method". Set <varname>method_column</varname> parameter ... modparam("usrloc", "method_column", "method") ...
<varname>user_agent_column</varname> (string) Name of column containing user-agent values. Default value is "user_agent". Set <varname>user_agent_column</varname> parameter ... modparam("usrloc", "user_agent_column", "user_agent") ...
<varname>received_column</varname> (string) Name of column containing the source IP, port, and protocol from the REGISTER message. Default value is "received". Set <varname>received_column</varname> parameter ... modparam("usrloc", "received_column", "received") ...
<varname>db_url</varname> (string) URL of the database that should be used. Default value is "mysql://ser:heslo@localhost/ser". Set <varname>db_url</varname> parameter ... modparam("usrloc", "db_url", "mysql://username:password@localhost/ser") ...
<varname>timer_interval</varname> (integer) Number of seconds between two timer runs. The module uses timer to delete expired contacts, synchronize with database and other tasks, that need to be run periodically. Default value is 60. Set <varname>timer_interval</varname> parameter ... modparam("usrloc", "timer_interval", 120) ...
<varname>db_mode</varname> (integer) The usrloc module can utilize database for persistent contact storage. If you use database, your contacts will survive machine restarts or SW crashes. The disadvantage is that accessing database can be very time consuming. Therefore, usrloc module implements four database accessing modes: 0 - This disables database completely. Only memory will be used. Contacts will not survive restart. Use this value if you need a really fast usrloc and contact persistence is not necessary or is provided by other means. 1 - Write-Through scheme. All changes to usrloc are immediately reflected in database too. This is very slow, but very reliable. Use this scheme if speed is not your priority but need to make sure that no registered contacts will be lost during crash or reboot. 2 - Write-Back scheme. This is a combination of previous two schemes. All changes are made to memory and database synchronization is done in the timer. The timer deletes all expired contacts and flushes all modified or new contacts to database. Use this scheme if you encounter high-load peaks and want them to process as fast as possible. The mode will not help at all if the load is high all the time. Also, latency of this mode is much lower than latency of mode 1, but slightly higher than latency of mode 0. 3 - Read-Only scheme. Disables commits to database. Upon start contacts are read from the database but changes are not written. Contacts will thus not survive a restart unless taken care of otherwise. Use this if you need the benefits of mode 0 with a databases backed preloading feature. Attention: There is a slight window where updated contacts might not make it into memory while reading and parsing contacts from the database before going into full production mode. In case of crash or restart contacts that are in memory only and haven't been flushed yet will get lost. If you want minimize the risk, use shorter timer interval. Default value is 0. Set <varname>db_mode</varname> parameter ... modparam("usrloc", "db_mode", 2) ...
kamailio-4.0.4/obsolete/usrloc/hslot.h0000644000000000000000000000345412223032460016430 0ustar rootroot/* * $Id$ * * Usrloc hash table collision slot * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef HSLOT_H #define HSLOT_H #include "udomain.h" #include "urecord.h" struct udomain; struct urecord; typedef struct hslot { int n; /* Number of elements in the collision slot */ struct urecord* first; /* First element in the list */ struct urecord* last; /* Last element in the list */ struct udomain* d; /* Domain we belong to */ } hslot_t; /* * Initialize slot structure */ int init_slot(struct udomain* _d, hslot_t* _s); /* * Deinitialize given slot structure */ void deinit_slot(hslot_t* _s); /* * Add an element to slot linked list */ void slot_add(hslot_t* _s, struct urecord* _r); /* * Remove an element from slot linked list */ void slot_rem(hslot_t* _s, struct urecord* _r); #endif /* HSLOT_H */ kamailio-4.0.4/obsolete/usrloc/utime.h0000644000000000000000000000230612223032460016415 0ustar rootroot/* * $Id$ * * Usrloc time related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef UTIME_H #define UTIME_H #include extern time_t act_time; /* * Get actual time */ void get_act_time(void); #endif /* UTIME_H */ kamailio-4.0.4/obsolete/usrloc/README0000644000000000000000000002766312223032460016016 0ustar rootroot1. Usrloc Module Jan Janak FhG FOKUS Copyright © 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. Dependencies 1.3. Parameters 1.3.1. user_column (string) 1.3.2. contact_column (string) 1.3.3. expires_column (string) 1.3.4. q_column (string) 1.3.5. callid_column (string) 1.3.6. cseq_column (string) 1.3.7. method_column (string) 1.3.8. user_agent_column (string) 1.3.9. received_column (string) 1.3.10. db_url (string) 1.3.11. timer_interval (integer) 1.3.12. db_mode (integer) 1.4. Usrloc Module API 1.4.1. ul_register_domain(name) 1.4.2. ul_insert_urecord(domain, aor, rec) 1.4.3. ul_delete_urecord(domain, aor) 1.4.4. ul_get_urecord(domain, aor) 1.4.5. ul_lock_udomain(domain) 1.4.6. ul_unlock_udomain(domain) 1.4.7. ul_release_urecord(record) 1.4.8. ul_insert_ucontact(record, contact, expires, q, callid, cseq, flags, cont, ua) 1.4.9. ul_delete_ucontact(record, contact) 1.4.10. ul_get_ucontact(record, contact) 1.4.11. ul_get_all_ucontacts(buf, len, flags) 1.4.12. ul_update_ucontact(contact, expires, q, callid, cseq, set, res, ua) 1.1. Overview User location module. The module keeps a user location table and provides access to the table to other modules. The module exports no functions that could be used directly from scripts. 1.2. Dependencies The following modules must be loaded before this module: * Optionally a database module. 1.3. Parameters 1.3.1. user_column (string) Name of column containing usernames. Default value is "username". Example 1. Set user_column parameter ... modparam("usrloc", "user_column", "username") ... 1.3.2. contact_column (string) Name of column containing contacts. Default value is "contact". Example 2. Set contact_column parameter ... modparam("usrloc", "contact_column", "contact") ... 1.3.3. expires_column (string) Name of column containing expires value. Default value is "expires". Example 3. Set expires_column parameter ... modparam("usrloc", "expires_column", "expires") ... 1.3.4. q_column (string) Name of column containing q values. Default value is "q". Example 4. Set q_column parameter ... modparam("usrloc", "q_column", "q") ... 1.3.5. callid_column (string) Name of column containing callid values. Default value is "callid". Example 5. Set callid_column parameter ... modparam("usrloc", "callid_column", "callid") ... 1.3.6. cseq_column (string) Name of column containing cseq numbers. Default value is "cseq". Example 6. Set cseq_column parameter ... modparam("usrloc", "cseq_column", "cseq") ... 1.3.7. method_column (string) Name of column containing supported methods. Default value is "method". Example 7. Set method_column parameter ... modparam("usrloc", "method_column", "method") ... 1.3.8. user_agent_column (string) Name of column containing user-agent values. Default value is "user_agent". Example 8. Set user_agent_column parameter ... modparam("usrloc", "user_agent_column", "user_agent") ... 1.3.9. received_column (string) Name of column containing the source IP, port, and protocol from the REGISTER message. Default value is "received". Example 9. Set received_column parameter ... modparam("usrloc", "received_column", "received") ... 1.3.10. db_url (string) URL of the database that should be used. Default value is "mysql://ser:heslo@localhost/ser". Example 10. Set db_url parameter ... modparam("usrloc", "db_url", "mysql://username:password@localhost/ser") ... 1.3.11. timer_interval (integer) Number of seconds between two timer runs. The module uses timer to delete expired contacts, synchronize with database and other tasks, that need to be run periodically. Default value is 60. Example 11. Set timer_interval parameter ... modparam("usrloc", "timer_interval", 120) ... 1.3.12. db_mode (integer) The usrloc module can utilize database for persistent contact storage. If you use database, your contacts will survive machine restarts or SW crashes. The disadvantage is that accessing database can be very time consuming. Therefore, usrloc module implements four database accessing modes: * 0 - This disables database completely. Only memory will be used. Contacts will not survive restart. Use this value if you need a really fast usrloc and contact persistence is not necessary or is provided by other means. * 1 - Write-Through scheme. All changes to usrloc are immediately reflected in database too. This is very slow, but very reliable. Use this scheme if speed is not your priority but need to make sure that no registered contacts will be lost during crash or reboot. * 2 - Write-Back scheme. This is a combination of previous two schemes. All changes are made to memory and database synchronization is done in the timer. The timer deletes all expired contacts and flushes all modified or new contacts to database. Use this scheme if you encounter high-load peaks and want them to process as fast as possible. The mode will not help at all if the load is high all the time. Also, latency of this mode is much lower than latency of mode 1, but slightly higher than latency of mode 0. * 3 - Read-Only scheme. Disables commits to database. Upon start contacts are read from the database but changes are not written. Contacts will thus not survive a restart unless taken care of otherwise. Use this if you need the benefits of mode 0 with a databases backed preloading feature. Attention: There is a slight window where updated contacts might not make it into memory while reading and parsing contacts from the database before going into full production mode. Warning In case of crash or restart contacts that are in memory only and haven't been flushed yet will get lost. If you want minimize the risk, use shorter timer interval. Default value is 0. Example 12. Set db_mode parameter ... modparam("usrloc", "db_mode", 2) ... 1.4. Usrloc Module API 1.4.1. ul_register_domain(name) The function registers a new domain. Domain is just another name for table used in registrar. The function is called from fixups in registrar. It gets name of the domain as a parameter and returns pointer to a new domain structure. The fixup than 'fixes' the parameter in registrar so that it will pass the pointer instead of the name every time save() or lookup() is called. Some usrloc functions get the pointer as parameter when called. For more details see implementation of save function in registrar. Meaning of the parameters is as follows: * const char* name - Name of the domain (also called table) to be registered. 1.4.2. ul_insert_urecord(domain, aor, rec) The function creates a new record structure and inserts it in the specified domain. The record is structure that contains all the contacts for belonging to the specified username. Meaning of the parameters is as follows: * udomain_t* domain - Pointer to domain returned by ul_register_udomain. * str* aor - Address of Record (aka username) of the new record (at this time the record will contain no contacts yet). * urecord_t** rec - The newly created record structure. 1.4.3. ul_delete_urecord(domain, aor) The function deletes all the contacts bound with the given Address Of Record. Meaning of the parameters is as follows: * udomain_t* domain - Pointer to domain returned by ul_register_udomain. * str* aor - Address of record (aka username) of the record, that should be deleted. 1.4.4. ul_get_urecord(domain, aor) The function returns pointer to record with given Address of Record. Meaning of the parameters is as follows: * udomain_t* domain - Pointer to domain returned by ul_register_udomain. * str* aor - Address of Record of request record. 1.4.5. ul_lock_udomain(domain) The function lock the specified domain, it means, that no other processes will be able to access during the time. This prevents race conditions. Scope of the lock is the specified domain, that means, that multiple domain can be accessed simultaneously, they don't block each other. Meaning of the parameters is as follows: * udomain_t* domain - Domain to be locked. 1.4.6. ul_unlock_udomain(domain) Unlock the specified domain previously locked by ul_lock_udomain. Meaning of the parameters is as follows: * udomain_t* domain - Domain to be unlocked. 1.4.7. ul_release_urecord(record) Do some sanity checks - if all contacts have been removed, delete the entire record structure. Meaning of the parameters is as follows: * urecord_t* record - Record to be released. 1.4.8. ul_insert_ucontact(record, contact, expires, q, callid, cseq, flags, cont, ua) The function inserts a new contact in the given record with specified parameters. Meaning of the parameters is as follows: * urecord_t* record - Record in which the contact should be inserted. * str* contact - Contact URI. * time_t expires - Expires of the contact in absolute value. * float q - q value of the contact. * str* callid - Call-ID of the REGISTER message that contained the contact. * int cseq - CSeq of the REGISTER message that contained the contact. * unsigned int flags - Flags to be set. * ucontact_t* cont - Pointer to newly created structure. * str* ua - User-Agent of the REGISTER message that contained the contact. 1.4.9. ul_delete_ucontact(record, contact) The function deletes given contact from record. Meaning of the parameters is as follows: * urecord_t* record - Record from which the contact should be removed. * ucontact_t* contact - Contact to be deleted. 1.4.10. ul_get_ucontact(record, contact) The function tries to find contact with given Contact URI and returns pointer to structure representing the contact. Meaning of the parameters is as follows: * urecord_t* record - Record to be searched for the contact. * str_t* contact - URI of the request contact. 1.4.11. ul_get_all_ucontacts(buf, len, flags) The function retrieves all contacts of all registered users and returns them in the caller-supplied buffer. If the buffer is too small, the function returns positive value indicating how much additional space would be necessary to accommodate all of them. Please note that the positive return value should be used only as a "hint", as there is no guarantee that during the time between two subsequent calls number of registered contacts will remain the same. If flag parameter is set to non-zero value then only contacts that have the specified flags set will be returned. It is, for example, possible to list only contacts that are behind NAT. Meaning of the parameters is as follows: * void* buf - Buffer for returning contacts. * int len - Length of the buffer. * unsigned int flags - Flags that must be set. 1.4.12. ul_update_ucontact(contact, expires, q, callid, cseq, set, res, ua) The function updates contact with new values. Meaning of the parameters is as follows: * ucontact_t* contact - Contact URI. * time_t expires - Expires of the contact in absolute value. * float q - q value of the contact. * str* callid - Call-ID of the REGISTER message that contained the contact. * int cseq - CSeq of the REGISTER message that contained the contact. * unsigned int set - OR value of flags to be set. * unsigned int res - OR value of flags to be reset. * str* ua - User-Agent of the REGISTER message that contained the contact. kamailio-4.0.4/obsolete/usrloc/utime.c0000644000000000000000000000224712223032460016414 0ustar rootroot/* * $Id$ * * Usrloc time related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "utime.h" #include "../../ser_time.h" time_t act_time; void get_act_time(void) { act_time = ser_time(0); } kamailio-4.0.4/obsolete/usrloc/reg_avps_db.c0000644000000000000000000000740612223032460017546 0ustar rootroot#include "reg_avps.h" #include "dlist.h" #include "ul_mod.h" #include "../../ut.h" #include "../../sr_module.h" #define TYPE_STRING 's' #define TYPE_NUM 'n' /* int use_serialized_reg_avps = 0; */ /*** table columns ***/ char *avp_column = NULL; /* FIXME - ugly */ extern avp_t *create_avp (avp_flags_t flags, avp_name_t name, avp_value_t val); static inline int num_digits(int i) { int digits = 1; while (i > 9) { i = i / 10; digits++; } /* better will be to use logarithms, but can I use math routines * here (is it linked with libm)? */ return digits; } void get_avp_value_ex(avp_t *avp, str *dst, int *type) { avp_value_t val; /* Warning! it uses static buffer from int2str !!! */ get_avp_val(avp, &val); if (avp->flags & AVP_VAL_STR) { *dst = val.s; *type = AVP_VAL_STR; } else { /* probably (!) number */ dst->s = int2str(val.n, &dst->len); *type = 0; } } /* returns length of serialized avp */ int serialize_avp(avp_t *avp, char *buf) { int len = 0; str name = STR_NULL; str val; str *s; int type; char c; get_avp_value_ex(avp, &val, &type); s = get_avp_name(avp); if (s) name = *s; len = name.len + val.len + 3 /* : */ + 1 /* type */ + num_digits(name.len) + num_digits(val.len) + num_digits(avp->flags); if (buf) { /* can't use int2str because of static buf */ if (type == AVP_VAL_STR) c = TYPE_STRING; else c = TYPE_NUM; sprintf(buf, "%c%d:%d:%d:%.*s%.*s", c, name.len, val.len, avp->flags, name.len, ZSW(name.s), val.len, ZSW(val.s)); /* not effective but simple */ } return len; } static inline char *get_separator(char *buf) /* returns position after the number */ { /* find next ':' */ int i; for (i = 0; buf[i] != ':'; i++); return buf + i; } static char *get_nums(char *buf, int *_name_len, int *_value_len, int *_flags) { str name_len, value_len, flags; char *last; name_len.s = buf + 1; /* skip type */ value_len.s = get_separator(name_len.s) + 1; flags.s = get_separator(value_len.s) + 1; last = get_separator(flags.s); flags.len = last - flags.s; value_len.len = flags.s - value_len.s - 1; name_len.len = value_len.s - name_len.s - 1; /* INFO("I read: name_len=%.*s, value_len=%.*s, flags=%.*s\n", name_len.len, name_len.s, value_len.len, value_len.s, flags.len, flags.s);*/ str2int(&name_len, (unsigned int*)_name_len); str2int(&value_len, (unsigned int*)_value_len); str2int(&flags, (unsigned int*)_flags); return last + 1; } avp_t *deserialize_avps(str *serialized_avps) { avp_t *first, *last, *avp; int i, flags; str value; avp_value_t val; avp_name_t name; if (!serialized_avps) return NULL; if ((serialized_avps->len < 1) || (!serialized_avps->s)) return NULL; first = NULL; last = NULL; i = 0; while (i < serialized_avps->len) { name.s.s = get_nums(serialized_avps->s + i, &name.s.len, &value.len, &flags); value.s = name.s.s + name.s.len; switch (serialized_avps->s[i]) { case TYPE_NUM: str2int(&value, (unsigned int*)&val.n); break; case TYPE_STRING: val.s = value; break; } /* INFO("I read: name=%.*s, value=%.*s, flags=%d\n", name.s.len, name.s.s, value.len, value.s, flags);*/ avp = create_avp(flags, name, val); if (last) last->next = avp; else first = avp; last = avp; i += value.s + value.len - serialized_avps->s; } return first; } int serialize_avps(avp_t *first, str *dst) { avp_t *avp; int len = 0; avp = first; while (avp) { len += serialize_avp(avp, NULL); avp = avp->next; } dst->len = len; if (len < 1) { dst->s = NULL; return 0; } dst->s = (char*)pkg_malloc(len + 1); /* hack: +1 due to \0 from sprintf */ if (!dst->s) { dst->len = 0; ERR("no pkg mem (%d)\n", len); return -1; } avp = first; len = 0; while (avp) { len += serialize_avp(avp, dst->s + len); avp = avp->next; } return 0; } kamailio-4.0.4/obsolete/usrloc/dlist.h0000644000000000000000000000421312223032460016410 0ustar rootroot/* * $Id$ * * List of registered domains * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef DLIST_H #define DLIST_H #include #include "udomain.h" #include "../../str.h" /* * List of all domains registered with usrloc */ typedef struct dlist { str name; /* Name of the domain */ udomain_t* d; /* Payload */ struct dlist* next; /* Next element in the list */ } dlist_t; extern dlist_t* root; /* * Function registers a new domain with usrloc * if the domain exists, pointer to existing structure * will be returned, otherwise a new domain will be * created */ typedef int (*register_udomain_t)(const char* _n, udomain_t** _d); int register_udomain(const char* _n, udomain_t** _d); /* * Free all registered domains */ void free_all_udomains(void); /* * Just for debugging */ void print_all_udomains(FILE* _f); /* * Called from timer */ int synchronize_all_udomains(void); /* * Get contacts to all registered users */ typedef int (*get_all_ucontacts_t) (void* buf, int len, unsigned int flags); int get_all_ucontacts(void *, int, unsigned int); /* * Find a particular domain */ int find_domain(str* _d, udomain_t** _p); #endif /* UDLIST_H */ kamailio-4.0.4/obsolete/usrloc/reg_avps.c0000644000000000000000000001414312223032460017075 0ustar rootroot#include "reg_avps.h" #include "dlist.h" #include "ul_mod.h" #include "../../ut.h" #include "../../sr_module.h" static avp_flags_t reg_avp_flag = 0; /* ************************************************************** */ /* utility functions */ /* FIXME - ugly */ extern avp_t *create_avp (avp_flags_t flags, avp_name_t name, avp_value_t val); void trace_avp(const char *prolog, avp_t *avp) { str *s; s = get_avp_name(avp); if (s) INFO("%s: \"%.*s\" (flags = %d)\n", prolog, s->len, s->s, avp->flags); else INFO("%s: unnamed AVP (flags = %d)\n", prolog, avp->flags); } int use_reg_avps() { return (reg_avp_flag != 0); } /* ************************************************************** */ /* internal functions for storing/restoring AVPs into ucontact */ static inline avp_t *avp_dup(avp_t *avp) { avp_value_t val; avp_name_t name; str *s; if (avp) { get_avp_val(avp, &val); if (avp->flags & AVP_NAME_STR) { s = get_avp_name(avp); if (s) name.s = *s; else { name.s.s = NULL; name.s.len = 0; } } else name.n = avp->id; return create_avp(avp->flags, name, val); } return NULL; } static void reg_destroy_avps(avp_t *avp) { avp_t *n; while (avp) { n = avp->next; shm_free(avp); /* FIXME: really ?? */ avp = n; } } static void remove_avps(avp_t *avp) { struct search_state ss; avp_name_t name; avp_t *a; str *s; if (avp->flags & AVP_NAME_STR) { s = get_avp_name(avp); if (s) name.s = *s; else { name.s.s = NULL; name.s.len = 0; } } else name.n = avp->id; a = search_first_avp(avp->flags, name, 0, &ss); while(a) { destroy_avp(a); a = search_next_avp(&ss, 0); } } static int save_reg_avps_impl(struct ucontact *c) { int i; struct usr_avp *avp, *dup; avp_t *first, *last; static unsigned short lists[] = { AVP_CLASS_USER | AVP_TRACK_FROM, AVP_CLASS_USER | AVP_TRACK_TO, AVP_CLASS_URI | AVP_TRACK_FROM, AVP_CLASS_URI | AVP_TRACK_TO, 0 }; /* destroy old AVPs */ /* if (c->avps) db_delete_reg_avps(c); */ reg_destroy_avps(c->avps); last = NULL; first = NULL; for (i = 0; lists[i]; i++) { for (avp = get_avp_list(lists[i]); avp; avp = avp->next) { /* trace_avp("trying to save avp", avp); */ if ((avp->flags & reg_avp_flag) == 0) continue; /* trace_avp("saving avp", avp); */ dup = avp_dup(avp); if (dup) { /* add AVP into list */ if (last) last->next = dup; else first = dup; last = dup; } } } c->avps = first; /* if (c->avps) db_save_reg_avps(c); */ return 0; } static int restore_reg_avps(struct ucontact *info) { avp_t *avp; avp_value_t val; avp_name_t name; str *s; /* remove all these AVPs ? */ avp = info->avps; while (avp) { remove_avps(avp); avp = avp->next; } /* add stored AVPs */ avp = info->avps; while (avp) { get_avp_val(avp, &val); if (avp->flags & AVP_NAME_STR) { s = get_avp_name(avp); if (s) name.s = *s; else { name.s.s = NULL; name.s.len = 0; } } else name.n = avp->id; /* trace_avp("restoring avp", avp); */ /* modify flags here? */ add_avp(avp->flags, name, val); avp = avp->next; } return 0; } static int delete_reg_avps_impl(struct ucontact *info) { /* db_delete_reg_avps(info); */ if (info->avps) reg_destroy_avps(info->avps); info->avps = NULL; return 0; } /* ************************************************************** */ int set_reg_avpflag_name(char *name) { reg_avp_flag = 0; if (name) { if (!(*name)) return 0; /* -> don't use reg AVPs when zero length */ reg_avp_flag = register_avpflag(name); if (!reg_avp_flag) { ERR("can't register AVP flag %s\n", name); return -1; } } /* else not use reg AVPs */ return 0; } /* * Take AVPS from the current lists and store them in the contact * structure as registration AVPs. Existing registration AVPs will * be destroyed. */ int save_reg_avps(struct ucontact *contact) { /* no locking here! */ if (!use_reg_avps()) return 0; /* INFO("saving registration AVP flags\n"); */ return save_reg_avps_impl(contact); } /* * Delete registration AVPs from the contact */ int delete_reg_avps(struct ucontact* c) { /* no locking here! */ if (!use_reg_avps()) return 0; /*INFO("removing registration AVP flags\n");*/ return delete_reg_avps_impl(c); } /* * Take registration AVPs from the contact and copy * them to the current AVP lists */ int load_reg_avps(struct ucontact *contact) { /* lock udomain here! */ if (!use_reg_avps()) return 0; /* INFO("loading registration AVP flags\n"); */ return restore_reg_avps(contact); } int read_reg_avps_fixup(void** param, int param_no) { udomain_t* d; switch (param_no) { case 1: if (register_udomain((char*)*param, &d) < 0) { ERR("Error while registering domain\n"); return -1; } *param = (void*)d; break; case 2: return fixup_var_str_2(param, param_no); } return 0; } int read_reg_avps(struct sip_msg *m, char* _domain, char* fp) { urecord_t* r = NULL; struct ucontact *contact = NULL; udomain_t *d; str uid; if (!use_reg_avps()) return 1; d = (udomain_t*)_domain; if (get_str_fparam(&uid, m, (fparam_t*)fp) < 0) { ERR("invalid parameter\n"); return -1; } /* INFO("reading avps for uid=%.*s\n", uid.len, ZSW(uid.s)); */ lock_udomain(d); if (get_urecord(d, &uid, &r) != 0) { unlock_udomain(d); WARN("urecord not found\n"); return -1; } if (get_ucontact(r, &m->new_uri, &contact) != 0) { unlock_udomain(d); WARN("ucontact not found\n"); return -1; } load_reg_avps(contact); unlock_udomain(d); return 1; } int dup_reg_avps(struct ucontact *dst, struct ucontact *src) { struct usr_avp *avp, *dup; avp_t *first, *last; /* no locking here! TODO: do it in package memory !!! */ if (!use_reg_avps()) return 0; /* don't use reg avps */ /* destroy old AVPs */ /* if (dst->avps) db_delete_reg_avps(dst); */ reg_destroy_avps(dst->avps); last = NULL; first = NULL; avp = src->avps; while (avp) { dup = avp_dup(avp); if (dup) { /* add AVP into list */ if (last) last->next = dup; else first = dup; last = dup; } avp = avp->next; } dst->avps = first; /* if (dst->avps) db_save_reg_avps(dst); */ return 0; } kamailio-4.0.4/obsolete/usrloc/ul_mod.h0000644000000000000000000000423612223032460016555 0ustar rootroot/* * $Id$ * * User location module interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2003-03-12 added replication and state column (nils) */ #ifndef UL_MOD_H #define UL_MOD_H #include "../../lib/srdb2/db.h" #include "../../str.h" /* * Module parameters */ enum ul_db_type { NO_DB = 0, /* No DB access */ WRITE_THROUGH, /* Propagate changes to DB immediately */ WRITE_BACK, /* Propagate changes with delay */ READONLY, /* Perform initial raad and don't update */ UL_DB_MAX }; #define NO_DB 0 #define WRITE_THROUGH 1 #define WRITE_BACK 2 #define READONLY 3 extern str uid_col; extern str contact_col; extern str expires_col; extern str q_col; extern str callid_col; extern str cseq_col; extern str method_col; extern str flags_col; extern str user_agent_col; extern str received_col; extern str instance_col; extern str aor_col; extern str server_id_col; extern str db_url; extern int timer_interval; extern int db_mode; extern int desc_time_order; extern int db_skip_delete; extern db_ctx_t* db; extern db_cmd_t** del_rec; extern db_cmd_t** del_contact; extern db_cmd_t** ins_contact; extern int cmd_n, cur_cmd; #endif /* UL_MOD_H */ kamailio-4.0.4/obsolete/usrloc/ul_rpc.c0000644000000000000000000002131112223032460016546 0ustar rootroot/* * $Id$ * * Usrloc module interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "../../dprint.h" #include "../../ut.h" #include "../../globals.h" #include "dlist.h" #include "utime.h" #include "ul_mod.h" #include "ul_rpc.h" static inline void rpc_find_domain(str* _name, udomain_t** _d) { dlist_t* ptr; ptr = root; while(ptr) { if ((ptr->name.len == _name->len) && !memcmp(ptr->name.s, _name->s, _name->len)) { break; } ptr = ptr->next; } if (ptr) { *_d = ptr->d; } else { *_d = 0; } } static inline int add_contact(udomain_t* _d, str* _u, str* _c, time_t _e, qvalue_t _q, int _f, int sid) { urecord_t* r; ucontact_t* c = 0; int res; str cid; str ua; str aor = STR_NULL; if (_e == 0 && !(_f & FL_PERMANENT)) { LOG(L_ERR, "rpc_add_contact(): expires == 0 and not persistent contact, giving up\n"); return -1; } get_act_time(); res = get_urecord(_d, _u, &r); if (res < 0) { LOG(L_ERR, "rpc_add_contact(): Error while getting record\n"); return -2; } if (res > 0) { /* Record not found */ if (insert_urecord(_d, _u, &r) < 0) { LOG(L_ERR, "rpc_add_contact(): Error while creating new urecord\n"); return -3; } } else { if (get_ucontact(r, _c, &c) < 0) { LOG(L_ERR, "rpc_add_contact(): Error while obtaining ucontact\n"); return -4; } } cid.s = "RPC-Call-ID"; cid.len = strlen(cid.s); ua.s = "SER-RPC"; ua.len = strlen(ua.s); if (c) { if (update_ucontact(c, &aor, _c, _e + act_time, _q, &cid, 42, _f, FL_NONE, &ua, 0, 0, 0, sid == -1 ? server_id : sid) < 0) { LOG(L_ERR, "rpc_add_contact(): Error while updating contact\n"); release_urecord(r); return -5; } } else { if (insert_ucontact(r, &aor, _c, _e + act_time, _q, &cid, 42, _f, &c, &ua, 0, 0, 0, sid == -1 ? server_id : sid) < 0) { LOG(L_ERR, "rpc_add_contact(): Error while inserting contact\n"); release_urecord(r); return -6; } } release_urecord(r); return 0; } static const char* rpc_stats_doc[2] = { "Print usrloc statistics", 0 }; static void rpc_stats(rpc_t* rpc, void* c) { dlist_t* ptr; void* handle; ptr = root; while(ptr) { rpc->add(c, "{", &handle); rpc->struct_add(handle, "Sdd", "domain", ptr->d->name, "users", ptr->d->users, "expired", ptr->d->expired); ptr = ptr->next; } } static const char* rpc_delete_uid_doc[2] = { "Delete all registered contacts for address of record.", 0 }; static void rpc_delete_uid(rpc_t* rpc, void* c) { udomain_t* d; str uid, t; if (rpc->scan(c, "SS", &t, &uid) < 2) return; rpc_find_domain(&t, &d); if (d) { lock_udomain(d); if (delete_urecord(d, &uid) < 0) { ERR("Error while deleting user %.*s\n", uid.len, uid.s); unlock_udomain(d); rpc->fault(c, 500, "Error While Deleting Record"); return; } unlock_udomain(d); } else { rpc->fault(c, 400, "Table Not Found"); } } static const char* rpc_delete_contact_doc[2] = { "Delete a contact if it exists.", 0 }; static void rpc_delete_contact(rpc_t* rpc, void* ctx) { udomain_t* d; urecord_t* r; ucontact_t* con; str uid, t, c; int res; if (rpc->scan(ctx, "SSS", &t, &uid, &c) < 3) return; rpc_find_domain(&t, &d); if (d) { lock_udomain(d); res = get_urecord(d, &uid, &r); if (res < 0) { rpc->fault(ctx, 500, "Error While Searching Table"); ERR("Error while looking for uid %.*s in table %.*s\n", uid.len, uid.s, t.len, t.s); unlock_udomain(d); return; } if (res > 0) { rpc->fault(ctx, 404, "AOR Not Found"); unlock_udomain(d); return; } res = get_ucontact(r, &c, &con); if (res < 0) { rpc->fault(ctx, 500, "Error While Searching for Contact"); ERR("Error while looking for contact %.*s\n", c.len, c.s); unlock_udomain(d); return; } if (res > 0) { rpc->fault(ctx, 404, "Contact Not Found"); unlock_udomain(d); return; } if (delete_ucontact(r, con) < 0) { rpc->fault(ctx, 500, "Error While Deleting Contact"); unlock_udomain(d); return; } release_urecord(r); unlock_udomain(d); } else { rpc->fault(ctx, 404, "Table Not Found"); } } static const char* rpc_dump_doc[2] = { "Print all registered contacts.", 0 }; static void rpc_dump(rpc_t* rpc, void* c) { rpc->fault(c, 500, "Not Yet Implemented"); } static const char* rpc_dump_file_doc[2] = { "Print all registered contacts into a file.", 0 }; static void rpc_dump_file(rpc_t* rpc, void* c) { str filename; FILE *file; if (rpc->scan(c, "S", &filename) < 1) { return; } DBG("dumping to file '%.*s'.\n", filename.len, ZSW(filename.s)); if (! (file = fopen(filename.s, "w"))) { ERR("failed to open file `%s'.\n", filename.s); rpc->fault(rpc, 500, "failed to open file `%s'.\n", filename.s); return; } print_all_udomains(file); fclose(file); } static const char* rpc_flush_doc[2] = { "Flush cache into database.", 0 }; static void rpc_flush(rpc_t* rpc, void* c) { synchronize_all_udomains(); } static const char* rpc_add_contact_doc[2] = { "Create a new contact.", 0 }; static void rpc_add_contact(rpc_t* rpc, void* c) { udomain_t* d; int expires, flags, sid; double q; qvalue_t qval; str table, uid, contact; if (rpc->scan(c, "SSSdfd", &table, &uid, &contact, &expires, &q, &flags) < 6) return; qval = double2q(q); if (rpc->scan(c, "d", &sid) < 1) sid = -1; rpc_find_domain(&table, &d); if (d) { lock_udomain(d); if (add_contact(d, &uid, &contact, expires, qval, flags, sid) < 0) { unlock_udomain(d); ERR("Error while adding contact ('%.*s','%.*s') in table '%.*s'\n", uid.len, ZSW(uid.s), contact.len, ZSW(contact.s), table.len, ZSW(table.s)); rpc->fault(c, 500, "Error while adding Contact"); return; } unlock_udomain(d); } else { rpc->fault(c, 400, "Table Not Found"); } } /* * Build Contact HF for reply */ static inline int print_contacts(rpc_t* rpc, void* ctx, ucontact_t* _c) { int cnt = 0; void* handle; while(_c) { if (VALID_CONTACT(_c, act_time)) { cnt++; if (rpc->add(ctx, "{", &handle) < 0) return -1; rpc->struct_add(handle, "SfdSS", "contact", &_c->c, "q", q2double(_c->q), "expires", (int)(_c->expires - act_time), "ua", &_c->user_agent, "recv", &_c->received); } _c = _c->next; } return cnt; } static const char* rpc_show_contacts_doc[2] = { "List all registered contacts for address of record", 0 }; static void rpc_show_contacts(rpc_t* rpc, void* c) { udomain_t* d; urecord_t* r; int res; str t, uid; if (rpc->scan(c, "SS", &t, &uid) < 2) return; rpc_find_domain(&t, &d); if (d) { lock_udomain(d); res = get_urecord(d, &uid, &r); if (res < 0) { rpc->fault(c, 500, "Error While Searching AOR"); ERR("Error while looking for username %.*s in table %.*s\n", uid.len, uid.s, t.len, t.s); unlock_udomain(d); return; } if (res > 0) { rpc->fault(c, 404, "AOR Not Found"); unlock_udomain(d); return; } get_act_time(); if (!print_contacts(rpc, c, r->contacts)) { unlock_udomain(d); rpc->fault(c, 404, "No Registered Contacts Found"); return; } unlock_udomain(d); } else { rpc->fault(c, 400, "Table Not Found"); } } rpc_export_t ul_rpc[] = { {"usrloc.stats", rpc_stats, rpc_stats_doc, RET_ARRAY}, {"usrloc.delete_uid", rpc_delete_uid, rpc_delete_uid_doc, 0}, {"usrloc.delete_contact", rpc_delete_contact, rpc_delete_contact_doc, 0}, {"usrloc.dump", rpc_dump, rpc_dump_doc, 0}, {"usrloc.dump_file", rpc_dump_file, rpc_dump_file_doc, 0}, {"usrloc.flush", rpc_flush, rpc_flush_doc, 0}, {"usrloc.add_contact", rpc_add_contact, rpc_add_contact_doc, 0}, {"usrloc.show_contacts", rpc_show_contacts, rpc_show_contacts_doc, RET_ARRAY}, {0, 0, 0, 0} }; kamailio-4.0.4/obsolete/usrloc/reg_avps.h0000644000000000000000000000151612223032460017102 0ustar rootroot#ifndef __REG_AVPS_H #define __REG_AVPS_H #define DEFAULT_REG_AVPFLAG_NAME "regavps" #include "../../usr_avp.h" #include "../../str.h" #include "../../lib/srdb2/db_con.h" #include "../usrloc/udomain.h" int save_reg_avps(struct ucontact* c); int dup_reg_avps(struct ucontact *dst, struct ucontact *src); int delete_reg_avps(struct ucontact* c); int set_reg_avpflag_name(char *name); int read_reg_avps(struct sip_msg *m, char*, char*); int read_reg_avps_fixup(void** param, int param_no); /* module parameters (column and table names and DB control) */ /* set serialized_reg_avp_column to non-NULL & non-empty value to * allow AVPs to be serialized into given column in location table * (better will be extra numeric switch, but ...) */ extern char *avp_column; /* returns 1 if reg AVPs are used, 0 if not */ int use_reg_avps(); #endif kamailio-4.0.4/obsolete/usrloc/usrloc.h0000644000000000000000000000365612223032460016612 0ustar rootroot/* * $Id$ * * Convenience usrloc header file * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef USRLOC_H #define USRLOC_H #include "dlist.h" #include "udomain.h" #include "urecord.h" #include "ucontact.h" #include "ul_callback.h" typedef struct usrloc_api { register_udomain_t register_udomain; get_all_ucontacts_t get_all_ucontacts; insert_urecord_t insert_urecord; delete_urecord_t delete_urecord; get_urecord_t get_urecord; lock_udomain_t lock_udomain; unlock_udomain_t unlock_udomain; release_urecord_t release_urecord; insert_ucontact_t insert_ucontact; delete_ucontact_t delete_ucontact; get_ucontact_t get_ucontact; get_ucontact_by_inst_t get_ucontact_by_instance; update_ucontact_t update_ucontact; register_watcher_t register_watcher; unregister_watcher_t unregister_watcher; register_ulcb_t register_ulcb; } usrloc_api_t; typedef int (*bind_usrloc_t)(usrloc_api_t* api); #endif /* USRLOC_H */ kamailio-4.0.4/obsolete/usrloc/urecord.c0000644000000000000000000002512212223032460016731 0ustar rootroot/* * $Id$ * * Usrloc record structure * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2003-03-12 added replication mark and zombie state support (nils) * 2004-03-17 generic callbacks added (bogdan) * 2004-06-07 updated to the new DB api (andrei) */ #include "urecord.h" #include #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../ut.h" #include "ul_mod.h" #include "utime.h" /* #include "del_list.h" */ /* #include "ins_list.h" */ #include "notify.h" #include "ul_callback.h" #include "reg_avps.h" /* * Create and initialize new record structure */ int new_urecord(str* _dom, str* _uid, urecord_t** _r) { *_r = (urecord_t*)shm_malloc(sizeof(urecord_t)); if (*_r == 0) { LOG(L_ERR, "new_urecord(): No memory left\n"); return -1; } memset(*_r, 0, sizeof(urecord_t)); (*_r)->uid.s = (char*)shm_malloc(_uid->len); if ((*_r)->uid.s == 0) { LOG(L_ERR, "new_urecord(): No memory left\n"); shm_free(*_r); return -2; } memcpy((*_r)->uid.s, _uid->s, _uid->len); (*_r)->uid.len = _uid->len; (*_r)->domain = _dom; return 0; } /* * Free all memory used by the given structure * The structure must be removed from all linked * lists first */ void free_urecord(urecord_t* _r) { notify_cb_t* watcher; ucontact_t* ptr; while(_r->watchers) { watcher = _r->watchers; _r->watchers = watcher->next; shm_free(watcher); } while(_r->contacts) { ptr = _r->contacts; _r->contacts = _r->contacts->next; free_ucontact(ptr); } if (_r->uid.s) shm_free(_r->uid.s); shm_free(_r); } /* * Print a record */ void print_urecord(FILE* _f, urecord_t* _r) { ucontact_t* ptr; fprintf(_f, "...Record(%p)...\n", _r); fprintf(_f, "domain: '%.*s'\n", _r->domain->len, ZSW(_r->domain->s)); fprintf(_f, "uid : '%.*s'\n", _r->uid.len, ZSW(_r->uid.s)); if (_r->contacts) { ptr = _r->contacts; while(ptr) { print_ucontact(_f, ptr); ptr = ptr->next; } } fprintf(_f, ".../Record...\n"); } /* * Add a new contact * Contacts are ordered by: 1) q * 2) descending modification time */ int mem_insert_ucontact(urecord_t* _r, str* aor, str* _c, time_t _e, qvalue_t _q, str* _cid, int _cs, unsigned int _flags, struct ucontact** _con, str* _ua, str* _recv, struct socket_info* sock, str* _inst, int sid) { ucontact_t* ptr, *prev = 0; if (new_ucontact(_r->domain, &_r->uid, aor, _c, _e, _q, _cid, _cs, _flags, _con, _ua, _recv, sock, _inst, sid) < 0) { LOG(L_ERR, "mem_insert_ucontact(): Can't create new contact\n"); return -1; } ptr = _r->contacts; if (!desc_time_order) { while(ptr) { if (ptr->q < _q) break; prev = ptr; ptr = ptr->next; } } if (ptr) { if (!ptr->prev) { ptr->prev = *_con; (*_con)->next = ptr; _r->contacts = *_con; } else { (*_con)->next = ptr; (*_con)->prev = ptr->prev; ptr->prev->next = *_con; ptr->prev = *_con; } } else if (prev) { prev->next = *_con; (*_con)->prev = prev; } else { _r->contacts = *_con; } return 0; } /* * Remove the contact from lists */ void mem_remove_ucontact(urecord_t* _r, ucontact_t* _c) { if (_c->prev) { _c->prev->next = _c->next; if (_c->next) { _c->next->prev = _c->prev; } } else { _r->contacts = _c->next; if (_c->next) { _c->next->prev = 0; } } } /* * Remove contact from the list and delete */ void mem_delete_ucontact(urecord_t* _r, ucontact_t* _c) { mem_remove_ucontact(_r, _c); free_ucontact(_c); } /* * This timer routine is used when * db_mode is set to NO_DB or READONLY */ static inline int nodb_timer(urecord_t* _r) { ucontact_t* ptr, *t; int not = 0; ptr = _r->contacts; while(ptr) { if (!VALID_CONTACT(ptr, act_time)) { /* run callbacks for EXPIRE event */ if (exists_ulcb_type(UL_CONTACT_EXPIRE)) run_ul_callbacks( UL_CONTACT_EXPIRE, ptr); notify_watchers(_r, ptr, PRES_OFFLINE); LOG(L_NOTICE, "Binding '%.*s','%.*s' has expired\n", ptr->uid->len, ZSW(ptr->uid->s), ptr->c.len, ZSW(ptr->c.s)); t = ptr; ptr = ptr->next; /* it was the last contact and it was in normal * state, so notify */ if (!ptr && t->state == CS_NEW) not=1; delete_reg_avps(t); mem_delete_ucontact(_r, t); _r->slot->d->expired++; } else { ptr = ptr->next; } } return 0; } /* * This routine is used when db_mode is * set to WRITE_THROUGH */ static inline int wt_timer(urecord_t* _r) { ucontact_t* ptr, *t; int not = 0; ptr = _r->contacts; while(ptr) { if (!VALID_CONTACT(ptr, act_time)) { /* run callbacks for EXPIRE event */ if (exists_ulcb_type(UL_CONTACT_EXPIRE)) { run_ul_callbacks( UL_CONTACT_EXPIRE, ptr); } notify_watchers(_r, ptr, PRES_OFFLINE); LOG(L_NOTICE, "Binding '%.*s','%.*s' has expired\n", ptr->uid->len, ZSW(ptr->uid->s), ptr->c.len, ZSW(ptr->c.s)); t = ptr; ptr = ptr->next; /* it was the last contact and it was in normal * state, so notify */ if (!ptr && t->state == CS_SYNC) not=1; if (db_delete_ucontact(t) < 0) { LOG(L_ERR, "wt_timer(): Error while deleting contact from " "database\n"); } delete_reg_avps(t); mem_delete_ucontact(_r, t); _r->slot->d->expired++; } else { /* the contact was unregistered and is not marked * for replication so remove it, but the notify was * already done during unregister */ ptr = ptr->next; } } return 0; } /* * Write-back timer */ static inline int wb_timer(urecord_t* _r) { ucontact_t* ptr, *t; int op; int not = 0; ptr = _r->contacts; while(ptr) { if (!VALID_CONTACT(ptr, act_time)) { /* run callbacks for EXPIRE event */ if (exists_ulcb_type(UL_CONTACT_EXPIRE)) { run_ul_callbacks( UL_CONTACT_EXPIRE, ptr); } notify_watchers(_r, ptr, PRES_OFFLINE); LOG(L_NOTICE, "Binding '%.*s','%.*s' has expired\n", ptr->uid->len, ZSW(ptr->uid->s), ptr->c.len, ZSW(ptr->c.s)); if (ptr->next == 0) not=1; _r->slot->d->expired++; t = ptr; ptr = ptr->next; /* Should we remove the contact from the database ? */ if (st_expired_ucontact(t) == 1) { if (db_delete_ucontact(t) < 0) { LOG(L_ERR, "wb_timer(): Can't delete contact from the database\n"); } } delete_reg_avps(t); mem_delete_ucontact(_r, t); } else { /* Determine the operation we have to do */ op = st_flush_ucontact(ptr); switch(op) { case 0: /* do nothing, contact is synchronized */ break; case 1: /* insert */ if (db_store_ucontact(ptr) < 0) { LOG(L_ERR, "wb_timer(): Error while inserting contact into database\n"); } break; case 2: /* update */ if (db_store_ucontact(ptr) < 0) { LOG(L_ERR, "wb_timer(): Error while updating contact in db\n"); } break; case 4: /* delete */ if (db_delete_ucontact(ptr) < 0) { LOG(L_ERR, "wb_timer(): Can't delete contact from database\n"); } /* fall through to the next case statement */ case 3: /* delete from memory */ delete_reg_avps(ptr); mem_delete_ucontact(_r, ptr); break; } ptr = ptr->next; } } return 0; } int timer_urecord(urecord_t* _r) { switch(db_mode) { case NO_DB: return nodb_timer(_r); case WRITE_THROUGH: return wt_timer(_r); case WRITE_BACK: return wb_timer(_r); case READONLY: return nodb_timer(_r); } return 0; /* Makes gcc happy */ } /* * Release urecord previously obtained * through get_urecord */ void release_urecord(urecord_t* _r) { if (_r->contacts == 0) { mem_delete_urecord(_r->slot->d, _r); } } /* * Create and insert new contact * into urecord */ int insert_ucontact(urecord_t* _r, str* aor, str* _c, time_t _e, qvalue_t _q, str* _cid, int _cs, unsigned int _flags, struct ucontact** _con, str* _ua, str* _recv, struct socket_info* sock, str* _inst, int sid) { if (mem_insert_ucontact(_r, aor, _c, _e, _q, _cid, _cs, _flags, _con, _ua, _recv, sock, _inst, sid) < 0) { LOG(L_ERR, "insert_ucontact(): Error while inserting contact\n"); return -1; } notify_watchers(_r, *_con, (_e > 0) ? PRES_ONLINE : PRES_OFFLINE); if (exists_ulcb_type(UL_CONTACT_INSERT)) { run_ul_callbacks( UL_CONTACT_INSERT, *_con); } save_reg_avps(*_con); if (db_mode == WRITE_THROUGH) { if (db_store_ucontact(*_con) < 0) { LOG(L_ERR, "insert_ucontact(): Error while inserting in database\n"); } (*_con)->state = CS_SYNC; } return 0; } /* * Delete ucontact from urecord */ int delete_ucontact(urecord_t* _r, struct ucontact* _c) { if (exists_ulcb_type(UL_CONTACT_DELETE)) { run_ul_callbacks( UL_CONTACT_DELETE, _c); } notify_watchers(_r, _c, PRES_OFFLINE); if (st_delete_ucontact(_c) > 0) { if (db_mode == WRITE_THROUGH) { if (db_delete_ucontact(_c) < 0) { LOG(L_ERR, "delete_ucontact(): Can't remove contact from " "database\n"); } } delete_reg_avps(_c); mem_delete_ucontact(_r, _c); } return 0; } /* * Get pointer to ucontact with given contact */ int get_ucontact(urecord_t* _r, str* _c, struct ucontact** _co) { ucontact_t* ptr; ptr = _r->contacts; while(ptr) { if ((_c->len == ptr->c.len && !memcmp(_c->s, ptr->c.s, _c->len))) { *_co = ptr; return 0; } ptr = ptr->next; } return 1; } /* * Get pointer to ucontact with given contact and given sip.instance */ int get_ucontact_by_instance(urecord_t* _r, str* _c, str* _i, struct ucontact** _co) { ucontact_t* ptr; if (_i == NULL) { return get_ucontact(_r, _c, _co); } ptr = _r->contacts; while(ptr) { if ((_i->len == ptr->instance.len && !memcmp(_i->s, ptr->instance.s, _i->len)) || (_c->len == ptr->c.len && !memcmp(_c->s, ptr->c.s, _c->len))) { *_co = ptr; return 0; } ptr = ptr->next; } return 1; } kamailio-4.0.4/obsolete/usrloc/ul_callback.h0000644000000000000000000000504712223032460017533 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2004-03-16 created (bogdan) */ #ifndef _UL_CALLBACKS_H #define _UL_CALLBACKS_H #include "ucontact.h" #define UL_CONTACT_INSERT (1<<0) #define UL_CONTACT_UPDATE (1<<1) #define UL_CONTACT_DELETE (1<<2) #define UL_CONTACT_EXPIRE (1<<3) #define ULCB_MAX ((1<<4)-1) /* callback function prototype */ typedef void (ul_cb) (ucontact_t *c, int type, void *param); /* register callback function prototype */ typedef int (*register_ulcb_t)( int cb_types, ul_cb f, void *param); struct ul_callback { int id; /* id of this callback - useless */ int types; /* types of events that trigger the callback*/ ul_cb* callback; /* callback function */ void *param; /* param to be passed to callback function */ struct ul_callback* next; }; struct ulcb_head_list { struct ul_callback *first; int reg_types; }; extern struct ulcb_head_list* ulcb_list; #define exists_ulcb_type(_types_) \ ( (ulcb_list->reg_types)|(_types_) ) int init_ulcb_list(); void destroy_ulcb_list(); /* register a callback for several types of events */ int register_ulcb( int types, ul_cb f, void *param ); /* run all transaction callbacks for an event type */ static inline void run_ul_callbacks( int type , ucontact_t *c) { struct ul_callback *cbp; for (cbp=ulcb_list->first; cbp; cbp=cbp->next) { DBG("DBG:usrloc: contact=%p, callback type %d, id %d entered\n", c, cbp->types, cbp->id ); cbp->callback( c, type, cbp->param ); } } #endif kamailio-4.0.4/obsolete/usrloc/notify.c0000644000000000000000000000630112223032460016574 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "notify.h" #include "../../ut.h" #include "../../mem/shm_mem.h" #include "dlist.h" #include "udomain.h" str dom = STR_STATIC_INIT("location"); void notify_watchers(struct urecord* _r, ucontact_t *_c, int state) { notify_cb_t* n; n = _r->watchers; while(n) { n->cb(&_r->uid, &_c->c, state, n->data); n = n->next; } } int add_watcher(struct urecord* _r, notcb_t _c, void* _d) { notify_cb_t* ptr; ucontact_t *c; ptr = (notify_cb_t*)shm_malloc(sizeof(notify_cb_t)); if (ptr == 0) { LOG(L_ERR, "add_watcher(): No memory left\n"); return -1; } ptr->cb = _c; ptr->data = _d; ptr->next = _r->watchers; _r->watchers = ptr; c = _r->contacts; while (c) { ptr->cb(&_r->uid, &c->c, PRES_ONLINE, ptr->data); c = c->next; } return 0; } int remove_watcher(struct urecord* _r, notcb_t _c, void* _d) { notify_cb_t* ptr, *prev = 0;; ptr = _r->watchers; while(ptr) { if ((ptr->cb == _c) && (ptr->data == _d)) { if (prev) prev->next = ptr->next; else _r->watchers = ptr->next; shm_free(ptr); return 0; } prev = ptr; ptr = ptr->next; } return 1; } int register_watcher(str* _f, str* _t, notcb_t _c, void* _data) { udomain_t* d; urecord_t* r; if (find_domain(&dom, &d) > 0) { LOG(L_ERR, "register_watcher(): Domain '%.*s' not found\n", dom.len, ZSW(dom.s)); return -1; } lock_udomain(d); if (get_urecord(d, _t, &r) > 0) { if (insert_urecord(d, _t, &r) < 0) { unlock_udomain(d); LOG(L_ERR, "register_watcher(): Error while creating a new record\n"); return -2; } } if (add_watcher(r, _c, _data) < 0) { LOG(L_ERR, "register_watcher(): Error while adding a watcher\n"); release_urecord(r); unlock_udomain(d); return -3; } unlock_udomain(d); return 0; } int unregister_watcher(str* _f, str* _t, notcb_t _c, void* _data) { udomain_t* d; urecord_t* r; if (find_domain(&dom, &d) > 0) { LOG(L_ERR, "unregister_watcher(): Domain '%.*s' not found\n", dom.len, ZSW(dom.s)); return -1; } lock_udomain(d); if (get_urecord(d, _t, &r) > 0) { unlock_udomain(d); DBG("unregister_watcher(): Record not found\n"); return 0; } remove_watcher(r, _c, _data); release_urecord(r); unlock_udomain(d); return 0; } kamailio-4.0.4/obsolete/usrloc/notify.h0000644000000000000000000000433712223032460016610 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NOTIFY_H #define NOTIFY_H #include "../../str.h" #include "urecord.h" #include "../../parser/msg_parser.h" /* FIXME: Possible race condition - a record pointer will be put in notify_record, domain lock * will be released, meanwhile pa module unregisters the callback and contacts will be removed * too, then the record will be removed and notify_record will point to an non-existent structure */ struct urecord; typedef enum pres_state { PRES_OFFLINE = 0, PRES_ONLINE } pres_state_t; typedef void (*notcb_t)(str* uid, str* _contact, pres_state_t _p, void* _d); typedef int (*register_watcher_t)(str* _f, str* _t, notcb_t _c, void* _data); typedef int (*unregister_watcher_t)(str* _f, str* _t, notcb_t _c, void* _data); typedef struct notify_cb { notcb_t cb; void* data; struct notify_cb* next; } notify_cb_t; void notify_watchers(struct urecord* _r, ucontact_t *_c, int state); int add_watcher(struct urecord* _r, notcb_t _c, void* _d); int remove_watcher(struct urecord* _r, notcb_t _c, void* _d); int register_watcher(str* _d, str* uid, notcb_t _c, void* _data); int unregister_watcher(str* _d, str* uid, notcb_t _c, void* _data); int post_script(struct sip_msg* _m, void* param); #endif /* NOTIFY_H */ kamailio-4.0.4/obsolete/usrloc/dlist.c0000644000000000000000000001675412223032460016420 0ustar rootroot/* * $Id$ * * List of registered domains * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "dlist.h" #include /* abort */ #include /* strlen, memcmp */ #include /* printf */ #include "../../ut.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "../../globals.h" #include "udomain.h" /* new_udomain, free_udomain */ #include "utime.h" #include "ul_mod.h" /* * List of all registered domains */ dlist_t* root = 0; /* * Find domain with the given name * Returns 0 if the domain was found * and 1 of not */ static inline int find_dlist(str* _n, dlist_t** _d) { dlist_t* ptr; ptr = root; while(ptr) { if ((_n->len == ptr->name.len) && !memcmp(_n->s, ptr->name.s, _n->len)) { *_d = ptr; return 0; } ptr = ptr->next; } return 1; } /* * Return list of all contacts for all currently registered * users in all domains. Caller must provide buffer of * sufficient length for fitting all those contacts. In the * case when buffer was exhausted, the function returns * estimated amount of additional space needed, in this * case the caller is expected to repeat the call using * this value as the hint. * * Information is packed into the buffer as follows: * * +------------+----------+-----+------------+----------+-----+ * |contact1.len|contact1.s|sock1|contact2.len|contact2.s|sock2| * +------------+----------+-----+------------+----------+-----+ * |.............................|contactN.len|contactN.s|sockN| * +------------+----------+-----+------------+----------+-----+ * |000000000000| * +------------+ */ int get_all_ucontacts(void *buf, int len, unsigned int flags) { dlist_t *p; urecord_t *r; ucontact_t *c; void *cp; int shortage; cp = buf; shortage = 0; /* Reserve space for terminating 0000 */ len -= sizeof(c->c.len); for (p = root; p != NULL; p = p->next) { lock_udomain(p->d); if (p->d->d_ll.n <= 0) { unlock_udomain(p->d); continue; } for (r = p->d->d_ll.first; r != NULL; r = r->d_ll.next) { for (c = r->contacts; c != NULL; c = c->next) { if (c->c.len <= 0) continue; /* * List only contacts that have all requested * flags set */ if ((c->flags & flags) != flags) continue; /* List only contacts with matching server id */ if (c->server_id != server_id) continue; if (c->received.s) { if (len >= (int)(sizeof(c->received.len) + c->received.len + sizeof(c->sock))) { memcpy(cp, &c->received.len, sizeof(c->received.len)); cp = (char*)cp + sizeof(c->received.len); memcpy(cp, c->received.s, c->received.len); cp = (char*)cp + c->received.len; memcpy(cp, &c->sock, sizeof(c->sock)); cp = (char*)cp + sizeof(c->sock); len -= sizeof(c->received.len) + c->received.len + sizeof(c->sock); } else { shortage += sizeof(c->received.len) + c->received.len + sizeof(c->sock); } } else { if (len >= (int)(sizeof(c->c.len) + c->c.len + sizeof(c->sock))) { memcpy(cp, &c->c.len, sizeof(c->c.len)); cp = (char*)cp + sizeof(c->c.len); memcpy(cp, c->c.s, c->c.len); cp = (char*)cp + c->c.len; memcpy(cp, &c->sock, sizeof(c->sock)); cp = (char*)cp + sizeof(c->sock); len -= sizeof(c->c.len) + c->c.len + sizeof(c->sock); } else { shortage += sizeof(c->c.len) + c->c.len + sizeof(c->sock); } } } } unlock_udomain(p->d); } /* len < 0 is possible, if size of the buffer < sizeof(c->c.len) */ if (len >= 0) memset(cp, 0, sizeof(c->c.len)); /* Shouldn't happen */ if (shortage > 0 && len > shortage) { abort(); } shortage -= len; return shortage > 0 ? shortage : 0; } /* * Create a new domain structure * Returns 0 if everything went OK, otherwise value < 0 * is returned */ static inline int new_dlist(str* _n, dlist_t** _d) { dlist_t* ptr; ptr = (dlist_t*)shm_malloc(sizeof(dlist_t)); if (ptr == 0) { LOG(L_ERR, "new_dlist(): No memory left\n"); return -1; } memset(ptr, 0, sizeof(dlist_t)); ptr->name.s = (char*)shm_malloc(_n->len + 1); if (ptr->name.s == 0) { LOG(L_ERR, "new_dlist(): No memory left 2\n"); shm_free(ptr); return -2; } memcpy(ptr->name.s, _n->s, _n->len); ptr->name.s[_n->len] = '\0'; ptr->name.len = _n->len; if (new_udomain(&(ptr->name), &(ptr->d)) < 0) { LOG(L_ERR, "new_dlist(): Error while creating domain structure\n"); shm_free(ptr->name.s); shm_free(ptr); return -3; } *_d = ptr; return 0; } /* * Function registers a new domain with usrloc * if the domain exists, pointer to existing structure * will be returned, otherwise a new domain will be * created */ int register_udomain(const char* _n, udomain_t** _d) { dlist_t* d; str s; s.s = (char*)_n; s.len = strlen(_n); if (find_dlist(&s, &d) == 0) { *_d = d->d; return 0; } if (new_dlist(&s, &d) < 0) { LOG(L_ERR, "register_udomain(): Error while creating new domain\n"); return -1; } /* Preload domain with data from database if we are gonna * to use database */ if (db_mode != NO_DB) { db = db_ctx("usrloc"); if (db == NULL) { ERR("Error while initializing database layer\n"); goto err; } if (db_add_db(db, db_url.s) < 0) goto err; if (db_connect(db) < 0) goto err; if (preload_udomain(d->d) < 0) { LOG(L_ERR, "register_udomain(): Error while preloading domain '%.*s'\n", s.len, ZSW(s.s)); goto err; } db_disconnect(db); db_ctx_free(db); db = NULL; } d->next = root; root = d; *_d = d->d; return 0; err: if (db) { db_disconnect(db); db_ctx_free(db); db = NULL; } free_udomain(d->d); shm_free(d->name.s); shm_free(d); return -1; } /* * Free all allocated memory */ void free_all_udomains(void) { dlist_t* ptr; while(root) { ptr = root; root = root->next; free_udomain(ptr->d); shm_free(ptr->name.s); shm_free(ptr); } } /* * Just for debugging */ void print_all_udomains(FILE* _f) { dlist_t* ptr; ptr = root; fprintf(_f, "===Domain list===\n"); while(ptr) { print_udomain(_f, ptr->d); ptr = ptr->next; } fprintf(_f, "===/Domain list===\n"); } /* * Run timer handler of all domains */ int synchronize_all_udomains(void) { int res = 0; dlist_t* ptr; get_act_time(); /* Get and save actual time */ ptr = root; while(ptr) { res |= timer_udomain(ptr->d); ptr = ptr->next; } return res; } /* * Find a particular domain */ int find_domain(str* _d, udomain_t** _p) { dlist_t* d; if (find_dlist(_d, &d) == 0) { *_p = d->d; return 0; } return 1; } kamailio-4.0.4/obsolete/usrloc/ul_mod.c0000644000000000000000000002675412223032460016561 0ustar rootroot/* * $Id$ * * Usrloc module interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * History: * --------- * 2003-01-27 timer activity printing #ifdef-ed to EXTRA_DEBUG (jiri) * 2003-03-11 New module interface (janakj) * 2003-03-12 added replication and state columns (nils) * 2003-03-16 flags export parameter added (janakj) * 2003-04-05: default_uri #define used (jiri) * 2003-04-21 failed fifo init stops init process (jiri) * 2004-03-17 generic callbacks added (bogdan) * 2004-06-07 updated to the new DB api (andrei) * 2005-02-25 incoming socket is saved in ucontact record (bogdan) */ #include #include "ul_mod.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../timer.h" /* register_timer */ #include "../../globals.h" /* is_main */ #include "dlist.h" /* register_udomain */ #include "udomain.h" /* {insert,delete,get,release}_urecord */ #include "urecord.h" /* {insert,delete,get}_ucontact */ #include "ucontact.h" /* update_ucontact */ #include "ul_callback.h" #include "notify.h" #include "ul_rpc.h" #include "usrloc.h" #include "reg_avps.h" MODULE_VERSION #define UID_COL "uid" #define CONTACT_COL "contact" #define EXPIRES_COL "expires" #define Q_COL "q" #define CALLID_COL "callid" #define CSEQ_COL "cseq" #define METHOD_COL "method" #define STATE_COL "state" #define FLAGS_COL "flags" #define USER_AGENT_COL "user_agent" #define RECEIVED_COL "received" #define INSTANCE_COL "instance" #define AOR_COL "aor" #define SERVER_ID_COL "server_id" static int mod_init(void); /* Module initialization function */ static void destroy(void); /* Module destroy function */ static void timer(unsigned int ticks, void* param); /* Timer handler */ static int child_init(int rank); /* Per-child init function */ extern int bind_usrloc(usrloc_api_t* api); /* * Module parameters and their default values */ str uid_col = STR_STATIC_INIT(UID_COL); /* Name of column containing usernames */ str contact_col = STR_STATIC_INIT(CONTACT_COL); /* Name of column containing contact addresses */ str expires_col = STR_STATIC_INIT(EXPIRES_COL); /* Name of column containing expires values */ str q_col = STR_STATIC_INIT(Q_COL); /* Name of column containing q values */ str callid_col = STR_STATIC_INIT(CALLID_COL); /* Name of column containing callid string */ str cseq_col = STR_STATIC_INIT(CSEQ_COL); /* Name of column containing cseq values */ str method_col = STR_STATIC_INIT(METHOD_COL); /* Name of column containing supported method */ str state_col = STR_STATIC_INIT(STATE_COL); /* Name of column containing contact state */ str flags_col = STR_STATIC_INIT(FLAGS_COL); /* Name of column containing flags */ str user_agent_col = STR_STATIC_INIT(USER_AGENT_COL); /* Name of column containing user agent string */ str received_col = STR_STATIC_INIT(RECEIVED_COL); /* Name of column containing transport info of REGISTER */ str instance_col = STR_STATIC_INIT(INSTANCE_COL); /* Name of column containing sip-instance parameter */ str aor_col = STR_STATIC_INIT(AOR_COL); /* Name of column containing address of record */ str server_id_col = STR_STATIC_INIT(SERVER_ID_COL); /* Name of column containing server id */ str db_url = STR_STATIC_INIT(DEFAULT_DB_URL); /* Database URL */ int timer_interval = 60; /* Timer interval in seconds */ int db_mode = 0; /* Database sync scheme: 0-no db, 1-write through, 2-write back */ int desc_time_order = 0; /* By default do not enable timestamp ordering */ int db_skip_delete = 0; /* Enable/disable contact deletion in database */ db_ctx_t* db = NULL; db_cmd_t** del_contact = NULL; db_cmd_t** ins_contact = NULL; int cmd_n = 0, cur_cmd = 0; static char *reg_avp_flag_name = NULL; /* * Exported functions */ static cmd_export_t cmds[] = { {"ul_register_udomain", (cmd_function)register_udomain, 1, 0, 0}, {"ul_insert_urecord", (cmd_function)insert_urecord, 1, 0, 0}, {"ul_delete_urecord", (cmd_function)delete_urecord, 1, 0, 0}, {"ul_get_urecord", (cmd_function)get_urecord, 1, 0, 0}, {"ul_lock_udomain", (cmd_function)lock_udomain, 1, 0, 0}, {"ul_unlock_udomain", (cmd_function)unlock_udomain, 1, 0, 0}, {"ul_release_urecord", (cmd_function)release_urecord, 1, 0, 0}, {"ul_insert_ucontact", (cmd_function)insert_ucontact, 1, 0, 0}, {"ul_delete_ucontact", (cmd_function)delete_ucontact, 1, 0, 0}, {"ul_get_ucontact", (cmd_function)get_ucontact, 1, 0, 0}, {"ul_get_ucontact_by_inst", (cmd_function)get_ucontact_by_instance, 1, 0, 0}, {"ul_get_all_ucontacts", (cmd_function)get_all_ucontacts, 1, 0, 0}, {"ul_update_ucontact", (cmd_function)update_ucontact, 1, 0, 0}, {"ul_register_watcher", (cmd_function)register_watcher, 1, 0, 0}, {"ul_unregister_watcher", (cmd_function)unregister_watcher, 1, 0, 0}, {"ul_bind_usrloc", (cmd_function)bind_usrloc, 1, 0, 0}, {"ul_register_ulcb", (cmd_function)register_ulcb, 1, 0, 0}, {"read_reg_avps", read_reg_avps, 2, read_reg_avps_fixup, REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE }, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {"uid_column", PARAM_STR, &uid_col }, {"contact_column", PARAM_STR, &contact_col }, {"expires_column", PARAM_STR, &expires_col }, {"q_column", PARAM_STR, &q_col }, {"callid_column", PARAM_STR, &callid_col }, {"cseq_column", PARAM_STR, &cseq_col }, {"method_column", PARAM_STR, &method_col }, {"flags_column", PARAM_STR, &flags_col }, {"db_url", PARAM_STR, &db_url }, {"timer_interval", PARAM_INT, &timer_interval }, {"db_mode", PARAM_INT, &db_mode }, {"desc_time_order", PARAM_INT, &desc_time_order}, {"user_agent_column", PARAM_STR, &user_agent_col }, {"received_column", PARAM_STR, &received_col }, {"instance_column", PARAM_STR, &instance_col }, {"aor_column", PARAM_STR, &aor_col }, {"reg_avp_column", PARAM_STRING, &avp_column }, {"reg_avp_flag", PARAM_STRING, ®_avp_flag_name }, {"db_skip_delete", PARAM_INT, &db_skip_delete}, {0, 0, 0} }; struct module_exports exports = { "usrloc", cmds, /* Exported functions */ ul_rpc, /* RPC methods */ params, /* Export parameters */ mod_init, /* Module initialization function */ 0, /* Response function */ destroy, /* Destroy function */ 0, /* OnCancel function */ child_init /* Child initialization function */ }; /* * Module initialization function */ static int mod_init(void) { DBG("usrloc - initializing\n"); if ((db_mode < 0) || (db_mode >= UL_DB_MAX)) { ERR("Invalid database mode '%d'\n", db_mode); return -1; } /* Register cache timer */ register_timer(timer, 0, timer_interval); /* init the callbacks list */ if ( init_ulcb_list() < 0) { LOG(L_ERR, "ERROR: usrloc/callbacks initialization failed\n"); return -1; } set_reg_avpflag_name(reg_avp_flag_name); return 0; } static int build_db_cmds(void) { db_fld_t del_contact_match[] = { {.name = uid_col.s, .type = DB_STR}, {.name = contact_col.s, .type = DB_STR}, {.name = NULL}, }; db_fld_t ins_contact_values[] = { {.name = uid_col.s, .type = DB_STR}, {.name = contact_col.s, .type = DB_STR}, {.name = expires_col.s, .type = DB_DATETIME}, {.name = q_col.s, .type = DB_DOUBLE}, {.name = callid_col.s, .type = DB_STR}, {.name = cseq_col.s, .type = DB_INT}, {.name = flags_col.s, .type = DB_BITMAP}, {.name = user_agent_col.s, .type = DB_STR}, {.name = received_col.s, .type = DB_STR}, {.name = instance_col.s, .type = DB_STR}, {.name = aor_col.s, .type = DB_STR}, {.name = server_id_col.s, .type = DB_INT}, {.name = avp_column, .type = DB_STR}, /* Must be the last element in the array */ {.name = NULL}, }; dlist_t* ptr; int i; INFO("usrloc: build_db_cmds()\n"); for(cmd_n = 0, ptr = root; ptr; cmd_n++, ptr = ptr->next); del_contact = pkg_malloc(cmd_n); if (del_contact == NULL) { ERR("No memory left\n"); return -1; } memset(del_contact, '\0', sizeof(del_contact) * cmd_n); ins_contact = pkg_malloc(cmd_n); if (ins_contact == NULL) { ERR("No memory left\n"); return -1; } memset(ins_contact, '\0', sizeof(ins_contact) * cmd_n); INFO("usrloc: building del_contact queries()\n"); for(i = 0, ptr = root; ptr; ptr = ptr->next, i++) { del_contact[i] = db_cmd(DB_DEL, db, ptr->name.s, NULL, del_contact_match, NULL); if (del_contact[i] == NULL) return -1; } INFO("usrloc: building inst_contact queries()\n"); for(i = 0, ptr = root; ptr; ptr = ptr->next, i++) { ins_contact[i] = db_cmd(DB_PUT, db, ptr->name.s, NULL, NULL, ins_contact_values); if (ins_contact[i] == NULL) return -1; } return 0; } static int child_init(int _rank) { INFO("usrloc: child_init( rank: %d)\n", _rank); if (_rank==PROC_INIT || _rank==PROC_MAIN || _rank==PROC_TCP_MAIN) { INFO("usrloc: do nothing for the init, main or tcp_main processes\n"); return 0; /* do nothing for the main or tcp_main processes */ } INFO("usrloc: db_mode = %d\n", db_mode); /* Shall we use database ? */ if ( db_mode != NO_DB) { /* Yes */ db = db_ctx("usrloc"); if (db == NULL) { ERR("Error while initializing database layer\n"); return -1; } if (db_add_db(db, db_url.s) < 0) return -1; if (db_connect(db) < 0) return -1; if (build_db_cmds() < 0) return -1; } INFO("usrloc: child_init( rank: %d), done OK\n", _rank); return 0; } /* * Module destroy function */ static void destroy(void) { int i; /* Parent only, synchronize the world * and then nuke it */ if (is_main) { if (db && synchronize_all_udomains() != 0) { LOG(L_ERR, "destroy(): Error while flushing cache\n"); } free_all_udomains(); } if (del_contact) { for(i = 0; i < cmd_n; i++) { if (del_contact[i]) db_cmd_free(del_contact[i]); } pkg_free(del_contact); } if (ins_contact) { for(i = 0; i < cmd_n; i++) { if (ins_contact[i]) db_cmd_free(ins_contact[i]); } pkg_free(ins_contact); } if (db) db_ctx_free(db); /* free callbacks list */ destroy_ulcb_list(); } /* * Timer handler */ static void timer(unsigned int ticks, void* param) { if (synchronize_all_udomains() != 0) { LOG(L_ERR, "timer(): Error while synchronizing cache\n"); } } kamailio-4.0.4/obsolete/usrloc/ul_callback.c0000644000000000000000000000535312223032460017526 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2004-03-16 created (bogdan) */ #include #include "../../dprint.h" #include "../../error.h" #include "../../mem/shm_mem.h" #include "ul_callback.h" struct ulcb_head_list* ulcb_list = 0; int init_ulcb_list() { ulcb_list = (struct ulcb_head_list*)shm_malloc ( sizeof(struct ulcb_head_list) ); if (ulcb_list==0) { LOG(L_CRIT,"ERROR:usrloc:init_ulcb_list: no more shared mem\n"); return -1; } ulcb_list->first = 0; ulcb_list->reg_types = 0; return 1; } void destroy_ulcb_list() { struct ul_callback *cbp, *cbp_tmp; if (!ulcb_list) return; for( cbp=ulcb_list->first; cbp ; ) { cbp_tmp = cbp; cbp = cbp->next; if (cbp_tmp->param) shm_free( cbp_tmp->param ); shm_free( cbp_tmp ); } shm_free(ulcb_list); } /* register a callback function 'f' for 'types' mask of events; */ int register_ulcb( int types, ul_cb f, void *param ) { struct ul_callback *cbp; /* are the callback types valid?... */ if ( types<0 || types>ULCB_MAX ) { LOG(L_CRIT, "BUG:usrloc:register_ulcb: invalid callback types: " "mask=%d\n",types); return E_BUG; } /* we don't register null functions */ if (f==0) { LOG(L_CRIT, "BUG:usrloc:register_ulcb: null callback function\n"); return E_BUG; } /* build a new callback structure */ if (!(cbp=(struct ul_callback*)shm_malloc(sizeof( struct ul_callback)))) { LOG(L_ERR, "ERROR:usrloc:register_ulcb: out of shm. mem\n"); return E_OUT_OF_MEM; } /* link it into the proper place... */ cbp->next = ulcb_list->first; ulcb_list->first = cbp; ulcb_list->reg_types |= types; /* ... and fill it up */ cbp->callback = f; cbp->param = param; cbp->types = types; if (cbp->next) cbp->id = cbp->next->id+1; else cbp->id = 0; return 1; } kamailio-4.0.4/obsolete/usrloc/hslot.c0000644000000000000000000000422112223032460016414 0ustar rootroot/* * $Id$ * * Hash table collision slot related functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "hslot.h" /* * Initialize cache slot structure */ int init_slot(struct udomain* _d, hslot_t* _s) { _s->n = 0; _s->first = 0; _s->last = 0; _s->d = _d; return 0; } /* * Deinitialize given slot structure */ void deinit_slot(hslot_t* _s) { struct urecord* ptr; /* Remove all elements */ while(_s->first) { ptr = _s->first; _s->first = _s->first->s_ll.next; free_urecord(ptr); } _s->n = 0; _s->last = 0; _s->d = 0; } /* * Add an element to an slot's linked list */ void slot_add(hslot_t* _s, struct urecord* _r) { if (_s->n == 0) { _s->first = _s->last = _r; } else { _r->s_ll.prev = _s->last; _s->last->s_ll.next = _r; _s->last = _r; } _s->n++; _r->slot = _s; } /* * Remove an element from slot linked list */ void slot_rem(hslot_t* _s, struct urecord* _r) { if (_r->s_ll.prev) { _r->s_ll.prev->s_ll.next = _r->s_ll.next; } else { _s->first = _r->s_ll.next; } if (_r->s_ll.next) { _r->s_ll.next->s_ll.prev = _r->s_ll.prev; } else { _s->last = _r->s_ll.prev; } _r->s_ll.prev = _r->s_ll.next = 0; _r->slot = 0; _s->n--; } kamailio-4.0.4/obsolete/usrloc/Makefile0000644000000000000000000000044412223032460016562 0ustar rootroot# $Id$ # # Usrloc module Makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=usrloc.so LIBS= DEFS+=-DSER_MOD_INTERFACE SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/usrloc/reg_avps_db.h0000644000000000000000000000033212223032460017542 0ustar rootroot#ifndef _REG_AVPS_DB_H #define _REG_AVPS_DB_H #include "../../str.h" #include "../../usr_avp.h" avp_t *deserialize_avps(str *serialized_avps); int serialize_avps(avp_t *first, str *dst); #endif /* _REG_AVPS_DB_H */ kamailio-4.0.4/obsolete/msilo/0000755000000000000000000000000012223032460014734 5ustar rootrootkamailio-4.0.4/obsolete/msilo/ms_msg_list.h0000644000000000000000000000402512223032460017426 0ustar rootroot/** * $Id$ * * MSILO module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2003-03-11 major locking changes, now it uses locking.h (andrei) */ #ifndef _MS_MSG_LIST_H_ #define _MS_MSG_LIST_H_ #include "../../locking.h" #define MS_MSG_NULL 0 #define MS_MSG_SENT 1 #define MS_MSG_DONE 4 #define MS_MSG_ERRO 8 #define MS_SEM_SENT 0 #define MS_SEM_DONE 1 #define MSG_LIST_OK 0 #define MSG_LIST_ERR -1 #define MSG_LIST_EXIST 1 typedef struct _msg_list_el { int msgid; int flag; struct _msg_list_el * prev; struct _msg_list_el * next; } t_msg_list_el, *msg_list_el; typedef struct _msg_list { int nrsent; int nrdone; msg_list_el lsent; msg_list_el ldone; gen_lock_t sem_sent; gen_lock_t sem_done; } t_msg_list, *msg_list; msg_list_el msg_list_el_new(); void msg_list_el_free(msg_list_el); void msg_list_el_free_all(msg_list_el); msg_list msg_list_init(); void msg_list_free(msg_list); int msg_list_check_msg(msg_list, int); int msg_list_set_flag(msg_list, int, int); int msg_list_check(msg_list); msg_list_el msg_list_reset(msg_list); #endif kamailio-4.0.4/obsolete/msilo/doc/0000755000000000000000000000000012223032460015501 5ustar rootrootkamailio-4.0.4/obsolete/msilo/doc/functions.xml0000644000000000000000000000510012223032460020227 0ustar rootroot
Functions
<function>m_store(mode, next_hop)</function> The method stores certain parts of the current SIP request (it should be called when the request type is MESSAGE and the destination user is offline or his UA does not support MESSAGE requests). If the user is registered with a UA which does not support MESSAGE requests you should not use mode="0" if you have changed the request uri with the contact address of user's UA. Meaning of the parameters is as follows: mode - specifies what to save as R-URI. "0" - first check if new_uri is an address of record. If yes, then use it and store it as R-URI, otherwise look at R-URI and, if necessary, at URI from "To" header. "1" - look first at R-URI and then at URI from "To" header. "2" - look only at URI form "To" header. next_hop (optional) - specifies next hop for sending outgoing messages (use as "outbound proxy"). Its value can be unset, empty or set to a sip URI like "sip:127.0.0.1:5060".
<function>m_dump(next_hop)</function> The method sends stored messages for the SIP user that is going to register to his actual contact address. The method should be called when a REGISTER request is received and the "Expire" header has a value greater than zero. The parameter can contain machine used as "outbound proxy" or can be empty. Meaning of the parameters is as follows: next_hop (optional) - specifies next hop for sending outgoing messages (use as "outbound proxy"). Its value can be unset, empty or set to a sip URI like "sip:127.0.0.1:5060".
kamailio-4.0.4/obsolete/msilo/doc/msilo.xml0000644000000000000000000000611412223032460017350 0ustar rootroot
Daniel-Constantin Mierla FhG FOKUS
mierla@fokus.fraunhofer.de
2003 FhG FOKUS
Msilo Module
Overview This modules provides offline message storage for SER. It stores received messages for an offline user and sends them when the user becomes online. For each message, the modules stores "Request-URI" ("R-URI") only if it is a complete address of record ("username@hostname"), URI from "To" header, URI from "From" header, incoming time, expiration time, content type and body of the message. If "R-URI" is not an address of record (it might be the contact address for current SIP session) the URI from "To" header will be used as R-URI. When the expiration time passed, the message is discarded from database. Expiration time is computed based on incoming time and one of the module's parameters. Every time when a user registers with SER, the module is looking in database for offline messages intended for that user. All of them will be sent to contact address provided in REGISTER request. It may happen the SIP user to be registered but his SIP User Agent to have no support for MESSAGE request. In this case it should be used the "failure_route" to store the undelivered requests.
Dependencies
SER modules The following modules must be loaded before this module: database module - mysql, dbtext or other module that implements the "db" interface and provides support for storing/receiving data to/from a database system. tm - Transaction module is used to send SIP requests.
Installation And Running
SER Configuration File Configuration example for msilo module. SER Configuration Example
kamailio-4.0.4/obsolete/msilo/doc/msilo.cfg0000644000000000000000000000741112223032460017310 0ustar rootroot# # MSILO usage example # # debug=9 # debug level (cmd line: -dddddddddd) fork=no # don't fork log_stderror=yes # log to stderr (cmd line: -E) children=2 # number of children check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 listen=127.0.0.1 # listen address # ------------------ module loading ---------------------------------- loadmodule "../sip_router/modules/print/print.so" loadmodule "../sip_router/modules/textops/textops.so" loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/mysql/mysql.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/msilo/msilo.so" loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/registrar/registrar.so" loadmodule "../sip_router/modules/usrloc/usrloc.so" # ----------------- setting module-specific parameters --------------- # -- registrar params -- modparam("registrar", "default_expires", 120) # -- registrar params -- modparam("usrloc", "db_mode", 0) # -- msilo params -- modparam("msilo","db_url","mysql://user:xxx@127.0.0.1/msilo") modparam("msilo","registrar","sip:registrar@mydomain.com") # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 15 ) modparam("tm", "wt_timer", 10 ) route{ if ( !mf_process_maxfwd_header("10") ) { sl_send_reply("483","To Many Hops"); drop(); }; if (uri==myself) { # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { save("location"); log("REGISTER received -> dumping messages with MSILO\n"); # MSILO - dumping user's offline messages if (m_dump()) { log("MSILO: offline messages dumped - if they were\n"); }else{ log("MSILO: no offline messages dumped\n"); }; break; }; # domestic SIP destinations are handled using our USRLOC DB if(!lookup("location")) { if (! t_newtran()) { sl_reply_error(); break; }; # we do not care about anything else but MESSAGEs if (!method=="MESSAGE") { if (!t_reply("404", "Not found")) { sl_reply_error(); }; break; }; log("MESSAGE received -> storing using MSILO\n"); # MSILO - storing as offline message if (m_store("0")) { log("MSILO: offline message stored\n"); if (!t_reply("202", "Accepted")) { sl_reply_error(); }; }else{ log("MSILO: offline message NOT stored\n"); if (!t_reply("503", "Service Unavailable")) { sl_reply_error(); }; }; break; }; # if the downstream UA does not support MESSAGE requests # go to failure_route[1] t_on_failure("1"); t_relay(); break; }; # forward anything else t_relay(); } failure_route[1] { # forwarding failed -- check if the request was a MESSAGE if (!method=="MESSAGE") { break; }; log(1,"MSILO:the downstream UA doesn't support MESSAGEs\n"); # we have changed the R-URI with the contact address, ignore it now if (m_store("1")) { log("MSILO: offline message stored\n"); t_reply("202", "Accepted"); }else{ log("MSILO: offline message NOT stored\n"); t_reply("503", "Service Unavailable"); }; } kamailio-4.0.4/obsolete/msilo/doc/Makefile0000644000000000000000000000012612223032460017140 0ustar rootrootdocs = msilo.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/msilo/doc/params.xml0000644000000000000000000000733612223032460017517 0ustar rootroot
Parameters
<varname>db_url</varname> (string) Database URL. Default value is "mysql://root@localhost/msilo". Set the "db_url" parameter ... modparam("msilo", "db_url", "mysql://user:passwd@host.com/dbname") ...
<varname>db_table</varname> (string) The name of table where to store the messages. Default value is "silo". Set the "db_table" parameter ... modparam("msilo", "db_table", "silo") ...
<varname>registrar</varname> (string) The SIP address used to inform users that destination of their message is not online and the message will be delivered next time when that user goes online. If the parameter is not set, the module will not send any notification. All requests intended for this SIP address will not be stored for lately delivery. Default value is "NULL". Set the <varname>registrar</varname> parameter ... modparam("msilo", "registrar", "sip:registrar@iptel.org") ...
<varname>expire_time</varname> (int) Expire time of stored messages - seconds. When this time passed, the message is silently discarded from database. Default value is 259200 (72 hours = 3 days). Set the <varname>expire_time</varname> parameter ... modparam("msilo", "expire_time", 36000) ...
<varname>check_time</varname> (int) Timer interval to check if dumped messages are sent OK - seconds. The module keeps each request send by itself for a new online user and if the reply is 2xx then the message is deleted from database. Default value is 30. Set the <varname>check_time</varname> parameter ... modparam("msilo", "check_time", 10) ...
<varname>clean_period</varname> (int) Number of check_time cycles when to check if there are expired messages in database. Default value is 5. Set the <varname>clean_period</varname> parameter ... modparam("msilo", "clean_period", "3") ...
<varname>use_contact</varname> (int) Turns on/off the usage of the Contact address to send notification back to sender whose message is stored by MSILO. Default value is 1 (0 = off, 1 = on). Set the <varname>param_name</varname> parameter ... modparam("msilo", "use_contact", 0) ...
kamailio-4.0.4/obsolete/msilo/ser-msilo.cfg0000644000000000000000000000052512223032460017331 0ustar rootroot# # Minimalistic SER configuration file for the purpose of # testing msilo module. # debug = 4 fork = no children = 1 log_stderror = yes listen=127.0.0.1 loadpath "./modules" loadmodule "mysql" loadmodule "sl" loadmodule "tm" loadmodule "msilo" modparam("msilo", "db_url", "mysql://ser:heslo@localhost/ser") route { m_dump(); break; } kamailio-4.0.4/obsolete/msilo/README0000644000000000000000000002314412223032460015620 0ustar rootroot1. Msilo Module Daniel-Constantin Mierla FhG FOKUS Copyright © 2003 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.2. Dependencies 1.2.1. SER modules 1.3. Installation And Running 1.3.1. SER Configuration File 1.4. Parameters 1.4.1. db_url (string) 1.4.2. db_table (string) 1.4.3. registrar (string) 1.4.4. expire_time (int) 1.4.5. check_time (int) 1.4.6. clean_period (int) 1.4.7. use_contact (int) 1.5. Functions 1.5.1. m_store(mode, next_hop) 1.5.2. m_dump(next_hop) 1.1. Overview This modules provides offline message storage for SER. It stores received messages for an offline user and sends them when the user becomes online. For each message, the modules stores "Request-URI" ("R-URI") only if it is a complete address of record ("username@hostname"), URI from "To" header, URI from "From" header, incoming time, expiration time, content type and body of the message. If "R-URI" is not an address of record (it might be the contact address for current SIP session) the URI from "To" header will be used as R-URI. When the expiration time passed, the message is discarded from database. Expiration time is computed based on incoming time and one of the module's parameters. Every time when a user registers with SER, the module is looking in database for offline messages intended for that user. All of them will be sent to contact address provided in REGISTER request. It may happen the SIP user to be registered but his SIP User Agent to have no support for MESSAGE request. In this case it should be used the "failure_route" to store the undelivered requests. 1.2. Dependencies 1.2.1. SER modules The following modules must be loaded before this module: * database module - mysql, dbtext or other module that implements the "db" interface and provides support for storing/receiving data to/from a database system. * tm - Transaction module is used to send SIP requests. 1.3. Installation And Running 1.3.1. SER Configuration File Configuration example for msilo module. Example 1. SER Configuration Example # # MSILO usage example # # debug=9 # debug level (cmd line: -dddddddddd) fork=no # don't fork log_stderror=yes # log to stderr (cmd line: -E) children=2 # number of children check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5060 listen=127.0.0.1 # listen address # ------------------ module loading ---------------------------------- loadmodule "../sip_router/modules/print/print.so" loadmodule "../sip_router/modules/textops/textops.so" loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/mysql/mysql.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/msilo/msilo.so" loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/registrar/registrar.so" loadmodule "../sip_router/modules/usrloc/usrloc.so" # ----------------- setting module-specific parameters --------------- # -- registrar params -- modparam("registrar", "default_expires", 120) # -- registrar params -- modparam("usrloc", "db_mode", 0) # -- msilo params -- modparam("msilo","db_url","mysql://user:xxx@127.0.0.1/msilo") modparam("msilo","registrar","sip:registrar@mydomain.com") # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 15 ) modparam("tm", "wt_timer", 10 ) route{ if ( !mf_process_maxfwd_header("10") ) { sl_send_reply("483","To Many Hops"); drop(); }; if (uri==myself) { # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { save("location"); log("REGISTER received -> dumping messages with MSILO\n"); # MSILO - dumping user's offline messages if (m_dump()) { log("MSILO: offline messages dumped - if they were\n"); }else{ log("MSILO: no offline messages dumped\n"); }; break; }; # domestic SIP destinations are handled using our USRLOC DB if(!lookup("location")) { if (! t_newtran()) { sl_reply_error(); break; }; # we do not care about anything else but MESSAGEs if (!method=="MESSAGE") { if (!t_reply("404", "Not found")) { sl_reply_error(); }; break; }; log("MESSAGE received -> storing using MSILO\n"); # MSILO - storing as offline message if (m_store("0")) { log("MSILO: offline message stored\n"); if (!t_reply("202", "Accepted")) { sl_reply_error(); }; }else{ log("MSILO: offline message NOT stored\n"); if (!t_reply("503", "Service Unavailable")) { sl_reply_error(); }; }; break; }; # if the downstream UA does not support MESSAGE requests # go to failure_route[1] t_on_failure("1"); t_relay(); break; }; # forward anything else t_relay(); } failure_route[1] { # forwarding failed -- check if the request was a MESSAGE if (!method=="MESSAGE") { break; }; log(1,"MSILO:the downstream UA doesn't support MESSAGEs\n"); # we have changed the R-URI with the contact address, ignore it now if (m_store("1")) { log("MSILO: offline message stored\n"); t_reply("202", "Accepted"); }else{ log("MSILO: offline message NOT stored\n"); t_reply("503", "Service Unavailable"); }; } 1.4. Parameters 1.4.1. db_url (string) Database URL. Default value is "mysql://root@localhost/msilo". Example 2. Set the "db_url" parameter ... modparam("msilo", "db_url", "mysql://user:passwd@host.com/dbname") ... 1.4.2. db_table (string) The name of table where to store the messages. Default value is "silo". Example 3. Set the "db_table" parameter ... modparam("msilo", "db_table", "silo") ... 1.4.3. registrar (string) The SIP address used to inform users that destination of their message is not online and the message will be delivered next time when that user goes online. If the parameter is not set, the module will not send any notification. All requests intended for this SIP address will not be stored for lately delivery. Default value is "NULL". Example 4. Set the registrar parameter ... modparam("msilo", "registrar", "sip:registrar@iptel.org") ... 1.4.4. expire_time (int) Expire time of stored messages - seconds. When this time passed, the message is silently discarded from database. Default value is 259200 (72 hours = 3 days). Example 5. Set the expire_time parameter ... modparam("msilo", "expire_time", 36000) ... 1.4.5. check_time (int) Timer interval to check if dumped messages are sent OK - seconds. The module keeps each request send by itself for a new online user and if the reply is 2xx then the message is deleted from database. Default value is 30. Example 6. Set the check_time parameter ... modparam("msilo", "check_time", 10) ... 1.4.6. clean_period (int) Number of check_time cycles when to check if there are expired messages in database. Default value is 5. Example 7. Set the clean_period parameter ... modparam("msilo", "clean_period", "3") ... 1.4.7. use_contact (int) Turns on/off the usage of the Contact address to send notification back to sender whose message is stored by MSILO. Default value is 1 (0 = off, 1 = on). Example 8. Set the param_name parameter ... modparam("msilo", "use_contact", 0) ... 1.5. Functions 1.5.1. m_store(mode, next_hop) The method stores certain parts of the current SIP request (it should be called when the request type is MESSAGE and the destination user is offline or his UA does not support MESSAGE requests). If the user is registered with a UA which does not support MESSAGE requests you should not use mode="0" if you have changed the request uri with the contact address of user's UA. Meaning of the parameters is as follows: * mode - specifies what to save as R-URI. + "0" - first check if new_uri is an address of record. If yes, then use it and store it as R-URI, otherwise look at R-URI and, if necessary, at URI from "To" header. + "1" - look first at R-URI and then at URI from "To" header. + "2" - look only at URI form "To" header. * next_hop (optional) - specifies next hop for sending outgoing messages (use as "outbound proxy"). Its value can be unset, empty or set to a sip URI like "sip:127.0.0.1:5060". 1.5.2. m_dump(next_hop) The method sends stored messages for the SIP user that is going to register to his actual contact address. The method should be called when a REGISTER request is received and the "Expire" header has a value greater than zero. The parameter can contain machine used as "outbound proxy" or can be empty. Meaning of the parameters is as follows: * next_hop (optional) - specifies next hop for sending outgoing messages (use as "outbound proxy"). Its value can be unset, empty or set to a sip URI like "sip:127.0.0.1:5060". kamailio-4.0.4/obsolete/msilo/ms_msg_list.c0000644000000000000000000001261412223032460017424 0ustar rootroot/* * $Id$ * * MSILO module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2003-03-11 major locking changes: not it uses locking.h (andrei) */ #include #include #include #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "../../dprint.h" #include "ms_msg_list.h" /** * create a new element */ msg_list_el msg_list_el_new() { msg_list_el mle = NULL; mle = (msg_list_el)shm_malloc(sizeof(t_msg_list_el)); if(mle == NULL) return NULL; mle->next = NULL; mle->prev = NULL; mle->msgid = 0; mle->flag = MS_MSG_NULL; return mle; } /** * free an element */ void msg_list_el_free(msg_list_el mle) { if(mle) shm_free(mle); } /** * free a list of elements */ void msg_list_el_free_all(msg_list_el mle) { msg_list_el p0, p1; if(!mle) return; p0 = mle; while(p0) { p1 = p0; p0 = p0->next; msg_list_el_free(p1); } } /** * init a list */ msg_list msg_list_init() { msg_list ml = NULL; ml = (msg_list)shm_malloc(sizeof(t_msg_list)); if(ml == NULL) return NULL; /* init locks */ if (lock_init(&ml->sem_sent)==0){ LOG(L_CRIT, "msilo: could not initialize a lock\n"); goto clean; }; if (lock_init(&ml->sem_done)==0){ LOG(L_CRIT, "msilo: could not initialize a lock\n"); lock_destroy(&ml->sem_sent); goto clean; }; ml->nrsent = 0; ml->nrdone = 0; ml->lsent = NULL; ml->ldone = NULL; return ml; clean: shm_free(ml); return NULL; } /** * free a list */ void msg_list_free(msg_list ml) { msg_list_el p0, p1; if(!ml) return; lock_destroy(&ml->sem_sent); lock_destroy(&ml->sem_done); if(ml->nrsent>0 && ml->lsent) { // free sent list p0 = ml->lsent; ml->lsent = NULL; ml->nrsent = 0; while(p0) { p1 = p0->next; msg_list_el_free(p0); p0 = p1; } } if(ml->nrdone>0 && ml->ldone) { // free done list p0 = ml->ldone; ml->ldone = NULL; ml->nrdone = 0; while(p0) { p1 = p0->next; msg_list_el_free(p0); p0 = p1; } } shm_free(ml); } /** * check if a message is in list */ int msg_list_check_msg(msg_list ml, int mid) { msg_list_el p0, p1; if(!ml || mid==0) goto errorx; DBG("MSILO:msg_list_check_msg: checking msgid=%d\n", mid); lock_get(&ml->sem_sent); p0 = p1 = ml->lsent; while(p0) { if(p0->msgid==mid) goto exist; p1 = p0; p0 = p0->next; } p0 = msg_list_el_new(); if(!p0) { DBG("MSILO:msg_list_check_msg: Error creating new msg elem.\n"); goto error; } p0->msgid = mid; p0->flag |= MS_MSG_SENT; if(p1) { p1->next = p0; p0->prev = p1; goto done; } ml->lsent = p0; done: ml->nrsent++; lock_release(&ml->sem_sent); DBG("MSILO:msg_list_check_msg: msg added to sent list.\n"); return MSG_LIST_OK; exist: lock_release(&ml->sem_sent); DBG("MSILO:msg_list_check_msg: msg already in sent list.\n"); return MSG_LIST_EXIST; error: lock_release(&ml->sem_sent); errorx: return MSG_LIST_ERR; } /** * set flag for message with mid */ int msg_list_set_flag(msg_list ml, int mid, int fl) { msg_list_el p0; if(!ml || mid==0) goto errorx; lock_get(&ml->sem_sent); p0 = ml->lsent; while(p0) { if(p0->msgid==mid) { p0->flag |= fl; DBG("MSILO: msg_list_set_flag: mid:%d fl:%d\n", p0->msgid, fl); goto done; } p0 = p0->next; } done: lock_release(&ml->sem_sent); return MSG_LIST_OK; errorx: return MSG_LIST_ERR; } /** * check if the messages from list were sent */ int msg_list_check(msg_list ml) { msg_list_el p0; msg_list_el p0_next; if(!ml) goto errorx; lock_get(&ml->sem_sent); if(ml->nrsent<=0) goto done; lock_get(&ml->sem_done); p0 = ml->lsent; while(p0) { p0_next = p0->next; if(p0->flag & MS_MSG_DONE || p0->flag & MS_MSG_ERRO) { DBG("MSILO: msg_list_check: mid:%d got reply\n", p0->msgid); if(p0->prev) (p0->prev)->next = p0->next; else ml->lsent = p0->next; if(p0->next) (p0->next)->prev = p0->prev; ml->nrsent--; if(!ml->nrsent) ml->lsent = NULL; if(ml->ldone) (ml->ldone)->prev = p0; p0->next = ml->ldone; p0->prev = NULL; ml->ldone = p0; ml->nrdone++; } p0 = p0_next; } lock_release(&ml->sem_done); done: lock_release(&ml->sem_sent); return MSG_LIST_OK; errorx: return MSG_LIST_ERR; } /** * reset a list * return old list */ msg_list_el msg_list_reset(msg_list ml) { msg_list_el p0; if(!ml) return NULL; lock_get(&ml->sem_done); p0 = ml->ldone; ml->ldone = NULL; ml->nrdone = 0; lock_release(&ml->sem_done); return p0; } kamailio-4.0.4/obsolete/msilo/msfuncs.c0000644000000000000000000001221612223032460016560 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "msfuncs.h" #include #include #include #include #include #include "../../dprint.h" #include "../../config.h" #include "../../ut.h" #include "../../forward.h" #include "../../resolve.h" #include "../../globals.h" #include "../../udp_server.h" #include "../../pt.h" #define CONTACT_PREFIX "Contact: <" #define CONTACT_SUFFIX ">;msilo=yes"CRLF #define CONTACT_PREFIX_LEN (sizeof(CONTACT_PREFIX)-1) #define CONTACT_SUFFIX_LEN (sizeof(CONTACT_SUFFIX)-1) #define EAT_SPACES(_p, _e) \ while((*(_p)) && ((_p) <= (_e)) && (*(_p)==' '\ || *(_p)=='\t')) (_p)++; \ if((_p)>(_e)) return -2 #define SKIP_CHARS(_p, _n, _e) \ if( (_p)+(_n) < (_e) ) (_p) += (_n); \ else goto error #define NEXT_SEP(_p, _pos, _e) \ (_pos) = 0; \ while( (*((_p)+(_pos))) && ((_p)+(_pos) <= (_e)) && \ (*((_p)+(_pos)) != ' ') \ && (*((_p)+(_pos)) != '\t') && (*((_p)+(_pos)) != '=') \ && (*((_p)+(_pos)) != ';') && (*((_p)+(_pos)) != '\n')) \ (_pos)++; \ if((_p)+(_pos) > (_e)) goto error /** * apostrophes escaping * - src: source buffer * - slen: length of source buffer * - dst: destination buffer * - dlen: max length of destination buffer * #return: destination length => OK; -1 => error */ int m_apo_escape(char* src, int slen, char* dst, int dlen) { int i, j; if(!src || !dst || dlen <= 0) return -1; if(slen == -1) slen = strlen(src); for(i=j=0; i=dlen) return -2; memcpy(&dst[j], "\\'", 2); j += 2; break; default: if(j+1>=dlen) return -2; dst[j] = src[i]; j++; } } dst[j] = '\0'; return j; } /** * extract the value of Content-Type header * - src: pointer to C-T content * - len: length of src * - ctype: parsed C-T * - flag: what to parse - bit mask of CT_TYPE, CT_CHARSET, CT_MSGR * * #return: 0 OK ; -1 error */ int m_extract_content_type(char* src, int len, t_content_type* ctype, int flag) { char *p, *end; int f = 0, pos; if( !src || len <=0 ) goto error; p = src; end = p + len; while((p < end) && f != flag) { EAT_SPACES(p, end); if((flag & CT_TYPE) && !(f & CT_TYPE)) { NEXT_SEP(p, pos, end); if(p[pos] == ';') { ctype->type.s = p; ctype->type.len = pos; SKIP_CHARS(p, pos+1, end); f |= CT_TYPE; continue; } } if((flag & CT_CHARSET) && !(f & CT_CHARSET)) { } if((flag & CT_MSGR) && !(f & CT_MSGR)) { } } return 0; error: return -1; } /** build MESSAGE headers * * only Content-Type at this moment * expects - max buf len of the resulted body in body->len * - body->s MUST be allocated * #return: 0 OK ; -1 error * */ int m_build_headers(str *buf, str ctype, str contact) { char *p; if(!buf || !buf->s || buf->len <= 0 || ctype.len < 0 || contact.len < 0 || buf->len <= ctype.len+contact.len+14 /*Content-Type: */ +CRLF_LEN+CONTACT_PREFIX_LEN+CONTACT_SUFFIX_LEN) goto error; p = buf->s; if(ctype.len > 0) { strncpy(p, "Content-Type: ", 14); p += 14; strncpy(p, ctype.s, ctype.len); p += ctype.len; strncpy(p, CRLF, CRLF_LEN); p += CRLF_LEN; } if(contact.len > 0) { strncpy(p, CONTACT_PREFIX, CONTACT_PREFIX_LEN); p += CONTACT_PREFIX_LEN; strncpy(p, contact.s, contact.len); p += contact.len; strncpy(p, CONTACT_SUFFIX, CONTACT_SUFFIX_LEN); p += CONTACT_SUFFIX_LEN; } buf->len = p - buf->s; return 0; error: return -1; } /** build MESSAGE body --- add incoming time and 'from' * * expects - max buf len of the resulted body in body->len * - body->s MUST be allocated * #return: 0 OK ; -1 error * */ int m_build_body(str *body, time_t date, str msg) { char *p; if(!body || !(body->s) || body->len <= 0 || date < 0 || msg.len < 0 || (46+msg.len > body->len) ) goto error; p = body->s; strncpy(p, "[Offline message - ", 19); p += 19; strncpy(p, ctime(&date), 24); p += 24; /** if(from.len > 0) { *p++ = ' '; strncpy(p, from.s, from.len); p += from.len; } **/ *p++ = ']'; if(msg.len > 0) { *p++ = ' '; strncpy(p, msg.s, msg.len); p += msg.len; } body->len = p - body->s; return 0; error: return -1; } kamailio-4.0.4/obsolete/msilo/msfuncs.h0000644000000000000000000000325312223032460016566 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _MSFUNCS_H_ #define _MSFUNCS_H_ #include #include "../../str.h" #define CT_TYPE 1 #define CT_CHARSET 2 #define CT_MSGR 4 #ifdef MSILO_TAG #undef MSILO_TAG #endif #define MSILO_TAG "msilo-HI4U-Ah0X-bZ98-" typedef struct _content_type { str type; str charset; str msgr; } t_content_type; /** apostrophes escape - useful for MySQL strings */ int m_apo_escape(char*, int, char*, int); /** extract content-type value */ int m_extract_content_type(char*, int, t_content_type*, int); /** build MESSAGE headers */ int m_build_headers(str *buf, str ctype, str contact); /** build MESSAGE body */ int m_build_body(str *body, time_t date, str msg); #endif kamailio-4.0.4/obsolete/msilo/Makefile0000644000000000000000000000044412223032460016376 0ustar rootroot# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=msilo.so LIBS= DEFS+=-DSER_MOD_INTERFACE SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/msilo/msilo.c0000644000000000000000000004622012223032460016227 0ustar rootroot/* * $Id$ * * MSILO module * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History * ------- * * 2003-01-23: switched from t_uac to t_uac_dlg (dcm) * 2003-02-28: protocolization of t_uac_dlg completed (jiri) * 2003-03-11: updated to the new module interface (andrei) * removed non-constant initializers to some strs (andrei) * 2003-03-16: flags parameter added (janakj) * 2003-04-05: default_uri #define used (jiri) * 2003-04-06: db_init removed from mod_init, will be called from child_init * now (janakj) * 2003-04-07: m_dump takes a parameter which sets the way the outgoing URI * is computed (dcm) * 2003-08-05 adapted to the new parse_content_type_hdr function (bogdan) * 2004-06-07 updated to the new DB api (andrei) */ #include #include #include #include #include #include #include #include #include "../../sr_module.h" #include "../../dprint.h" #include "../../ut.h" #include "../../timer.h" #include "../../mem/shm_mem.h" #include "../../lib/srdb2/db.h" #include "../../parser/parse_from.h" #include "../../parser/parse_content.h" #include "../../parser/contact/parse_contact.h" #include "../../resolve.h" #include "../../id.h" #include "../../modules/tm/tm_load.h" #define CONTACT_PREFIX "Content-Type: text/plain"CRLF"Contact: <" #define CONTACT_SUFFIX ">;msilo=yes"CRLF #define CONTACT_PREFIX_LEN (sizeof(CONTACT_PREFIX)-1) #define CONTACT_SUFFIX_LEN (sizeof(CONTACT_SUFFIX)-1) #define OFFLINE_MESSAGE "] is offline. The message will be delivered when user goes online." #define OFFLINE_MESSAGE_LEN (sizeof(OFFLINE_MESSAGE)-1) #include "ms_msg_list.h" #include "msfuncs.h" char *sc_mid = "mid"; /* 0 */ char *sc_from = "from_hdr"; /* 1 */ char *sc_to = "to_hdr"; /* 2 */ char *sc_ruri = "ruri"; /* 3 */ char *sc_uid = "uid"; /* 4 */ char *sc_body = "body"; /* 5 */ char *sc_ctype = "ctype"; /* 6 */ char *sc_exp_time = "exp_time"; /* 7 */ char *sc_inc_time = "inc_time"; /* 8 */ MODULE_VERSION /** database layer variables */ static db_ctx_t* ctx = NULL; static db_cmd_t* store = NULL; static db_cmd_t* load = NULL; static db_cmd_t* del_mid = NULL; static db_cmd_t* del_expired = NULL; /** precessed msg list - used for dumping the messages */ msg_list ml = NULL; /** TM bind */ struct tm_binds tmb; /** parameters */ char *ms_db_url=DEFAULT_DB_URL; char *ms_db_table="silo"; char *ms_registrar=NULL; //"sip:registrar@iptel.org"; int ms_expire_time=259200; int ms_check_time=30; int ms_clean_period=5; int ms_use_contact=1; str msg_type = STR_STATIC_INIT("MESSAGE"); str reg_addr; /** module functions */ static int mod_init(void); static int child_init(int); static int m_store(struct sip_msg*, char*, char*); static int m_dump(struct sip_msg*, char*, char*); static void destroy(void); void m_clean_silo(unsigned int ticks, void *); /** TM callback function */ static void m_tm_callback( struct cell *t, int type, struct tmcb_params *ps); static cmd_export_t cmds[]={ {"m_store", m_store, 2, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"m_store", m_store, 1, 0, REQUEST_ROUTE | FAILURE_ROUTE}, {"m_dump", m_dump, 1, 0, REQUEST_ROUTE}, {"m_dump", m_dump, 0, 0, REQUEST_ROUTE}, {0,0,0,0,0} }; static param_export_t params[]={ {"db_url", PARAM_STRING, &ms_db_url}, {"db_table", PARAM_STRING, &ms_db_table}, {"registrar", PARAM_STRING, &ms_registrar}, {"expire_time", PARAM_INT, &ms_expire_time}, {"check_time", PARAM_INT, &ms_check_time}, {"clean_period", PARAM_INT, &ms_clean_period}, {"use_contact", PARAM_INT, &ms_use_contact}, {"sc_mid", PARAM_STRING, &sc_mid}, {"sc_from", PARAM_STRING, &sc_from}, {"sc_to", PARAM_STRING, &sc_to}, {"sc_ruri", PARAM_STRING, &sc_ruri}, {"sc_uid", PARAM_STRING, &sc_uid}, {"sc_body", PARAM_STRING, &sc_body}, {"sc_ctype", PARAM_STRING, &sc_ctype}, {"sc_exp_time", PARAM_STRING, &sc_exp_time}, {"sc_inc_time", PARAM_STRING, &sc_inc_time}, {0,0,0} }; /** module exports */ struct module_exports exports= { "msilo", /* module id */ cmds, /* module's exported functions */ 0, /* RPC methods */ params, /* module's exported parameters */ mod_init, /* module initialization function */ (response_function) 0, /* response handler */ (destroy_function) destroy, /* module destroy function */ 0, child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { load_tm_f load_tm; DBG("MSILO: initializing ...\n"); /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) { LOG(L_ERR, "ERROR: msilo: mod_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) return -1; ml = msg_list_init(); if(!ml) { DBG("ERROR: msilo: mod_init: can't initialize msg list\n"); return -1; } register_timer( m_clean_silo, 0, ms_check_time); reg_addr.s = ms_registrar; reg_addr.len = (ms_registrar)?strlen(ms_registrar):0; return 0; } void msilo_db_close(void) { if (store) db_cmd_free(store); store = NULL; if (load) db_cmd_free(load); load = NULL; if (del_mid) db_cmd_free(del_mid); del_mid = NULL; if (del_expired) db_cmd_free(del_expired); del_expired = NULL; if (ctx) { db_disconnect(ctx); db_ctx_free(ctx); ctx = NULL; } } int msilo_db_init(char* db_url) { db_fld_t del_mid_param[] = { {.name = sc_mid, .type = DB_INT}, {.name = 0} }; db_fld_t del_expired_param[] = { {.name = sc_exp_time, .type = DB_DATETIME, .op = DB_LEQ}, {.name = 0} }; db_fld_t store_param[] = { {.name = sc_to, .type = DB_STR }, {.name = sc_from, .type = DB_STR }, {.name = sc_ruri, .type = DB_STR }, {.name = sc_uid, .type = DB_STR }, {.name = sc_body, .type = DB_BLOB }, {.name = sc_ctype, .type = DB_STR }, {.name = sc_exp_time, .type = DB_DATETIME}, {.name = sc_inc_time, .type = DB_DATETIME}, {.name = 0} }; db_fld_t load_match[] = { {.name = sc_uid, .type = DB_STR}, {.name = 0} }; db_fld_t load_cols[] = { {.name = sc_mid, .type = DB_INT}, {.name = sc_from, .type = DB_STR}, {.name = sc_to, .type = DB_STR}, {.name = sc_body, .type = DB_BLOB}, {.name = sc_ctype, .type = DB_STR}, {.name = sc_inc_time, .type = DB_DATETIME}, {.name = sc_ruri, .type = DB_STR}, {.name = 0} }; ctx = db_ctx("msilo"); if (!ctx) goto error; if (db_add_db(ctx, db_url) < 0) goto error; if (db_connect(ctx) < 0) goto error; store = db_cmd(DB_PUT, ctx, ms_db_table, NULL, NULL, store_param); if (!store) goto error; load = db_cmd(DB_GET, ctx, ms_db_table, load_cols, load_match, NULL); if (!store) goto error; del_mid = db_cmd(DB_DEL, ctx, ms_db_table, NULL, del_mid_param, NULL); if (!del_mid) goto error; del_expired = db_cmd(DB_DEL, ctx, ms_db_table, NULL, del_expired_param, NULL); if (!store) goto error; return 0; error: msilo_db_close(); ERR("msilo: Error while initializing database layer\n"); return -1; } /** * Initialize children */ static int child_init(int rank) { if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN) return 0; /* do nothing for the main or tcp_main processes */ DBG("MSILO: init_child #%d / pid <%d>\n", rank, getpid()); if (msilo_db_init(ms_db_url) < 0) return -1; return 0; } /** * destroy function */ static void destroy(void) { DBG("MSILO: destroy module ...\n"); msg_list_free(ml); msilo_db_close(); } /** * store message * mode = "0" -- look for outgoing URI starting with new_uri * = "1" -- look for outgoing URI starting with r-uri * = "2" -- look for outgoing URI only at to header * next_hop = parameter specifying next hop for outgoing messages (like outbound proxy) */ static int m_store(struct sip_msg* msg, char* str1, char* str2) { str body, str_hdr, ctaddr, uri, uid; struct to_body* to, *from; int val, lexpire; t_content_type ctype; static char buf[512]; static char buf1[1024]; int mime, mode; str next_hop = STR_NULL; uac_req_t uac_r; DBG("MSILO: m_store: ------------ start ------------\n"); if (!str1) { LOG(L_ERR, "MSILO:m_store: Invalid parameter value\n"); goto error; } mode = str1[0] - '0'; if (str2) { next_hop.s = str2; next_hop.len = strlen(str2); } if (get_to_uid(&uid, msg) < 0) { LOG(L_ERR, "MSILO:m_store: Unable to find out identity of user\n"); goto error; } /* get message body - after that whole SIP MESSAGE is parsed */ body.s = get_body( msg ); if (body.s==0) { LOG(L_ERR,"MSILO:m_store: ERROR cannot extract body from msg\n"); goto error; } /* content-length (if present) must be already parsed */ if (!msg->content_length) { LOG(L_ERR,"MSILO:m_store: ERROR no Content-Length header found!\n"); goto error; } body.len = get_content_length(msg); /* check if the body of message contains something */ if(body.len <= 0) { DBG("MSILO:m_store: body of the message is empty!\n"); goto error; } to = get_to(msg); if (!to) { LOG(L_ERR, "MSILO:m_store: Cannot get To header\n"); goto error; } if (parse_from_header(msg) < 0) { LOG(L_ERR, "MSILO:m_store: Error while Parsing From header\n"); goto error; } from = get_from(msg); if (!from) { LOG(L_ERR, "MSILO:m_store: Cannot find From header\n"); goto error; } store->vals[0].v.lstr = to->uri; store->vals[1].v.lstr = from->uri; switch(mode) { case 0: uri = *GET_RURI(msg); /* new_ruri, orig_ruri*/ break; case 1: /* orig_ruri */ uri = msg->first_line.u.request.uri; break; case 2: uri = to->uri; break; default: LOG(L_ERR, "MSILO:m_store: Unrecognized parameter value: %s\n", str1); goto error; } store->vals[2].v.lstr = uri; store->vals[3].v.lstr = uid; /* add the message's body in SQL query */ store->vals[4].v.blob = body; lexpire = ms_expire_time; /* add 'content-type' -- parse the content-type header */ if ((mime=parse_content_type_hdr(msg))<1 ) { LOG(L_ERR,"MSILO:m_store: ERROR cannot parse Content-Type header\n"); goto error; } store->vals[5].v.lstr.s = "text/plain"; store->vals[5].v.lstr.len = 10; /** check the content-type value */ if( mime!=(TYPE_TEXT<<16)+SUBTYPE_PLAIN && mime!=(TYPE_MESSAGE<<16)+SUBTYPE_CPIM ) { if(m_extract_content_type(msg->content_type->body.s, msg->content_type->body.len, &ctype, CT_TYPE) != -1) { DBG("MSILO:m_store: 'content-type' found\n"); store->vals[5].v.lstr = ctype.type; } } /* check 'expires' -- no more parsing - already done by get_body() */ if(msg->expires && msg->expires->body.len > 0) { DBG("MSILO:m_store: 'expires' found\n"); val = atoi(msg->expires->body.s); if(val > 0) lexpire = (ms_expire_time<=val)?ms_expire_time:val; } /* current time */ val = (int)time(NULL); /* add expiration time */ store->vals[6].v.time = val + lexpire; store->vals[7].v.time = val; if (db_exec(NULL, store) < 0) { LOG(L_ERR, "MSILO:m_store: error storing message\n"); goto error; } DBG("MSILO:m_store: message stored. uid:<%.*s> F:<%.*s>\n", uid.len, uid.s, from->uri.len, ZSW(from->uri.s)); if(reg_addr.len > 0 && reg_addr.len+CONTACT_PREFIX_LEN+CONTACT_SUFFIX_LEN+1<1024) { DBG("MSILO:m_store: sending info message.\n"); strcpy(buf1, CONTACT_PREFIX); strncat(buf1,reg_addr.s,reg_addr.len); strncat(buf1, CONTACT_SUFFIX, CONTACT_SUFFIX_LEN); str_hdr.len = CONTACT_PREFIX_LEN+reg_addr.len+CONTACT_SUFFIX_LEN; str_hdr.s = buf1; strncpy(buf, "User [", 6); body.len = 6; if(uri.len+OFFLINE_MESSAGE_LEN+7/*6+1*/ < 512) { strncpy(buf+body.len, uri.s, uri.len); body.len += uri.len; } strncpy(buf+body.len, OFFLINE_MESSAGE, OFFLINE_MESSAGE_LEN); body.len += OFFLINE_MESSAGE_LEN; body.s = buf; /* look for Contact header -- must be parsed by now*/ ctaddr.s = NULL; if(ms_use_contact && msg->contact!=NULL && msg->contact->body.s!=NULL && msg->contact->body.len > 0) { DBG("MSILO:m_store: contact header found\n"); if((msg->contact->parsed!=NULL && ((contact_body_t*)(msg->contact->parsed))->contacts!=NULL) || (parse_contact(msg->contact)==0 && msg->contact->parsed!=NULL && ((contact_body_t*)(msg->contact->parsed))->contacts!=NULL)) { DBG("MSILO:m_store: using contact header for info msg\n"); ctaddr.s = ((contact_body_t*)(msg->contact->parsed))->contacts->uri.s; ctaddr.len = ((contact_body_t*)(msg->contact->parsed))->contacts->uri.len; if(!ctaddr.s || ctaddr.len < 6 || strncmp(ctaddr.s, "sip:", 4) || ctaddr.s[4]==' ') ctaddr.s = NULL; else DBG("MSILO:m_store: feedback contact [%.*s]\n", ctaddr.len,ctaddr.s); } } set_uac_req(&uac_r, &msg_type, /* Type of the message */ &str_hdr, /* Optional headers including CRLF */ &body, /* Message body */ 0, /* dialog */ 0, /* callback flags */ 0, /* callback function */ 0 /* callback parameter */ ); tmb.t_request(&uac_r, (ctaddr.s)?&ctaddr:&from->uri, /* Request-URI */ &from->uri, /* To */ ®_addr, /* From */ next_hop.len ? &next_hop: NULL /* next hop */ ); } return 1; error: return -1; } /** * dump message */ static int m_dump(struct sip_msg* msg, char* str1, char* str2) { db_res_t* res = NULL; db_rec_t* rec; int i, mid, n; char hdr_buf[1024], body_buf[1024]; str str_vals[5], hdr_str , body_str, uid; time_t rtime; str next_hop = STR_NULL; uac_req_t uac_r; i=0; /* fix warning in DBG() */ if (str1) { next_hop.s = str1; next_hop.len = strlen(str1); } DBG("MSILO:m_dump: ------------ start ------------\n"); if (get_to_uid(&uid, msg) < 0) { LOG(L_ERR, "MSILO:m_dump: Unable to retrieve identity of user\n"); goto error; } hdr_str.s = hdr_buf; hdr_str.len = 1024; body_str.s = body_buf; body_str.len = 1024; /** * check if has expires=0 (REGISTER) */ if(parse_headers(msg, HDR_EXPIRES_F, 0) >= 0) { /* check 'expires' > 0 */ if(msg->expires && msg->expires->body.len > 0) { i = atoi(msg->expires->body.s); if(i <= 0) { /* user goes offline */ DBG("MSILO:m_dump: user <%.*s> goes offline - expires=%d\n", uid.len, uid.s, i); goto error; } else DBG("MSILO:m_dump: user <%.*s> online - expires=%d\n", uid.len, uid.s, i); } } else { DBG("MSILO:m_dump: 'expires' threw error at parsing\n"); goto error; } load->match[0].v.lstr = uid; if (db_exec(&res, load) < 0) { ERR("msilo: Error while loading messages from database\n"); goto error; } if (!res || !(rec = db_first(res))) { DBG("MSILO:m_dump: no stored message for <%.*s>!\n", STR_FMT(&uid)); goto done; } for(; rec; rec = db_next(res)) { if (rec->fld[0].flags & DB_NULL) { ERR("msilo: Database returned message with NULL msgid, skipping\n"); continue; } mid = rec->fld[0].v.int4; if(msg_list_check_msg(ml, mid)) { DBG("MSILO:m_dump: message[%d] mid=%d already sent.\n", i, mid); continue; } memset(str_vals, 0, 4*sizeof(str)); if (!(rec->fld[1].flags & DB_NULL)) str_vals[0] = rec->fld[1].v.lstr; if (!(rec->fld[2].flags & DB_NULL)) str_vals[1] = rec->fld[2].v.lstr; if (!(rec->fld[3].flags & DB_NULL)) str_vals[2] = rec->fld[3].v.lstr; if (!(rec->fld[4].flags & DB_NULL)) str_vals[3] = rec->fld[4].v.lstr; if (!(rec->fld[6].flags & DB_NULL)) str_vals[4] = rec->fld[6].v.lstr; hdr_str.len = 1024; if(m_build_headers(&hdr_str, str_vals[3] /*ctype*/, str_vals[0]/*from*/) < 0) { DBG("MSILO:m_dump: headers building failed!!!\n"); msg_list_set_flag(ml, mid, MS_MSG_ERRO); goto error; } DBG("MSILO:m_dump: msg [%d-%d] for: %.*s\n", i+1, mid, uid.len, ZSW(uid.s)); /** sending using TM function: t_uac */ body_str.len = 1024; if (rec->fld[5].flags & DB_NULL) { rtime = 0; } else { rtime = rec->fld[5].v.time; } n = m_build_body(&body_str, rtime, str_vals[2/*body*/]); if(n<0) DBG("MSILO:m_dump: sending simple body\n"); else DBG("MSILO:m_dump: sending composed body\n"); set_uac_req(&uac_r, &msg_type, /* Type of the message */ &hdr_str, /* Optional headers including CRLF */ (n<0)?&str_vals[2]:&body_str, /* Message body */ 0, /* dialog */ TMCB_LOCAL_COMPLETED, /* callback flags */ m_tm_callback, /* Callback function */ (void*)(long)mid /* Callback parameter */ ); tmb.t_request(&uac_r, &str_vals[4], /* Request-URI */ &str_vals[1], /* To */ &str_vals[0], /* From */ next_hop.len ? &next_hop: NULL /* next hop */ ); } done: /** * Free the result because we don't need it * anymore */ if (res) db_res_free(res); return 1; error: if (res) db_res_free(res); return -1; } /** * - cleaning up the messages that got reply * - delete expired messages from database */ void m_clean_silo(unsigned int ticks, void *param) { msg_list_el mle = NULL, p; DBG("MSILO:clean_silo: cleaning stored messages - %d\n", ticks); msg_list_check(ml); mle = p = msg_list_reset(ml); while(p) { if(p->flag & MS_MSG_DONE) { del_mid->match[0].v.int4 = p->msgid; DBG("MSILO:clean_silo: cleaning sent message [%d]\n", p->msgid); if (db_exec(NULL, del_mid) < 0) { DBG("MSILO:clean_silo: error while cleaning message %d.\n", p->msgid); } } p = p->next; } msg_list_el_free_all(mle); /* cleaning expired messages */ if(ticks % (ms_check_time * ms_clean_period) < ms_check_time) { DBG("MSILO:clean_silo: cleaning expired messages\n"); del_expired->match[0].v.time = (int)time(NULL); if (db_exec(NULL, del_expired) < 0) { DBG("MSILO:clean_silo: ERROR cleaning expired messages\n"); } } } /** * TM callback function - delete message from database if was sent OK */ void m_tm_callback( struct cell *t, int type, struct tmcb_params *ps) { int mid = -1; DBG("MSILO:m_tm_callback: completed with status %d\n", ps->code); if(!ps->param) { DBG("MSILO m_tm_callback: message id not received\n"); goto done; } mid = (int)(long)(*ps->param); if(ps->code < 200 || ps->code >= 300) { DBG("MSILO:m_tm_callback: message <%d> was not sent successfully\n", mid); msg_list_set_flag(ml, mid, MS_MSG_ERRO); goto done; } msg_list_set_flag(ml, mid, MS_MSG_DONE); done: return; } kamailio-4.0.4/obsolete/dbtext/0000755000000000000000000000000012223032460015103 5ustar rootrootkamailio-4.0.4/obsolete/dbtext/dbt_base.c0000644000000000000000000002653612223032460017026 0ustar rootroot/* * $Id$ * * DBText module core functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText module interface * * 2003-01-30 created by Daniel * */ #include #include "../../str.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include "dbtext.h" #include "dbt_res.h" #include "dbt_api.h" #ifndef CFG_DIR #define CFG_DIR "/tmp" #endif #define DBT_ID "dbtext://" #define DBT_ID_LEN (sizeof(DBT_ID)-1) #define DBT_PATH_LEN 256 /* * Initialize database connection */ db_con_t* dbt_init(const char* _sqlurl) { db_con_t* _res; str _s; char dbt_path[DBT_PATH_LEN]; if (!_sqlurl) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_init: Invalid parameter value\n"); #endif return NULL; } _s.s = (char*)_sqlurl; _s.len = strlen(_sqlurl); if(_s.len <= DBT_ID_LEN || strncmp(_s.s, DBT_ID, DBT_ID_LEN)!=0) { LOG(L_ERR, "DBT:dbt_init: invalid database URL - should be:" " <%s[/]path/to/directory>\n", DBT_ID); return NULL; } _s.s += DBT_ID_LEN; _s.len -= DBT_ID_LEN; if(_s.s[0]!='/') { if(sizeof(CFG_DIR)+_s.len+2 > DBT_PATH_LEN) { LOG(L_ERR, "DBT:dbt_init: path to database is too long\n"); return NULL; } strcpy(dbt_path, CFG_DIR); dbt_path[sizeof(CFG_DIR)] = '/'; strncpy(&dbt_path[sizeof(CFG_DIR)+1], _s.s, _s.len); _s.len += sizeof(CFG_DIR); _s.s = dbt_path; } _res = pkg_malloc(sizeof(db_con_t)+sizeof(dbt_con_t)); if (!_res) { LOG(L_ERR, "DBT:dbt_init: No memory left\n"); return NULL; } memset(_res, 0, sizeof(db_con_t) + sizeof(dbt_con_t)); _res->tail = (unsigned long)((char*)_res+sizeof(db_con_t)); DBT_CON_CONNECTION(_res) = dbt_cache_get_db(&_s); if (!DBT_CON_CONNECTION(_res)) { LOG(L_ERR, "DBT:dbt_init: cannot get the link to database\n"); return NULL; } return _res; } /* * Close a database connection */ void dbt_close(db_con_t* _h) { if (!_h) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_close: Invalid parameter value\n"); #endif return; } if (DBT_CON_RESULT(_h)) dbt_result_free(DBT_CON_RESULT(_h)); pkg_free(_h); return; } /* * Free all memory allocated by get_result */ int dbt_free_query(db_con_t* _h, db_res_t* _r) { if ((!_h) || (!_r)) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_free_query: Invalid parameter value\n"); #endif return -1; } if(dbt_free_result(_r) < 0) { LOG(L_ERR,"DBT:dbt_free_query:Unable to free result structure\n"); return -1; } if(dbt_result_free(DBT_CON_RESULT(_h)) < 0) { LOG(L_ERR, "DBT:dbt_free_query: Unable to free internal structure\n"); return -1; } DBT_CON_RESULT(_h) = NULL; return 0; } /* * Query table for specified rows * _h: structure representing database connection * _k: key names * _op: operators * _v: values of the keys that must match * _c: column names to return * _n: number of key=values pairs to compare * _nc: number of columns to return * _o: order by the specified column */ int dbt_query(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc, db_key_t _o, db_res_t** _r) { tbl_cache_p _tbc = NULL; dbt_table_p _dtp = NULL; dbt_row_p _drp = NULL; dbt_result_p _dres = NULL; str stbl; int *lkey=NULL, *lres=NULL; if ((!_h) || (!_r) || !CON_TABLE(_h)) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_query: Invalid parameter value\n"); #endif return -1; } stbl.s = (char*)CON_TABLE(_h); stbl.len = strlen(CON_TABLE(_h)); _tbc = dbt_db_get_table(DBT_CON_CONNECTION(_h), &stbl); if(!_tbc) { DBG("DBT:dbt_query: table does not exist!\n"); return -1; } lock_get(&_tbc->sem); _dtp = _tbc->dtp; if(!_dtp || _dtp->nrcols < _nc) { DBG("DBT:dbt_query: table not loaded!\n"); goto error; } if(_k) { lkey = dbt_get_refs(_dtp, _k, _n); if(!lkey) goto error; } if(_c) { lres = dbt_get_refs(_dtp, _c, _nc); if(!lres) goto error; } DBG("DBT:dbt_query: new res with %d cols\n", _nc); _dres = dbt_result_new(_dtp, lres, _nc); if(!_dres) goto error; _drp = _dtp->rows; while(_drp) { if(dbt_row_match(_dtp, _drp, lkey, _op, _v, _n)) { if(dbt_result_extract_fields(_dtp, _drp, lres, _dres)) { DBG("DBT:dbt_query: error extracting result fields!\n"); goto clean; } } _drp = _drp->next; } dbt_table_update_flags(_dtp, DBT_TBFL_ZERO, DBT_FL_IGN, 1); lock_release(&_tbc->sem); #ifdef DBT_EXTRA_DEBUG dbt_result_print(_dres); #endif DBT_CON_RESULT(_h) = _dres; if(lkey) pkg_free(lkey); if(lres) pkg_free(lres); return dbt_get_result(_h, _r); error: lock_release(&_tbc->sem); if(lkey) pkg_free(lkey); if(lres) pkg_free(lres); DBG("DBT:dbt_query: error while querying table!\n"); return -1; clean: lock_release(&_tbc->sem); if(lkey) pkg_free(lkey); if(lres) pkg_free(lres); if(_dres) dbt_result_free(_dres); DBG("DBT:dbt_query: make clean\n"); return -1; } /* * Raw SQL query -- is not the case to have this method */ int dbt_raw_query(db_con_t* _h, char* _s, db_res_t** _r) { *_r = NULL; return -1; } /* * Insert a row into table */ int dbt_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n) { tbl_cache_p _tbc = NULL; dbt_table_p _dtp = NULL; dbt_row_p _drp = NULL; str stbl; int *lkey=NULL, i, j; if (!_h || !CON_TABLE(_h)) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_insert: Invalid parameter value\n"); #endif return -1; } if(!_k || !_v || _n<=0) { #ifdef DBT_EXTRA_DEBUG DBG("DBT:dbt_insert: no key-value to insert\n"); #endif return -1; } stbl.s = (char*)CON_TABLE(_h); stbl.len = strlen(CON_TABLE(_h)); _tbc = dbt_db_get_table(DBT_CON_CONNECTION(_h), &stbl); if(!_tbc) { DBG("DBT:db_insert: table does not exist!\n"); return -1; } lock_get(&_tbc->sem); _dtp = _tbc->dtp; if(!_dtp) { DBG("DBT:db_insert: table does not exist!!\n"); goto error; } if(_dtp->nrcols<_n) { DBG("DBT:db_insert: more values than columns!!\n"); goto error; } if(_k) { lkey = dbt_get_refs(_dtp, _k, _n); if(!lkey) goto error; } _drp = dbt_row_new(_dtp->nrcols); if(!_drp) { DBG("DBT:db_insert: no memory for a new row!!\n"); goto error; } for(i=0; i<_n; i++) { j = (lkey)?lkey[i]:i; if(dbt_is_neq_type(_dtp->colv[j]->type, _v[i].type)) { DBG("DBT:db_insert: incompatible types v[%d] - c[%d]!\n", i, j); goto clean; } if(dbt_row_set_val(_drp, &(_v[i]), _v[i].type, j)) { DBG("DBT:db_insert: cannot set v[%d] in c[%d]!\n", i, j); goto clean; } } if(dbt_table_add_row(_dtp, _drp)) { DBG("DBT:db_insert: cannot insert the new row!!\n"); goto clean; } #ifdef DBT_EXTRA_DEBUG dbt_print_table(_dtp, NULL); #endif lock_release(&_tbc->sem); if(lkey) pkg_free(lkey); DBG("DBT:db_insert: done!\n"); return 0; error: lock_release(&_tbc->sem); if(lkey) pkg_free(lkey); DBG("DBT:db_insert: error inserting row in table!\n"); return -1; clean: lock_release(&_tbc->sem); if(lkey) pkg_free(lkey); if(_drp) // free row dbt_row_free(_dtp, _drp); DBG("DBT:db_insert: make clean!\n"); return -1; } /* * Delete a row from table */ int dbt_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n) { tbl_cache_p _tbc = NULL; dbt_table_p _dtp = NULL; dbt_row_p _drp = NULL, _drp0 = NULL; int *lkey = NULL; str stbl; if (!_h || !CON_TABLE(_h)) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_delete: Invalid parameter value\n"); #endif return -1; } stbl.s = (char*)CON_TABLE(_h); stbl.len = strlen(CON_TABLE(_h)); _tbc = dbt_db_get_table(DBT_CON_CONNECTION(_h), &stbl); if(!_tbc) { DBG("DBT:dbt_delete: error loading table <%s>!\n", CON_TABLE(_h)); return -1; } lock_get(&_tbc->sem); _dtp = _tbc->dtp; if(!_dtp) { DBG("DBT:dbt_delete: table does not exist!!\n"); goto error; } if(!_k || !_v || _n<=0) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_delete: delete all values\n"); #endif dbt_table_free_rows(_dtp); lock_release(&_tbc->sem); return 0; } lkey = dbt_get_refs(_dtp, _k, _n); if(!lkey) goto error; _drp = _dtp->rows; while(_drp) { _drp0 = _drp->next; if(dbt_row_match(_dtp, _drp, lkey, _o, _v, _n)) { // delete row DBG("DBT:dbt_delete: deleting a row!\n"); if(_drp->prev) (_drp->prev)->next = _drp->next; else _dtp->rows = _drp->next; if(_drp->next) (_drp->next)->prev = _drp->prev; _dtp->nrrows--; // free row dbt_row_free(_dtp, _drp); } _drp = _drp0; } dbt_table_update_flags(_dtp, DBT_TBFL_MODI, DBT_FL_SET, 1); #ifdef DBT_EXTRA_DEBUG dbt_print_table(_dtp, NULL); #endif lock_release(&_tbc->sem); if(lkey) pkg_free(lkey); return 0; error: lock_release(&_tbc->sem); DBG("DBT:dbt_delete: error deleting from table!\n"); return -1; } /* * Update a row in table */ int dbt_update(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un) { tbl_cache_p _tbc = NULL; dbt_table_p _dtp = NULL; dbt_row_p _drp = NULL; int i; str stbl; int *lkey=NULL, *lres=NULL; if (!_h || !CON_TABLE(_h) || !_uk || !_uv || _un <= 0) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_update: Invalid parameter value\n"); #endif return -1; } stbl.s = (char*)CON_TABLE(_h); stbl.len = strlen(CON_TABLE(_h)); _tbc = dbt_db_get_table(DBT_CON_CONNECTION(_h), &stbl); if(!_tbc) { DBG("DBT:dbt_update: table does not exist!\n"); return -1; } lock_get(&_tbc->sem); _dtp = _tbc->dtp; if(!_dtp || _dtp->nrcols < _un) { DBG("DBT:dbt_update: table not loaded or more values" " to update than columns!\n"); goto error; } if(_k) { lkey = dbt_get_refs(_dtp, _k, _n); if(!lkey) goto error; } lres = dbt_get_refs(_dtp, _uk, _un); if(!lres) goto error; DBG("DBT:dbt_update: ---- \n"); _drp = _dtp->rows; while(_drp) { if(dbt_row_match(_dtp, _drp, lkey, _o, _v, _n)) { // update fields for(i=0; i<_un; i++) { if(dbt_is_neq_type(_dtp->colv[lres[i]]->type, _uv[i].type)) { DBG("DBT:dbt_update: incompatible types!\n"); goto error; } if(dbt_row_update_val(_drp, &(_uv[i]), _uv[i].type, lres[i])) { DBG("DBT:dbt_update: cannot set v[%d] in c[%d]!\n", i, lres[i]); goto error; } } } _drp = _drp->next; } dbt_table_update_flags(_dtp, DBT_TBFL_MODI, DBT_FL_SET, 1); #ifdef DBT_EXTRA_DEBUG dbt_print_table(_dtp, NULL); #endif lock_release(&_tbc->sem); if(lkey) pkg_free(lkey); if(lres) pkg_free(lres); return 0; error: lock_release(&_tbc->sem); if(lkey) pkg_free(lkey); if(lres) pkg_free(lres); DBG("DBT:dbt_update: error while updating table!\n"); return -1; } kamailio-4.0.4/obsolete/dbtext/dbtex/0000755000000000000000000000000012223032460016211 5ustar rootrootkamailio-4.0.4/obsolete/dbtext/dbtex/dbtex.c0000644000000000000000000001631712223032460017473 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../../sr_module.h" #include #include "../../db/db.h" /* * database URL - the path to the directory where the tables are located */ #define DB_URL "/tmp/dbtext" /** * table name - the file name - it must be located in database directory */ #define DB_TABLE "location" #define TRUE 1 #define FALSE 0 /* * Database module client example */ static int mod_init(); struct module_exports exports= { "DBTExample", (char*[]) { }, (cmd_function[]) { }, (int[]) { }, (fixup_function[]) { }, 0, /* number of functions*/ 0, /* Module parameter names */ 0, /* Module parameter types */ 0, /* Module parameter variable pointers */ 0, /* Number of module parameters */ mod_init, /* module initialization function */ 0, /* response function*/ 0, /* destroy function */ 0, /* oncancel function */ 0 /* per-child init function */ }; static int print_res(db_res_t* _r) { int i, j; for(i = 0; i < RES_COL_N(_r); i++) { printf("%s ", RES_NAMES(_r)[i]); } printf("\n"); for(i = 0; i < RES_ROW_N(_r); i++) { for(j = 0; j < RES_COL_N(_r); j++) { if (RES_ROWS(_r)[i].values[j].nul == TRUE) { printf("NULL "); continue; } switch(RES_ROWS(_r)[i].values[j].type) { case DB_INT: printf("%d ", RES_ROWS(_r)[i].values[j].val.int_val); break; case DB_FLOAT: printf("%f ", RES_ROWS(_r)[i].values[j].val.float_val); break; case DB_DOUBLE: printf("%f ", RES_ROWS(_r)[i].values[j].val.double_val); break; case DB_DATETIME: printf("%s ", ctime(&(RES_ROWS(_r)[i].values[j].val.time_val))); break; case DB_STRING: printf("%s ", RES_ROWS(_r)[i].values[j].val.string_val); break; case DB_STR: printf("%.*s ", RES_ROWS(_r)[i].values[j].val.str_val.len, RES_ROWS(_r)[i].values[j].val.str_val.s); break; case DB_BLOB: printf("%.*s ", RES_ROWS(_r)[i].values[j].val.blob_val.len, RES_ROWS(_r)[i].values[j].val.blob_val.s); break; } } printf("\n"); } return TRUE; } int mod_init() { /* * Column names of table location */ db_key_t keys1[] = {"user", "contact", "q", "expire", "opaque" }; db_key_t keys2[] = {"user", "q"}; db_key_t keys3[] = {"user", "contact"}; db_key_t keys4[] = {"contact", "q"}; db_val_t vals1[] = { { DB_STRING , 0, { .string_val = "foo@bar.com" } }, { DB_STR , 0, { .str_val = { "real@foo.bar.com", 18 } } }, { DB_DOUBLE , 0, { .double_val = 1.2 } }, { DB_DATETIME, 0, { .time_val = 439826493 } }, { DB_BLOB , 0, { .blob_val = { "hdslgkhas\0glksf", 17 } } } }; db_val_t vals2[] = { { DB_STRING , 0, { .string_val = "foo2@bar2.com" } }, { DB_STR , 0, { .str_val = { "real2@foo.bar2.com", 18 } } }, { DB_DOUBLE , 0, { .double_val = 1.3 } }, { DB_DATETIME, 0, { .time_val = 12345 } }, { DB_BLOB , 0, { .blob_val = { "\0a\0balkdfj", 10 } } } }; db_val_t vals3[] = { { DB_STRING , 0, { .string_val = "foo3@bar3.com" } }, { DB_STR , 0, { .str_val = { "real3@foo.bar3.com", 18 } } }, { DB_DOUBLE , 0, { .double_val = 1.5 } }, { DB_DATETIME, 0, { .time_val = 123456 } }, { DB_BLOB , 0, { .blob_val = { "halgkasdg\'", 10 } } } }; db_val_t vals4[] = { { DB_STRING, 0, { .string_val = "foo2@bar2.com" } }, { DB_DOUBLE, 0, { .double_val = 1.30 } } }; db_val_t vals5[] = { { DB_STRING, 0, { .string_val = "foo3@bar3.com" } }, { DB_STRING, 0, { .string_val = "real3@foo.bar3.com" } } }; db_val_t vals6[] = { { DB_STRING, 0, { .string_val = "different@address.com" } }, { DB_DOUBLE, 0, { .double_val = 2.5 } } }; db_con_t* h; db_res_t* res; fprintf(stderr, "DBExample - registering...\n"); /* The first call must be bind_dbmod * This call will search for functions * exported by a database module */ if (bind_dbmod()) { fprintf(stderr, "Error while binding database module, did you forget to load a database module ?\n"); return -1; } /* * Create a database connection * DB_URL is database URL of form * /path/to/dbtext/database * The function returns handle, that * represents a database connection */ h = db_init(DB_URL); if (!h) { fprintf(stderr, "Error while initializing database connection\n"); return -1; } /* * Specify a table name, that will * be used for manipulations */ if (db_use_table(h, DB_TABLE) < 0) { fprintf(stderr, "Error while calling db_use_table\n"); return -1; } /* If you do not specify any keys and values to be * matched, all rows will be deleted */ if (db_delete(h, NULL, NULL, NULL, 0) < 0) { fprintf(stderr, "Error while flushing table\n"); return -1; } if (db_insert(h, keys1, vals1, 5) < 0) { fprintf(stderr, "Error while inserting line 1\n"); return -1; } if (db_insert(h, keys1, vals2, 5) < 0) { fprintf(stderr, "Error while inserting line 2\n"); return -1; } if (db_insert(h, keys1, vals3, 5) < 0) { fprintf(stderr, "Error while inserting line 3\n"); return -1; } /* * Let's delete middle line with * user = foo2@bar2.com and q = 1.3 */ if (db_delete(h, keys2, NULL, vals4, 2) < 0) { fprintf(stderr, "Error while deleting line\n"); return -1; } /* * Modify last line */ if (db_update(h, keys3, NULL, vals5, keys4, vals6, 2, 2) < 0) { fprintf(stderr, "Error while modifying table\n"); return -1; } /* * Last but not least, dump the result of db_query */ if (db_query(h, NULL, NULL, NULL, NULL, 0, 0, NULL, &res) < 0) { fprintf(stderr, "Error while querying table\n"); return -1; } print_res(res); /* * Free the result because we don't need it * anymore */ if (db_free_query(h, res) < 0) { fprintf(stderr, "Error while freeing result of query\n"); return -1; } /* * Close existing database connection * and free previously allocated * memory */ db_close(h); return 0; } kamailio-4.0.4/obsolete/dbtext/dbtex/db/0000755000000000000000000000000012223032460016576 5ustar rootrootkamailio-4.0.4/obsolete/dbtext/dbtex/db/test0000644000000000000000000000020712223032460017477 0ustar rootrootid(int,auto) name(str,null) flag(double) desc(str,null) 1:aaa\:sss:0.34:ddd\tffff\tggggg 2:xxx\tzzz:-3.75:ccccc 3:wwwww:2.50:qqqqqqqq kamailio-4.0.4/obsolete/dbtext/dbtex/db/location0000644000000000000000000000021112223032460020323 0ustar rootrootuser(str) domain(str,null) contact(str,null) expires(int, null) q(double, null) callid(str,null) cseq(int,null) last_modified(int,null) kamailio-4.0.4/obsolete/dbtext/dbtex/Makefile0000644000000000000000000000027512223032460017655 0ustar rootroot# $Id$ # # database example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile auto_gen= NAME=dbtex.so LIBS= include ../../Makefile.modules kamailio-4.0.4/obsolete/dbtext/dbtext.c0000644000000000000000000000520012223032460016536 0ustar rootroot/* * $Id$ * * DBText module interface * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText module interface * * 2003-01-30 created by Daniel * 2003-03-11 New module interface (janakj) * 2003-03-16 flags export parameter added (janakj) * */ #include #include #include "../../sr_module.h" #include "dbtext.h" #include "dbt_lib.h" #include "dbt_api.h" MODULE_VERSION static int mod_init(void); static void destroy(void); /* * Exported functions */ static cmd_export_t cmds[] = { {"db_use_table", (cmd_function)dbt_use_table, 2, 0, 0}, {"db_init", (cmd_function)dbt_init, 1, 0, 0}, {"db_close", (cmd_function)dbt_close, 2, 0, 0}, {"db_query", (cmd_function)dbt_query, 2, 0, 0}, {"db_raw_query", (cmd_function)dbt_raw_query, 2, 0, 0}, {"db_free_result", (cmd_function)dbt_free_query, 2, 0, 0}, {"db_insert", (cmd_function)dbt_insert, 2, 0, 0}, {"db_delete", (cmd_function)dbt_delete, 2, 0, 0}, {"db_update", (cmd_function)dbt_update, 2, 0, 0}, {0, 0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { {0, 0, 0} }; struct module_exports exports = { "dbtext", cmds, /* Exported functions */ 0, /* RPC method */ params, /* Exported parameters */ mod_init, /* module initialization function */ 0, /* response function*/ destroy, /* destroy function */ 0, /* oncancel function */ 0 /* per-child init function */ }; static int mod_init(void) { if(dbt_init_cache()) return -1; /*return make_demo(); */ return 0; } static void destroy(void) { DBG("DBT:destroy ...\n"); dbt_cache_print(0); dbt_cache_destroy(); } kamailio-4.0.4/obsolete/dbtext/dbt_api.c0000644000000000000000000002045312223032460016655 0ustar rootroot/* * $Id$ * * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText library * * 2003-02-05 created by Daniel * */ #include #include "../../str.h" #include "../../mem/mem.h" #include "dbt_res.h" #include "dbt_api.h" /* * Release memory used by columns */ int dbt_free_columns(db_res_t* _r) { if (!_r) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_free_columns: Invalid parameter\n"); #endif return -1; } if (RES_NAMES(_r)) pkg_free(RES_NAMES(_r)); if (RES_TYPES(_r)) pkg_free(RES_TYPES(_r)); return 0; } /* * Release memory used by row */ int dbt_free_row(db_row_t* _r) { if (!_r) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_free_row: Invalid parameter value\n"); #endif return -1; } if(ROW_VALUES(_r)) pkg_free(ROW_VALUES(_r)); return 0; } /* * Release memory used by rows */ int dbt_free_rows(db_res_t* _r) { int i; if (!_r) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_free_rows: Invalid parameter value\n"); #endif return -1; } if (RES_ROWS(_r)) { for(i = 0; i < RES_ROW_N(_r); i++) { dbt_free_row(&(RES_ROWS(_r)[i])); } pkg_free(RES_ROWS(_r)); } return 0; } /* * Release memory used by a result structure */ int dbt_free_result(db_res_t* _r) { if (!_r) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_free_result: Invalid parameter\n"); #endif return -1; } dbt_free_columns(_r); dbt_free_rows(_r); pkg_free(_r); return 0; } int dbt_use_table(db_con_t* _h, const char* _t) { if ((!_h) || (!_t)) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_use_table: Invalid parameter value\n"); #endif return -1; } CON_TABLE(_h) = _t; return 0; } /* * Create a new result structure and initialize it */ db_res_t* dbt_new_result(void) { db_res_t* r; r = (db_res_t*)pkg_malloc(sizeof(db_res_t)); if (!r) { LOG(L_ERR, "dbt_new_result(): No memory left\n"); return 0; } RES_NAMES(r) = 0; RES_TYPES(r) = 0; RES_COL_N(r) = 0; RES_ROWS(r) = 0; RES_ROW_N(r) = 0; return r; } /* * Fill the structure with data from database */ int dbt_convert_result(db_con_t* _h, db_res_t* _r) { if ((!_h) || (!_r)) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_convert_result: Invalid parameter\n"); #endif return -1; } if (dbt_get_columns(_h, _r) < 0) { LOG(L_ERR, "DBT:dbt_convert_result: Error while getting column names\n"); return -2; } if (dbt_convert_rows(_h, _r) < 0) { LOG(L_ERR, "DBT:dbt_convert_result: Error while converting rows\n"); dbt_free_columns(_r); return -3; } return 0; } /* * Retrieve result set */ int dbt_get_result(db_con_t* _h, db_res_t** _r) { if ((!_h) || (!_r)) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_get_result: Invalid parameter value\n"); #endif return -1; } if (!DBT_CON_RESULT(_h)) { LOG(L_ERR, "DBT:dbt_get_result: error getting result\n"); *_r = 0; return -3; } *_r = dbt_new_result(); if (*_r == 0) { LOG(L_ERR, "DBT:dbt_get_result: No memory left\n"); return -2; } if (dbt_convert_result(_h, *_r) < 0) { LOG(L_ERR, "DBT:dbt_get_result: Error while converting result\n"); pkg_free(*_r); return -4; } return 0; } /* * Get and convert columns from a result */ int dbt_get_columns(db_con_t* _h, db_res_t* _r) { int n, i; if ((!_h) || (!_r)) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_get_columns: Invalid parameter\n"); #endif return -1; } n = DBT_CON_RESULT(_h)->nrcols; if (!n) { LOG(L_ERR, "DBT:dbt_get_columns: No columns\n"); return -2; } RES_NAMES(_r) = (db_key_t*)pkg_malloc(sizeof(db_key_t) * n); if (!RES_NAMES(_r)) { LOG(L_ERR, "DBT:dbt_get_columns: No memory left\n"); return -3; } RES_TYPES(_r) = (db_type_t*)pkg_malloc(sizeof(db_type_t) * n); if (!RES_TYPES(_r)) { LOG(L_ERR, "DBT:dbt_get_columns: No memory left\n"); pkg_free(RES_NAMES(_r)); return -4; } RES_COL_N(_r) = n; for(i = 0; i < n; i++) { RES_NAMES(_r)[i] = DBT_CON_RESULT(_h)->colv[i].name.s; switch( DBT_CON_RESULT(_h)->colv[i].type) { case DB_INT: case DB_DATETIME: RES_TYPES(_r)[i] = DB_INT; break; case DB_FLOAT: RES_TYPES(_r)[i] = DB_FLOAT; break; case DB_DOUBLE: RES_TYPES(_r)[i] = DB_DOUBLE; break; default: RES_TYPES(_r)[i] = DB_STR; break; } } return 0; } /* * Convert rows from internal to db API representation */ int dbt_convert_rows(db_con_t* _h, db_res_t* _r) { int n, i; dbt_row_p _rp = NULL; if ((!_h) || (!_r)) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_convert_rows: Invalid parameter\n"); #endif return -1; } n = DBT_CON_RESULT(_h)->nrrows; RES_ROW_N(_r) = n; if (!n) { RES_ROWS(_r) = 0; return 0; } RES_ROWS(_r) = (struct db_row*)pkg_malloc(sizeof(db_row_t) * n); if (!RES_ROWS(_r)) { LOG(L_ERR, "DBT:dbt_convert_rows: No memory left\n"); return -2; } i = 0; _rp = DBT_CON_RESULT(_h)->rows; while(_rp) { DBT_CON_ROW(_h) = _rp; if (!DBT_CON_ROW(_h)) { LOG(L_ERR, "DBT:dbt_convert_rows: error getting current row\n"); RES_ROW_N(_r) = i; dbt_free_rows(_r); return -3; } if (dbt_convert_row(_h, _r, &(RES_ROWS(_r)[i])) < 0) { LOG(L_ERR, "DBT:dbt_convert_rows: Error while converting" " row #%d\n", i); RES_ROW_N(_r) = i; dbt_free_rows(_r); return -4; } i++; _rp = _rp->next; } return 0; } /* * Convert a row from result into db API representation */ int dbt_convert_row(db_con_t* _h, db_res_t* _res, db_row_t* _r) { int i; if ((!_h) || (!_r) || (!_res)) { #ifdef DBT_EXTRA_DEBUG LOG(L_ERR, "DBT:dbt_convert_row: Invalid parameter value\n"); #endif return -1; } ROW_VALUES(_r) = (db_val_t*)pkg_malloc(sizeof(db_val_t) * RES_COL_N(_res)); ROW_N(_r) = RES_COL_N(_res); if (!ROW_VALUES(_r)) { LOG(L_ERR, "DBT:dbt_convert_row: No memory left\n"); return -1; } for(i = 0; i < RES_COL_N(_res); i++) { (ROW_VALUES(_r)[i]).nul = DBT_CON_ROW(_h)->fields[i].nul; switch(RES_TYPES(_res)[i]) { case DB_INT: VAL_INT(&(ROW_VALUES(_r)[i])) = DBT_CON_ROW(_h)->fields[i].val.int_val; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_INT; break; case DB_FLOAT: VAL_FLOAT(&(ROW_VALUES(_r)[i])) = DBT_CON_ROW(_h)->fields[i].val.float_val; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_FLOAT; break; case DB_DOUBLE: VAL_DOUBLE(&(ROW_VALUES(_r)[i])) = DBT_CON_ROW(_h)->fields[i].val.double_val; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_DOUBLE; break; case DB_STRING: VAL_STR(&(ROW_VALUES(_r)[i])).s = DBT_CON_ROW(_h)->fields[i].val.str_val.s; VAL_STR(&(ROW_VALUES(_r)[i])).len = DBT_CON_ROW(_h)->fields[i].val.str_val.len; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_STR; break; case DB_STR: VAL_STR(&(ROW_VALUES(_r)[i])).s = DBT_CON_ROW(_h)->fields[i].val.str_val.s; VAL_STR(&(ROW_VALUES(_r)[i])).len = DBT_CON_ROW(_h)->fields[i].val.str_val.len; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_STR; break; case DB_DATETIME: VAL_INT(&(ROW_VALUES(_r)[i])) = DBT_CON_ROW(_h)->fields[i].val.int_val; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_INT; break; case DB_BLOB: VAL_STR(&(ROW_VALUES(_r)[i])).s = DBT_CON_ROW(_h)->fields[i].val.str_val.s; VAL_STR(&(ROW_VALUES(_r)[i])).len = DBT_CON_ROW(_h)->fields[i].val.str_val.len; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_STR; break; case DB_BITMAP: VAL_INT(&(ROW_VALUES(_r)[i])) = DBT_CON_ROW(_h)->fields[i].val.bitmap_val; VAL_TYPE(&(ROW_VALUES(_r)[i])) = DB_INT; break; } } return 0; } kamailio-4.0.4/obsolete/dbtext/dbt_res.c0000644000000000000000000003015712223032460016677 0ustar rootroot/* * $Id$ * * DBText module core functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText module interface * 2003-06-05 fixed bug: when comparing two values and the first was less than * the second one, the result of 'dbt_row_match' was always true, * thanks to Gabriel, (Daniel) * 2003-02-04 created by Daniel * */ #include #include #include #include "../../mem/mem.h" #include "dbt_res.h" dbt_result_p dbt_result_new(dbt_table_p _dtp, int *_lres, int _sz) { dbt_result_p _dres = NULL; int i, n; char *p; if(!_dtp || _sz < 0) return NULL; if(!_lres) _sz = _dtp->nrcols; _dres = (dbt_result_p)pkg_malloc(sizeof(dbt_result_t)); if(!_dres) return NULL; _dres->colv = (dbt_column_p)pkg_malloc(_sz*sizeof(dbt_column_t)); if(!_dres->colv) { DBG("DBT:dbt_result_new: no memory!\n"); pkg_free(_dres); return NULL; } DBG("DBT:dbt_result_new: new res with %d cols\n", _sz); for(i = 0; i < _sz; i++) { n = (_lres)?_dtp->colv[_lres[i]]->name.len:_dtp->colv[i]->name.len; p = (_lres)?_dtp->colv[_lres[i]]->name.s:_dtp->colv[i]->name.s; _dres->colv[i].name.s = (char*)pkg_malloc((n+1)*sizeof(char)); if(!_dres->colv[i].name.s) { DBG("DBT:dbt_result_new: no memory\n"); goto clean; } _dres->colv[i].name.len = n; strncpy(_dres->colv[i].name.s, p, n); _dres->colv[i].name.s[n] = 0; _dres->colv[i].type = (_lres)?_dtp->colv[_lres[i]]->type:_dtp->colv[i]->type; } _dres->nrcols = _sz; _dres->nrrows = 0; _dres->rows = NULL; return _dres; clean: while(i>=0) { if(_dres->colv[i].name.s) pkg_free(_dres->colv[i].name.s); i--; } pkg_free(_dres->colv); pkg_free(_dres); return NULL; } int dbt_result_free(dbt_result_p _dres) { dbt_row_p _rp=NULL, _rp0=NULL; int i; if(!_dres) return -1; _rp = _dres->rows; while(_rp) { _rp0=_rp; if(_rp0->fields) { for(i=0; i<_dres->nrcols; i++) { if(_dres->colv[i].type==DB_STR && _rp0->fields[i].val.str_val.s) pkg_free(_rp0->fields[i].val.str_val.s); } pkg_free(_rp0->fields); } pkg_free(_rp0); _rp=_rp->next; } if(_dres->colv) { for(i=0; i<_dres->nrcols; i++) { if(_dres->colv[i].name.s) pkg_free(_dres->colv[i].name.s); } pkg_free(_dres->colv); } pkg_free(_dres); return 0; } int dbt_result_add_row(dbt_result_p _dres, dbt_row_p _drp) { if(!_dres || !_drp) return -1; _dres->nrrows++; if(_dres->rows) (_dres->rows)->prev = _drp; _drp->next = _dres->rows; _dres->rows = _drp; return 0; } int* dbt_get_refs(dbt_table_p _dtp, db_key_t* _k, int _n) { int i, j, *_lref=NULL; if(!_dtp || !_k || _n < 0) return NULL; _lref = (int*)pkg_malloc(_n*sizeof(int)); if(!_lref) return NULL; for(i=0; i < _n; i++) { for(j=0; j<_dtp->nrcols; j++) { if(strlen(_k[i])==_dtp->colv[j]->name.len && !strncasecmp(_k[i], _dtp->colv[j]->name.s, _dtp->colv[j]->name.len)) { _lref[i] = j; break; } } if(j>=_dtp->nrcols) { DBG("DBT:dbt_get_refs: ERROR column <%s> not found\n", _k[i]); pkg_free(_lref); return NULL; } } return _lref; } int dbt_row_match(dbt_table_p _dtp, dbt_row_p _drp, int* _lkey, db_op_t* _op, db_val_t* _v, int _n) { int i, res; if(!_dtp || !_drp) return 0; if(!_lkey) return 1; for(i=0; i<_n; i++) { res = dbt_cmp_val(&_drp->fields[_lkey[i]], &_v[i]); if(!_op || !strcmp(_op[i], OP_EQ)) { if(res!=0) return 0; }else{ if(!strcmp(_op[i], OP_LT)) { if(res!=-1) return 0; }else{ if(!strcmp(_op[i], OP_GT)) { if(res!=1) return 0; }else{ if(!strcmp(_op[i], OP_LEQ)) { if(res==1) return 0; }else{ if(!strcmp(_op[i], OP_GEQ)) { if(res==-1) return 0; }else{ return 0; }}}}} } return 1; } int dbt_result_extract_fields(dbt_table_p _dtp, dbt_row_p _drp, int* _lres, dbt_result_p _dres) { dbt_row_p _rp=NULL; int i, n; if(!_dtp || !_drp || !_dres || _dres->nrcols<=0) return -1; _rp = dbt_result_new_row(_dres); if(!_rp) return -1; for(i=0; i<_dres->nrcols; i++) { n = (_lres)?_lres[i]:i; if(dbt_is_neq_type(_dres->colv[i].type, _dtp->colv[n]->type)) { DBG("DBT:dbt_result_extract_fields: wrong types!\n"); goto clean; } _rp->fields[i].nul = _drp->fields[n].nul; if(_rp->fields[i].nul) { memset(&(_rp->fields[i].val), 0, sizeof(_rp->fields[i].val)); continue; } switch(_dres->colv[i].type) { case DB_INT: case DB_DATETIME: case DB_BITMAP: _rp->fields[i].type = DB_INT; _rp->fields[i].val.int_val = _drp->fields[n].val.int_val; break; case DB_FLOAT: _rp->fields[i].type = DB_FLOAT; _rp->fields[i].val.float_val=_drp->fields[n].val.float_val; break; case DB_DOUBLE: _rp->fields[i].type = DB_DOUBLE; _rp->fields[i].val.double_val=_drp->fields[n].val.double_val; break; case DB_STRING: case DB_STR: case DB_BLOB: _rp->fields[i].type = DB_STR; _rp->fields[i].val.str_val.len = _drp->fields[n].val.str_val.len; _rp->fields[i].val.str_val.s =(char*)pkg_malloc(sizeof(char)* (_drp->fields[n].val.str_val.len+1)); if(!_rp->fields[i].val.str_val.s) goto clean; strncpy(_rp->fields[i].val.str_val.s, _drp->fields[n].val.str_val.s, _rp->fields[i].val.str_val.len); _rp->fields[i].val.str_val.s[_rp->fields[i].val.str_val.len]=0; break; default: goto clean; } } if(_dres->rows) (_dres->rows)->prev = _rp; _rp->next = _dres->rows; _dres->rows = _rp; _dres->nrrows++; return 0; clean: DBG("DBT:dbt_result_extract_fields: make clean!\n"); while(i>=0) { if(_rp->fields[i].type == DB_STR && !_rp->fields[i].nul && _rp->fields[i].val.str_val.s) pkg_free(_rp->fields[i].val.str_val.s); i--; } pkg_free(_rp->fields); pkg_free(_rp); return -1; } int dbt_result_print(dbt_result_p _dres) { #ifdef DBT_EXTRA_DEBUG int i; FILE *fout = stdout; dbt_row_p rowp = NULL; char *p; if(!_dres || _dres->nrcols<=0) return -1; fprintf(fout, "\nContent of result\n"); for(i=0; i<_dres->nrcols; i++) { switch(_dres->colv[i].type) { case DB_INT: fprintf(fout, "%.*s(int", _dres->colv[i].name.len, _dres->colv[i].name.s); if(_dres->colv[i].flag & DBT_FLAG_NULL) fprintf(fout, ",null"); fprintf(fout, ") "); break; case DB_FLOAT: fprintf(fout, "%.*s(float", _dres->colv[i].name.len, _dres->colv[i].name.s); if(_dres->colv[i].flag & DBT_FLAG_NULL) fprintf(fout, ",null"); fprintf(fout, ") "); break; case DB_DOUBLE: fprintf(fout, "%.*s(double", _dres->colv[i].name.len, _dres->colv[i].name.s); if(_dres->colv[i].flag & DBT_FLAG_NULL) fprintf(fout, ",null"); fprintf(fout, ") "); break; case DB_STR: fprintf(fout, "%.*s(str", _dres->colv[i].name.len, _dres->colv[i].name.s); if(_dres->colv[i].flag & DBT_FLAG_NULL) fprintf(fout, ",null"); fprintf(fout, ") "); break; default: return -1; } } fprintf(fout, "\n"); rowp = _dres->rows; while(rowp) { for(i=0; i<_dres->nrcols; i++) { switch(_dres->colv[i].type) { case DB_INT: if(rowp->fields[i].nul) fprintf(fout, "N "); else fprintf(fout, "%d ", rowp->fields[i].val.int_val); break; case DB_FLOAT: if(rowp->fields[i].nul) fprintf(fout, "N "); else fprintf(fout, "%.2f ", rowp->fields[i].val.float_val); break; case DB_DOUBLE: if(rowp->fields[i].nul) fprintf(fout, "N "); else fprintf(fout, "%.2f ", rowp->fields[i].val.double_val); break; case DB_STR: fprintf(fout, "\""); if(!rowp->fields[i].nul) { p = rowp->fields[i].val.str_val.s; while(p < rowp->fields[i].val.str_val.s + rowp->fields[i].val.str_val.len) { switch(*p) { case '\n': fprintf(fout, "\\n"); break; case '\r': fprintf(fout, "\\r"); break; case '\t': fprintf(fout, "\\t"); break; case '\\': fprintf(fout, "\\\\"); break; case '"': fprintf(fout, "\\\""); break; case '\0': fprintf(fout, "\\0"); break; default: fprintf(fout, "%c", *p); } p++; } } fprintf(fout, "\" "); break; default: return -1; } } fprintf(fout, "\n"); rowp = rowp->next; } #endif return 0; } int dbt_cmp_val(dbt_val_p _vp, db_val_t* _v) { int _l, _n; if(!_vp && !_v) return 0; if(!_v) return 1; if(!_vp) return -1; if(_vp->nul && _v->nul) return 0; if(_v->nul) return 1; if(_vp->nul) return -1; switch(VAL_TYPE(_v)) { case DB_INT: return (_vp->val.int_val<_v->val.int_val)?-1: (_vp->val.int_val>_v->val.int_val)?1:0; case DB_FLOAT: return (_vp->val.float_val<_v->val.float_val)?-1: (_vp->val.float_val>_v->val.float_val)?1:0; case DB_DOUBLE: return (_vp->val.double_val<_v->val.double_val)?-1: (_vp->val.double_val>_v->val.double_val)?1:0; case DB_DATETIME: return (_vp->val.int_val<_v->val.time_val)?-1: (_vp->val.int_val>_v->val.time_val)?1:0; case DB_STRING: _l = strlen(_v->val.string_val); _l = (_l>_vp->val.str_val.len)?_vp->val.str_val.len:_l; _n = strncasecmp(_vp->val.str_val.s, _v->val.string_val, _l); if(_n) return _n; if(_vp->val.str_val.len == strlen(_v->val.string_val)) return 0; if(_l==_vp->val.str_val.len) return -1; return 1; case DB_STR: _l = _v->val.str_val.len; _l = (_l>_vp->val.str_val.len)?_vp->val.str_val.len:_l; _n = strncasecmp(_vp->val.str_val.s, _v->val.str_val.s, _l); if(_n) return _n; if(_vp->val.str_val.len == _v->val.str_val.len) return 0; if(_l==_vp->val.str_val.len) return -1; return 1; case DB_BLOB: _l = _v->val.blob_val.len; _l = (_l>_vp->val.str_val.len)?_vp->val.str_val.len:_l; _n = strncasecmp(_vp->val.str_val.s, _v->val.blob_val.s, _l); if(_n) return _n; if(_vp->val.str_val.len == _v->val.blob_val.len) return 0; if(_l==_vp->val.str_val.len) return -1; return 1; case DB_BITMAP: return (_vp->val.int_val<_v->val.bitmap_val)?-1: (_vp->val.int_val>_v->val.bitmap_val)?1:0; } return -2; } dbt_row_p dbt_result_new_row(dbt_result_p _dres) { dbt_row_p _drp = NULL; if(!_dres || _dres->nrcols<=0) return NULL; _drp = (dbt_row_p)pkg_malloc(sizeof(dbt_row_t)); if(!_drp) return NULL; memset(_drp, 0, sizeof(dbt_row_t)); _drp->fields = (dbt_val_p)pkg_malloc(_dres->nrcols*sizeof(dbt_val_t)); if(!_drp->fields) { pkg_free(_drp); return NULL; } memset(_drp->fields, 0, _dres->nrcols*sizeof(dbt_val_t)); _drp->next = _drp->prev = NULL; return _drp; } int dbt_is_neq_type(db_type_t _t0, db_type_t _t1) { // DBG("DBT:dbt_is_neq_type: t0=%d t1=%d!\n", _t0, _t1); if(_t0 == _t1) return 0; switch(_t1) { case DB_INT: if(_t0==DB_DATETIME || _t0==DB_BITMAP) return 0; case DB_DATETIME: if(_t0==DB_INT) return 0; if(_t0==DB_BITMAP) return 0; case DB_FLOAT: break; case DB_DOUBLE: break; case DB_STRING: if(_t0==DB_STR) return 0; case DB_STR: if(_t0==DB_STRING || _t0==DB_BLOB) return 0; case DB_BLOB: if(_t0==DB_STR) return 0; case DB_BITMAP: if (_t0==DB_INT) return 0; } return 1; } kamailio-4.0.4/obsolete/dbtext/dbt_tb.c0000644000000000000000000002207012223032460016506 0ustar rootroot/* * $Id$ * * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText library * * 2003-02-03 created by Daniel * */ #include #include #include #include #include #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "../../locking.h" #include "dbt_util.h" #include "dbt_lib.h" /** * */ dbt_column_p dbt_column_new(char *_s, int _l) { dbt_column_p dcp = NULL; if(!_s || _l <=0) return NULL; dcp = (dbt_column_p)shm_malloc(sizeof(dbt_column_t)); if(!dcp) return NULL; dcp->name.s = (char*)shm_malloc(_l*sizeof(char)); if(!dcp->name.s) { shm_free(dcp); return NULL; } dcp->name.len = _l; strncpy(dcp->name.s, _s, _l); dcp->next = dcp->prev = NULL; dcp->type = 0; dcp->flag = DBT_FLAG_UNSET; return dcp; } /** * */ int dbt_column_free(dbt_column_p dcp) { if(!dcp) return -1; if(dcp->name.s) shm_free(dcp->name.s); shm_free(dcp); return 0; } /** * */ dbt_row_p dbt_row_new(int _nf) { int i; dbt_row_p _drp = NULL; _drp = (dbt_row_p)shm_malloc(sizeof(dbt_row_t)); if(!_drp) return NULL; _drp->fields = (dbt_val_p)shm_malloc(_nf*sizeof(dbt_val_t)); if(!_drp->fields) { shm_free(_drp); return NULL; } memset(_drp->fields, 0, _nf*sizeof(dbt_val_t)); for(i=0; i<_nf; i++) _drp->fields[i].nul = 1; _drp->next = _drp->prev = NULL; return _drp; } /** * */ int dbt_row_free(dbt_table_p _dtp, dbt_row_p _drp) { int i; if(!_dtp || !_drp) return -1; if(_drp->fields) { for(i=0; i<_dtp->nrcols; i++) if(_dtp->colv[i]->type==DB_STR && _drp->fields[i].val.str_val.s) shm_free(_drp->fields[i].val.str_val.s); shm_free(_drp->fields); } shm_free(_drp); return 0; } /** * */ dbt_table_p dbt_table_new(char *_s, int _l) { dbt_table_p dtp = NULL; if(!_s || _l <= 0) return NULL; dtp = (dbt_table_p)shm_malloc(sizeof(dbt_table_t)); if(!dtp) goto done; dtp->name.s = (char*)shm_malloc(_l*sizeof(char)); if(!dtp->name.s) { shm_free(dtp); dtp = NULL; goto done; } memcpy(dtp->name.s, _s, _l); dtp->name.len = _l; dtp->rows = NULL; dtp->cols = NULL; dtp->colv = NULL; dtp->mark = (int)time(NULL); dtp->flag = DBT_TBFL_ZERO; dtp->nrrows = dtp->nrcols = dtp->auto_val = 0; dtp->auto_col = -1; done: return dtp; } /** * */ int dbt_table_free_rows(dbt_table_p _dtp) { dbt_row_p _rp=NULL, _rp0=NULL; if(!_dtp || !_dtp->rows || !_dtp->colv) return -1; _rp = _dtp->rows; while(_rp) { _rp0=_rp; _rp=_rp->next; dbt_row_free(_dtp, _rp0); } dbt_table_update_flags(_dtp, DBT_TBFL_MODI, DBT_FL_SET, 1); _dtp->rows = NULL; _dtp->nrrows = 0; return 0; } /** * */ int dbt_table_add_row(dbt_table_p _dtp, dbt_row_p _drp) { if(!_dtp || !_drp) return -1; if(dbt_table_check_row(_dtp, _drp)) return -1; dbt_table_update_flags(_dtp, DBT_TBFL_MODI, DBT_FL_SET, 1); if(_dtp->rows) (_dtp->rows)->prev = _drp; _drp->next = _dtp->rows; _dtp->rows = _drp; _dtp->nrrows++; return 0; } /** * */ int dbt_table_free(dbt_table_p _dtp) { dbt_column_p _cp=NULL, _cp0=NULL; if(!_dtp) return -1; if(_dtp->name.s) shm_free(_dtp->name.s); if(_dtp->rows && _dtp->nrrows>0) dbt_table_free_rows(_dtp); _cp = _dtp->cols; while(_cp) { _cp0=_cp; _cp=_cp->next; dbt_column_free(_cp0); } if(_dtp->colv) shm_free(_dtp->colv); shm_free(_dtp); return 0; } /** * */ int dbt_row_set_val(dbt_row_p _drp, dbt_val_p _vp, int _t, int _idx) { if(!_drp || !_vp || _idx<0) return -1; _drp->fields[_idx].nul = _vp->nul; _drp->fields[_idx].type = _t; if(!_vp->nul) { switch(_t) { case DB_STR: case DB_BLOB: _drp->fields[_idx].type = DB_STR; _drp->fields[_idx].val.str_val.s = (char*)shm_malloc(_vp->val.str_val.len*sizeof(char)); if(!_drp->fields[_idx].val.str_val.s) { _drp->fields[_idx].nul = 1; return -1; } memcpy(_drp->fields[_idx].val.str_val.s, _vp->val.str_val.s, _vp->val.str_val.len); _drp->fields[_idx].val.str_val.len = _vp->val.str_val.len; break; case DB_STRING: _drp->fields[_idx].type = DB_STR; _drp->fields[_idx].val.str_val.len=strlen(_vp->val.string_val); _drp->fields[_idx].val.str_val.s = (char*)shm_malloc(_drp->fields[_idx].val.str_val.len *sizeof(char)); if(!_drp->fields[_idx].val.str_val.s) { _drp->fields[_idx].nul = 1; return -1; } memcpy(_drp->fields[_idx].val.str_val.s, _vp->val.string_val, _drp->fields[_idx].val.str_val.len); break; case DB_FLOAT: _drp->fields[_idx].type = DB_FLOAT; _drp->fields[_idx].val.float_val = _vp->val.float_val; break; case DB_DOUBLE: _drp->fields[_idx].type = DB_DOUBLE; _drp->fields[_idx].val.double_val = _vp->val.double_val; break; case DB_INT: _drp->fields[_idx].type = DB_INT; _drp->fields[_idx].val.int_val = _vp->val.int_val; break; case DB_DATETIME: _drp->fields[_idx].type = DB_INT; _drp->fields[_idx].val.int_val = (int)_vp->val.time_val; break; default: _drp->fields[_idx].nul = 1; return -1; } } return 0; } /** * */ int dbt_row_update_val(dbt_row_p _drp, dbt_val_p _vp, int _t, int _idx) { if(!_drp || !_vp || _idx<0) return -1; _drp->fields[_idx].nul = _vp->nul; _drp->fields[_idx].type = _t; if(!_vp->nul) { switch(_t) { case DB_BLOB: case DB_STR: _drp->fields[_idx].type = DB_STR; // free if already exists if(_drp->fields[_idx].val.str_val.s) shm_free(_drp->fields[_idx].val.str_val.s); _drp->fields[_idx].val.str_val.s = (char*)shm_malloc(_vp->val.str_val.len*sizeof(char)); if(!_drp->fields[_idx].val.str_val.s) { _drp->fields[_idx].nul = 1; return -1; } memcpy(_drp->fields[_idx].val.str_val.s, _vp->val.str_val.s, _vp->val.str_val.len); _drp->fields[_idx].val.str_val.len = _vp->val.str_val.len; break; case DB_STRING: _drp->fields[_idx].type = DB_STR; /* free if already exists */ if(_drp->fields[_idx].val.str_val.s) shm_free(_drp->fields[_idx].val.str_val.s); _drp->fields[_idx].type = DB_STR; _drp->fields[_idx].val.str_val.len=strlen(_vp->val.string_val); _drp->fields[_idx].val.str_val.s = (char*)shm_malloc(_drp->fields[_idx].val.str_val.len *sizeof(char)); if(!_drp->fields[_idx].val.str_val.s) { _drp->fields[_idx].nul = 1; return -1; } memcpy(_drp->fields[_idx].val.str_val.s, _vp->val.string_val, _drp->fields[_idx].val.str_val.len); break; case DB_FLOAT: _drp->fields[_idx].type = DB_FLOAT; _drp->fields[_idx].val.float_val = _vp->val.float_val; break; case DB_DOUBLE: _drp->fields[_idx].type = DB_DOUBLE; _drp->fields[_idx].val.double_val = _vp->val.double_val; break; case DB_INT: _drp->fields[_idx].type = DB_INT; _drp->fields[_idx].val.int_val = _vp->val.int_val; break; case DB_DATETIME: _drp->fields[_idx].type = DB_INT; _drp->fields[_idx].val.int_val = (int)_vp->val.time_val; break; default: LOG(L_ERR,"ERROR:dbtext: unsupported type %d in update\n",_t); _drp->fields[_idx].nul = 1; return -1; } } return 0; } /** * */ int dbt_table_check_row(dbt_table_p _dtp, dbt_row_p _drp) { int i; if(!_dtp || _dtp->nrcols <= 0 || !_drp) return -1; for(i=0; i<_dtp->nrcols; i++) { if(!_drp->fields[i].nul &&(_dtp->colv[i]->type!=_drp->fields[i].type)) { DBG("DBT:dbt_table_check_row: incompatible types - field %d\n",i); return -1; } if(_dtp->colv[i]->flag & DBT_FLAG_NULL) continue; if(!_drp->fields[i].nul) continue; if(_dtp->colv[i]->type==DB_INT && (_dtp->colv[i]->flag & DBT_FLAG_AUTO) && i==_dtp->auto_col) { _drp->fields[i].nul = 0; _drp->fields[i].val.int_val = ++_dtp->auto_val; continue; } DBG("DBT:dbt_table_check_row: NULL value not allowed - field %d\n",i); return -1; } return 0; } /** * */ int dbt_table_update_flags(dbt_table_p _dtp, int _f, int _o, int _m) { if(!_dtp) return -1; if(_o == DBT_FL_SET) _dtp->flag |= _f; else if(_o == DBT_FL_UNSET) _dtp->flag &= ~_f; if(_m) _dtp->mark = (int)time(NULL); return 0; } kamailio-4.0.4/obsolete/dbtext/doc/0000755000000000000000000000000012223032460015650 5ustar rootrootkamailio-4.0.4/obsolete/dbtext/doc/dbtext-ser.cfg0000644000000000000000000000715612223032460020423 0ustar rootroot# # $Id$ # # simple quick-start config script with dbtext # # ----------- global configuration parameters ------------------------ #debug=9 # debug level (cmd line: -dddddddddd) #fork=yes #log_stderror=no # (cmd line: -E) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) children=4 listen=10.100.100.1 port=5060 fifo="/tmp/ser_fifo" alias=alpha.org # ------------------ module loading ---------------------------------- # use dbtext database loadmodule "../sip_router/modules/dbtext/dbtext.so" loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/rr/rr.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/usrloc/usrloc.so" loadmodule "../sip_router/modules/registrar/registrar.so" loadmodule "../sip_router/modules/textops/textops.so" # modules for digest authentication loadmodule "../sip_router/modules/auth/auth.so" loadmodule "../sip_router/modules/auth_db/auth_db.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- # use dbtext database for persistent storage modparam("usrloc", "db_mode", 2) modparam("usrloc|auth_db", "db_url", "dbtext:///tmp/serdb") # -- auth params -- # modparam("auth_db", "calculate_ha1", 1) modparam("auth_db", "password_column", "password") modparam("auth_db", "user_column", "username") modparam("auth_db", "domain_column", "domain") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); break; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!method=="REGISTER") record_route(); # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); break; }; if (!uri==myself) { # mark routing logic in request append_hf("P-hint: outbound\r\n"); route(1); break; }; # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (method=="REGISTER") { # digest authentication if (!www_authorize("", "subscriber")) { www_challenge("", "0"); break; }; save("location"); break; }; lookup("aliases"); if (!uri==myself) { append_hf("P-hint: outbound alias\r\n"); route(1); break; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); break; }; }; append_hf("P-hint: usrloc applied\r\n"); route(1); } route[1] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; } kamailio-4.0.4/obsolete/dbtext/doc/dbtext.xml0000644000000000000000000001765712223032460017704 0ustar rootroot
Daniel-Constantin Mierla FhG FOKUS
mierla@fokus.fraunhofer.de
2003 2004 FhG FOKUS
Dbtext Module
Overview The module implements a simplified database engine based on text files. It can be used by SER DB interface instead of other database module (like MySQL). The module is meant for use in demos or small devices that do not support other DB modules. It keeps everything in memory and if you deal with large amount of data you may run quickly out of memory. Also, it has not implemented all standard database facilities (like order by), it includes minimal functionality to work properly (who knows ?!?) with SER.
Design of Dbtext Engine The dbtext database system architecture: A database is represented by a directory on the local file system. When you use "dbtext" in SER, the database URL for modules must be the path to the directory where the table-files are located, prefixed by "dbtext://", e.g., "dbtext:///var/dbtext/ser". If there is no "/" after "dbtext://" then "CFG_DIR/" is inserted at the beginning of the database path. So, either you provide an absolute path to database directory or a relative one to "CFG_DIR" directory. A table is represented by a text file inside database directory.
Internal Format of a Dbtext Table First line is the definition of the columns. Each column must be declared as follows: the name of column must not include white spaces. the format of a column definition is: name(type,attr). between two column definitions must be a white space, e.g., "first_name(str) last_name(str)". the type of a column can be: int - integer numbers. double - real numbers with two decimals. str - strings with maximum size of 4KB. a column can have one of the attributes: auto - only for 'int' columns, the maximum value in that column is incremented and stored in this field if it is not provided in queries. null - accept null values in column fields. if no attribute is set, the fields of the column cannot have null value. each other line is a row with data. The line ends with "\n". the fields are separated by ":". no value between two ':' (or between ':' and start/end of a row) means "null" value. next characters must be escaped in strings: "\n", "\r", "\t", ":". 0 - The zero value must be escaped too. Sample of a dbtext table ... id(int,auto) name(str) flag(double) desc(str,null) 1:nick:0.34:a\tgood\:friend 2:cole:-3.75:colleague3:bob:2.50: ... Minimal SER location dbtext table definition ... username(str) contact(str) expires(int) q(double) callid(str) cseq(int) ... Minimal SER subscriber dbtext table example ... username(str) password(str) ha1(str) domain(str) ha1b(str) suser:supasswd:xxx:iptel.org:xxx ...
Installation And Running Compile the module and load it instead of mysql or other DB modules. When you use dbtext in SER, the database URL for modules must be the path to the directory where the table-files are located, prefixed by "dbtext://", e.g., "dbtext:///var/dbtext/ser". If there is no "/" after "dbtext://" then "CFG_DIR/" is inserted at the beginning of the database path. So, either you provide an absolute path to database directory or a relative one to "CFG_DIR" directory. Load the dbtext module ... loadmodule "/path/to/ser/modules/dbtext.so" ... modparam("module_name", "db_url", "dbtext:///path/to/dbtext/database") ...
Using Dbtext With Basic SER Configuration Here are the definitions for most important table as well as a basic configuration file to use dbtext with SER. The table structures may change in time and you will have to adjust next examples. You have to populate the table 'subscriber' by hand with user profiles in order to have authentication. To use with the given configuration file, the table files must be placed in the '/tmp/serdb' directory. Definition of 'subscriber' table (one line) ... username(str) domn(str) password(str) first_name(str) last_name(str) phone(str) email_address(str) datetime_created(int) datetime_modified(int) confirmation(str) flag(str) sendnotification(str) greeting(str) ha1(str) ha1b(str) perms(str) allow_find(str) timezone(str,null) rpid(str,null) uuid(str,null) ... Definition of 'location' and 'aliases' tables (one line) ... username(str) domain(str,null) contact(str,null) expires(int,null) q(double,null) callid(str,null) cseq(int,null) last_modified(str) replicate(int,null) state(int,null) flags(int) user_agent(str) received(str) ... Definition of 'version' table and sample records ... table_name(str) table_version(int) subscriber:3 location:6 aliases:6 ... Configuration file
kamailio-4.0.4/obsolete/dbtext/doc/Makefile0000644000000000000000000000012712223032460017310 0ustar rootrootdocs = dbtext.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/dbtext/README0000644000000000000000000002142512223032460015767 0ustar rootroot1. Dbtext Module Daniel-Constantin Mierla FhG FOKUS Copyright © 2003, 2004 FhG FOKUS __________________________________________________________________ 1.1. Overview 1.1.1. Design of Dbtext Engine 1.1.2. Internal Format of a Dbtext Table 1.2. Installation And Running 1.2.1. Using Dbtext With Basic SER Configuration 1.1. Overview The module implements a simplified database engine based on text files. It can be used by SER DB interface instead of other database module (like MySQL). The module is meant for use in demos or small devices that do not support other DB modules. It keeps everything in memory and if you deal with large amount of data you may run quickly out of memory. Also, it has not implemented all standard database facilities (like order by), it includes minimal functionality to work properly (who knows ?!?) with SER. 1.1.1. Design of Dbtext Engine The dbtext database system architecture: * A database is represented by a directory on the local file system. Note When you use "dbtext" in SER, the database URL for modules must be the path to the directory where the table-files are located, prefixed by "dbtext://", e.g., "dbtext:///var/dbtext/ser". If there is no "/" after "dbtext://" then "CFG_DIR/" is inserted at the beginning of the database path. So, either you provide an absolute path to database directory or a relative one to "CFG_DIR" directory. * A table is represented by a text file inside database directory. 1.1.2. Internal Format of a Dbtext Table First line is the definition of the columns. Each column must be declared as follows: * the name of column must not include white spaces. * the format of a column definition is: name(type,attr). * between two column definitions must be a white space, e.g., "first_name(str) last_name(str)". * the type of a column can be: + int - integer numbers. + double - real numbers with two decimals. + str - strings with maximum size of 4KB. * a column can have one of the attributes: + auto - only for 'int' columns, the maximum value in that column is incremented and stored in this field if it is not provided in queries. + null - accept null values in column fields. + if no attribute is set, the fields of the column cannot have null value. * each other line is a row with data. The line ends with "\n". * the fields are separated by ":". * no value between two ':' (or between ':' and start/end of a row) means "null" value. * next characters must be escaped in strings: "\n", "\r", "\t", ":". * 0 - The zero value must be escaped too. Example 1. Sample of a dbtext table ... id(int,auto) name(str) flag(double) desc(str,null) 1:nick:0.34:a\tgood\:friend 2:cole:-3.75:colleague3:bob:2.50: ... Example 2. Minimal SER location dbtext table definition ... username(str) contact(str) expires(int) q(double) callid(str) cseq(int) ... Example 3. Minimal SER subscriber dbtext table example ... username(str) password(str) ha1(str) domain(str) ha1b(str) suser:supasswd:xxx:iptel.org:xxx ... 1.2. Installation And Running Compile the module and load it instead of mysql or other DB modules. Note When you use dbtext in SER, the database URL for modules must be the path to the directory where the table-files are located, prefixed by "dbtext://", e.g., "dbtext:///var/dbtext/ser". If there is no "/" after "dbtext://" then "CFG_DIR/" is inserted at the beginning of the database path. So, either you provide an absolute path to database directory or a relative one to "CFG_DIR" directory. Example 4. Load the dbtext module ... loadmodule "/path/to/ser/modules/dbtext.so" ... modparam("module_name", "db_url", "dbtext:///path/to/dbtext/database") ... 1.2.1. Using Dbtext With Basic SER Configuration Here are the definitions for most important table as well as a basic configuration file to use dbtext with SER. The table structures may change in time and you will have to adjust next examples. You have to populate the table 'subscriber' by hand with user profiles in order to have authentication. To use with the given configuration file, the table files must be placed in the '/tmp/serdb' directory. Example 5. Definition of 'subscriber' table (one line) ... username(str) domn(str) password(str) first_name(str) last_name(str) phone(str) email_address(str) datetime_created(int) datetime_modified(int) confirmation(str) flag(str) sendnotification(str) greeting(str) ha1(str) ha1b(str) perms(str) allow_find(str) timezone(str,null) rpid(str,null) uuid(str,null) ... Example 6. Definition of 'location' and 'aliases' tables (one line) ... username(str) domain(str,null) contact(str,null) expires(int,null) q(double,null) callid(str,null) cseq(int,null) last_modified(str) replicate(int,null) state(int,null) flags(int) user_agent(str) received(str) ... Example 7. Definition of 'version' table and sample records ... table_name(str) table_version(int) subscriber:3 location:6 aliases:6 ... Example 8. Configuration file # # $Id$ # # simple quick-start config script with dbtext # # ----------- global configuration parameters ------------------------ #debug=9 # debug level (cmd line: -dddddddddd) #fork=yes #log_stderror=no # (cmd line: -E) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) children=4 listen=10.100.100.1 port=5060 fifo="/tmp/ser_fifo" alias=alpha.org # ------------------ module loading ---------------------------------- # use dbtext database loadmodule "../sip_router/modules/dbtext/dbtext.so" loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/rr/rr.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/usrloc/usrloc.so" loadmodule "../sip_router/modules/registrar/registrar.so" loadmodule "../sip_router/modules/textops/textops.so" # modules for digest authentication loadmodule "../sip_router/modules/auth/auth.so" loadmodule "../sip_router/modules/auth_db/auth_db.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- # use dbtext database for persistent storage modparam("usrloc", "db_mode", 2) modparam("usrloc|auth_db", "db_url", "dbtext:///tmp/serdb") # -- auth params -- # modparam("auth_db", "calculate_ha1", 1) modparam("auth_db", "password_column", "password") modparam("auth_db", "user_column", "username") modparam("auth_db", "domain_column", "domain") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); break; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!method=="REGISTER") record_route(); # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); break; }; if (!uri==myself) { # mark routing logic in request append_hf("P-hint: outbound\r\n"); route(1); break; }; # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (method=="REGISTER") { # digest authentication if (!www_authorize("", "subscriber")) { www_challenge("", "0"); break; }; save("location"); break; }; lookup("aliases"); if (!uri==myself) { append_hf("P-hint: outbound alias\r\n"); route(1); break; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); break; }; }; append_hf("P-hint: usrloc applied\r\n"); route(1); } route[1] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; } kamailio-4.0.4/obsolete/dbtext/dbt_api.h0000644000000000000000000000376412223032460016670 0ustar rootroot/* * $Id$ * * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText library * * 2003-02-05 created by Daniel * */ #ifndef _DBT_API_H_ #define _DBT_API_H_ #include "../../db/db_op.h" #include "../../lib/srdb2/db_res.h" #include "../../lib/srdb2/db_con.h" #include "../../db/db_row.h" int dbt_free_columns(db_res_t* _r); /* * Release memory used by row */ int dbt_free_row(db_row_t* _r); /* * Release memory used by rows */ int dbt_free_rows(db_res_t* _r); /* * Release memory used by a result structure */ int dbt_free_result(db_res_t* _r); /* * Retrieve result set */ int dbt_get_result(db_con_t* _h, db_res_t** _r); /* * Get and convert columns from a result */ int dbt_get_columns(db_con_t* _h, db_res_t* _r); /* * Convert rows from mysql to db API representation */ int dbt_convert_rows(db_con_t* _h, db_res_t* _r); /* * Convert a row from result into db API representation */ int dbt_convert_row(db_con_t* _h, db_res_t* _res, db_row_t* _r); int dbt_use_table(db_con_t* _h, const char* _t); #endif kamailio-4.0.4/obsolete/dbtext/dbt_lib.c0000644000000000000000000002241212223032460016647 0ustar rootroot/* * $Id$ * * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText library * * 2003-01-30 created by Daniel * */ #include #include #include #include #include #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "dbt_util.h" #include "dbt_lib.h" static dbt_cache_p *_cachedb = NULL; static gen_lock_t *_cachesem = NULL; /** * */ int dbt_init_cache() { if(!_cachesem) { /* init locks */ _cachesem = lock_alloc(); if(!_cachesem) { LOG(L_CRIT,"dbtext:dbt_init_cache: could not alloc a lock\n"); return -1; } if (lock_init(_cachesem)==0) { LOG(L_CRIT,"dbtext:dbt_init_cache: could not initialize a lock\n"); lock_dealloc(_cachesem); return -1; } } /* init pointer to caches list */ if (!_cachedb) { _cachedb = shm_malloc( sizeof(dbt_cache_p) ); if (!_cachedb) { LOG(L_CRIT,"dbtext:dbt_init_cache: no enough shm mem\n"); lock_dealloc(_cachesem); return -1; } *_cachedb = NULL; } return 0; } /** * */ dbt_cache_p dbt_cache_get_db(str *_s) { dbt_cache_p _dcache=NULL;; if(!_cachesem || !_cachedb) { LOG(L_ERR, "DBT:dbt_cache_get_db:dbtext cache is not initialized!\n"); return NULL; } if(!_s || !_s->s || _s->len<=0) return NULL; DBG("DBT:dbt_cache_get_db: looking for db %.*s!\n",_s->len,_s->s); lock_get(_cachesem); _dcache = *_cachedb; while(_dcache) { lock_get(&_dcache->sem); if(_dcache->dbp) { if(_dcache->dbp->name.len==_s->len && !strncasecmp(_dcache->dbp->name.s, _s->s, _s->len)) { lock_release(&_dcache->sem); DBG("DBT:dbt_cache_get_db: db already cached!\n"); goto done; } } lock_release(&_dcache->sem); _dcache = _dcache->next; } if(!dbt_is_database(_s)) { LOG(L_ERR, "DBT:dbt_cache_get_db: database [%.*s] does not exists!\n", _s->len, _s->s); goto done; } DBG("DBT:dbt_cache_get_db: new db!\n"); _dcache = (dbt_cache_p)shm_malloc(sizeof(dbt_cache_t)); if(!_dcache) { LOG(L_ERR, "DBT:dbt_cache_get_db: no memory for dbt_cache_t.\n"); goto done; } _dcache->dbp = (dbt_db_p)shm_malloc(sizeof(dbt_db_t)); if(!_dcache->dbp) { LOG(L_ERR, "DBT:dbt_cache_get_db: no memory for dbt_db_t!\n"); shm_free(_dcache); goto done; } _dcache->dbp->name.s = (char*)shm_malloc(_s->len*sizeof(char)); if(!_dcache->dbp->name.s) { LOG(L_ERR, "DBT:dbt_cache_get_db: no memory for s!!\n"); shm_free(_dcache->dbp); shm_free(_dcache); _dcache = NULL; goto done; } memcpy(_dcache->dbp->name.s, _s->s, _s->len); _dcache->dbp->name.len = _s->len; _dcache->dbp->tables = NULL; if(!lock_init(&_dcache->sem)) { LOG(L_ERR, "DBT:dbt_cache_get_db: no sems!\n"); shm_free(_dcache->dbp->name.s); shm_free(_dcache->dbp); shm_free(_dcache); _dcache = NULL; goto done; } _dcache->prev = NULL; if(*_cachedb) { _dcache->next = *_cachedb; (*_cachedb)->prev = _dcache; } else _dcache->next = NULL; *_cachedb = _dcache; done: lock_release(_cachesem); return _dcache; } /** * */ int dbt_cache_check_db(str *_s) { dbt_cache_p _dcache=NULL;; if(!_cachesem || !(*_cachedb) || !_s || !_s->s || _s->len<=0) return -1; lock_get(_cachesem); _dcache = *_cachedb; while(_dcache) { if(_dcache->dbp) { if(_dcache->dbp->name.len == _s->len && strncasecmp(_dcache->dbp->name.s, _s->s, _s->len)) { lock_release(_cachesem); return 0; } } _dcache = _dcache->next; } lock_release(_cachesem); return -1; } /** * */ int dbt_cache_del_db(str *_s) { dbt_cache_p _dcache=NULL;; if(!_cachesem || !(*_cachedb) || !_s || !_s->s || _s->len<=0) return -1; lock_get(_cachesem); _dcache = *_cachedb; while(_dcache) { if(_dcache->dbp) { if(_dcache->dbp->name.len == _s->len && strncasecmp(_dcache->dbp->name.s, _s->s, _s->len)) break; } // else - delete this cell _dcache = _dcache->next; } if(!_dcache) { lock_release(_cachesem); return 0; } if(_dcache->prev) (_dcache->prev)->next = _dcache->next; else *_cachedb = _dcache->next; if(_dcache->next) (_dcache->next)->prev = _dcache->prev; lock_release(_cachesem); dbt_cache_free(_dcache); return 0; } /** * */ tbl_cache_p dbt_db_get_table(dbt_cache_p _dc, str *_s) { // dbt_db_p _dbp = NULL; tbl_cache_p _tbc = NULL; dbt_table_p _dtp = NULL; if(!_dc || !_s || !_s->s || _s->len<=0) return NULL; lock_get(&_dc->sem); if(!_dc->dbp) { lock_release(&_dc->sem); return NULL; } _tbc = _dc->dbp->tables; while(_tbc) { if(_tbc->dtp) { lock_get(&_tbc->sem); if(_tbc->dtp->name.len == _s->len && !strncasecmp(_tbc->dtp->name.s, _s->s, _s->len )) { lock_release(&_tbc->sem); lock_release(&_dc->sem); return _tbc; } lock_release(&_tbc->sem); } _tbc = _tbc->next; } // new table _tbc = tbl_cache_new(); if(!_tbc) { lock_release(&_dc->sem); return NULL; } _dtp = dbt_load_file(_s, &(_dc->dbp->name)); #ifdef DBT_EXTRA_DEBUG DBG("DTB:dbt_db_get_table: %.*s\n", _s->len, _s->s); dbt_print_table(_dtp, NULL); #endif if(!_dtp) { lock_release(&_dc->sem); return NULL; } _tbc->dtp = _dtp; if(_dc->dbp->tables) (_dc->dbp->tables)->prev = _tbc; _tbc->next = _dc->dbp->tables; _dc->dbp->tables = _tbc; lock_release(&_dc->sem); return _tbc; } /** * */ int dbt_db_del_table(dbt_cache_p _dc, str *_s) { tbl_cache_p _tbc = NULL; if(!_dc || !_s || !_s->s || _s->len<=0) return -1; lock_get(&_dc->sem); if(!_dc->dbp) { lock_release(&_dc->sem); return -1; } _tbc = _dc->dbp->tables; while(_tbc) { if(_tbc->dtp) { lock_get(&_tbc->sem); if(_tbc->dtp->name.len == _s->len && !strncasecmp(_tbc->dtp->name.s, _s->s, _s->len)) { if(_tbc->prev) (_tbc->prev)->next = _tbc->next; else _dc->dbp->tables = _tbc->next; if(_tbc->next) (_tbc->next)->prev = _tbc->prev; break; } lock_release(&_tbc->sem); } _tbc = _tbc->next; } lock_release(&_dc->sem); tbl_cache_free(_tbc); return 0; } /** * */ int dbt_cache_destroy() { dbt_cache_p _dc=NULL, _dc0=NULL; if(!_cachesem) return -1; lock_get(_cachesem); if( _cachedb!=NULL ) { _dc = *_cachedb; while(_dc) { _dc0 = _dc; _dc = _dc->next; dbt_cache_free(_dc0); } shm_free(_cachedb); } lock_destroy(_cachesem); lock_dealloc(_cachesem); return 0; } /** * */ int dbt_cache_print(int _f) { dbt_cache_p _dc=NULL; tbl_cache_p _tbc = NULL; if(!_cachesem) return -1; lock_get(_cachesem); _dc = *_cachedb; while(_dc) { lock_get(&_dc->sem); if(_dc->dbp) { if(_f) fprintf(stdout, "\n--- Database [%.*s]\n", _dc->dbp->name.len, _dc->dbp->name.s); _tbc = _dc->dbp->tables; while(_tbc) { lock_get(&_tbc->sem); if(_tbc->dtp) { if(_f) { fprintf(stdout, "\n----- Table [%.*s]\n", _tbc->dtp->name.len, _tbc->dtp->name.s); fprintf(stdout, "------- LA=<%d> FL=<%x> AC=<%d>" " AV=<%d>\n", _tbc->dtp->mark, _tbc->dtp->flag, _tbc->dtp->auto_col, _tbc->dtp->auto_val); dbt_print_table(_tbc->dtp, NULL); } else { if(_tbc->dtp->flag & DBT_TBFL_MODI) { dbt_print_table(_tbc->dtp, &(_dc->dbp->name)); dbt_table_update_flags(_tbc->dtp,DBT_TBFL_MODI, DBT_FL_UNSET, 0); } } } lock_release(&_tbc->sem); _tbc = _tbc->next; } } lock_release(&_dc->sem); _dc = _dc->next; } lock_release(_cachesem); return 0; } /** * */ int dbt_cache_free(dbt_cache_p _dc) { if(!_dc) return -1; lock_get(&_dc->sem); if(_dc->dbp) dbt_db_free(_dc->dbp); lock_destroy(&_dc->sem); shm_free(_dc); return 0; } /** * */ int dbt_db_free(dbt_db_p _dbp) { tbl_cache_p _tbc = NULL, _tbc0=NULL; if(!_dbp) return -1; _tbc = _dbp->tables; while(_tbc) { _tbc0 = _tbc; tbl_cache_free(_tbc0); _tbc = _tbc->next; } if(_dbp->name.s) shm_free(_dbp->name.s); shm_free(_dbp); return 0; } /** * */ tbl_cache_p tbl_cache_new() { tbl_cache_p _tbc = NULL; _tbc = (tbl_cache_p)shm_malloc(sizeof(tbl_cache_t)); if(!_tbc) return NULL; if(!lock_init(&_tbc->sem)) { shm_free(_tbc); return NULL; } return _tbc; } /** * */ int tbl_cache_free(tbl_cache_p _tbc) { // FILL IT IN ????????????? if(!_tbc) return -1; lock_get(&_tbc->sem); if(_tbc->dtp) dbt_table_free(_tbc->dtp); lock_destroy(&_tbc->sem); shm_free(_tbc); return 0; } kamailio-4.0.4/obsolete/dbtext/dbt_lib.h0000644000000000000000000000673312223032460016664 0ustar rootroot/* * $Id$ * * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText library * * 2003-01-30 created by Daniel * */ #ifndef _DBT_LIB_H_ #define _DBT_LIB_H_ #include "../../str.h" #include "../../db/db_val.h" #include "../../locking.h" #define DBT_FLAG_UNSET 0 #define DBT_FLAG_NULL 1 #define DBT_FLAG_AUTO 2 #define DBT_TBFL_ZERO 0 #define DBT_TBFL_MODI 1 #define DBT_FL_IGN -1 #define DBT_FL_SET 0 #define DBT_FL_UNSET 1 #define DBT_DELIM ':' #define DBT_DELIM_C ' ' #define DBT_DELIM_R '\n' /*** typedef struct _dbt_val { int null; union { int int_val; double double_val; str str_val; } val; } dbt_val_t, *dbt_val_p; ***/ typedef db_val_t dbt_val_t, *dbt_val_p; typedef struct _dbt_row { dbt_val_p fields; struct _dbt_row *prev; struct _dbt_row *next; } dbt_row_t, *dbt_row_p; typedef struct _dbt_column { str name; int type; int flag; struct _dbt_column *prev; struct _dbt_column *next; } dbt_column_t, *dbt_column_p; typedef struct _dbt_table { str name; int mark; int flag; int auto_col; int auto_val; int nrcols; dbt_column_p cols; dbt_column_p *colv; int nrrows; dbt_row_p rows; } dbt_table_t, *dbt_table_p; typedef struct _tbl_cache { gen_lock_t sem; dbt_table_p dtp; struct _tbl_cache *prev; struct _tbl_cache *next; } tbl_cache_t, *tbl_cache_p; typedef struct _dbt_database { str name; tbl_cache_p tables; } dbt_db_t, *dbt_db_p; typedef struct _dbt_cache { gen_lock_t sem; dbt_db_p dbp; struct _dbt_cache *prev; struct _dbt_cache *next; } dbt_cache_t, *dbt_cache_p; int dbt_init_cache(); int dbt_cache_destroy(); int dbt_cache_print(int); dbt_cache_p dbt_cache_get_db(str*); int dbt_cache_check_db(str*); int dbt_cache_del_db(str*); tbl_cache_p dbt_db_get_table(dbt_cache_p, str*); int dbt_db_del_table(dbt_cache_p, str*); int dbt_db_free(dbt_db_p); int dbt_cache_free(dbt_cache_p); dbt_column_p dbt_column_new(char*, int); dbt_row_p dbt_row_new(int); dbt_table_p dbt_table_new(char*, int); tbl_cache_p tbl_cache_new(); int dbt_row_free(dbt_table_p, dbt_row_p); int dbt_column_free(dbt_column_p); int dbt_table_free_rows(dbt_table_p); int dbt_table_free(dbt_table_p); int tbl_cache_free(tbl_cache_p); int dbt_row_set_val(dbt_row_p, dbt_val_p, int, int); int dbt_row_update_val(dbt_row_p, dbt_val_p, int, int); int dbt_table_add_row(dbt_table_p, dbt_row_p); int dbt_table_check_row(dbt_table_p, dbt_row_p); int dbt_table_update_flags(dbt_table_p, int, int, int); dbt_table_p dbt_load_file(str *, str *); int dbt_print_table(dbt_table_p, str *); #endif kamailio-4.0.4/obsolete/dbtext/dbt_res.h0000644000000000000000000000440512223032460016701 0ustar rootroot/* * $Id$ * * DBText module core functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText module interface * * 2003-02-04 created by Daniel * */ #ifndef _DBT_RES_H_ #define _DBT_RES_H_ #include "../../db/db_op.h" #include "../../lib/srdb2/db_res.h" #include "dbt_lib.h" typedef struct _dbt_result { int nrcols; int nrrows; dbt_column_p colv; dbt_row_p rows; } dbt_result_t, *dbt_result_p; //typedef db_res_t dbt_result_t, *dbt_result_p; typedef struct _dbt_con { dbt_cache_p con; dbt_result_p res; dbt_row_p row; } dbt_con_t, *dbt_con_p; #define DBT_CON_CONNECTION(db_con) (((dbt_con_p)((db_con)->tail))->con) #define DBT_CON_RESULT(db_con) (((dbt_con_p)((db_con)->tail))->res) #define DBT_CON_ROW(db_con) (((dbt_con_p)((db_con)->tail))->row) dbt_result_p dbt_result_new(dbt_table_p, int*, int); int dbt_result_free(dbt_result_p); int dbt_row_match(dbt_table_p _dtp, dbt_row_p _drp, int* _lkey, db_op_t* _op, db_val_t* _v, int _n); int dbt_result_extract_fields(dbt_table_p _dtp, dbt_row_p _drp, int* lres, dbt_result_p _dres); int dbt_result_print(dbt_result_p _dres); int* dbt_get_refs(dbt_table_p, db_key_t*, int); int dbt_cmp_val(dbt_val_p _vp, db_val_t* _v); dbt_row_p dbt_result_new_row(dbt_result_p _dres); int dbt_is_neq_type(db_type_t _t0, db_type_t _t1); #endif kamailio-4.0.4/obsolete/dbtext/dbt_file.c0000644000000000000000000003255112223032460017025 0ustar rootroot/* * $Id$ * * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText library * * 2003-02-03 created by Daniel * */ #include #include #include #include #include #include "../../mem/shm_mem.h" #include "../../mem/mem.h" #include "../../dprint.h" #include "dbt_util.h" #include "dbt_lib.h" /** * */ dbt_table_p dbt_load_file(str *tbn, str *dbn) { FILE *fin=NULL; char path[512], buf[4096]; int c, crow, ccol, bp, sign, max_auto; dbt_val_t dtval; dbt_table_p dtp = NULL; dbt_column_p colp, colp0 = NULL; dbt_row_p rowp, rowp0 = NULL; enum {DBT_FLINE_ST, DBT_NLINE_ST, DBT_DATA_ST} state; DBG("DBT:dbt_load_file: request for table [%.*s]\n", tbn->len, tbn->s); if(!tbn || !tbn->s || tbn->len<=0 || tbn->len>=255) return NULL; path[0] = 0; if(dbn && dbn->s && dbn->len>0) { DBG("DBT:dbt_load_file: db is [%.*s]\n", dbn->len, dbn->s); if(dbn->len+tbn->len<511) { strncpy(path, dbn->s, dbn->len); path[dbn->len] = '/'; strncpy(path+dbn->len+1, tbn->s, tbn->len); path[dbn->len+tbn->len+1] = 0; } } if(path[0] == 0) { strncpy(path, tbn->s, tbn->len); path[tbn->len] = 0; } DBG("DBT:dbt_load_file: loading file [%s]\n", path); fin = fopen(path, "rt"); if(!fin) return NULL; dtp = dbt_table_new(tbn->s, tbn->len); if(!dtp) goto done; state = DBT_FLINE_ST; crow = ccol = -1; colp = colp0 = NULL; rowp = rowp0 = NULL; c = fgetc(fin); max_auto = 0; while(c!=EOF) { switch(state) { case DBT_FLINE_ST: //DBG("DBT:dbt_load_file: state FLINE!\n"); bp = 0; while(c==DBT_DELIM_C) c = fgetc(fin); if(c==DBT_DELIM_R && !colp0) goto clean; if(c==DBT_DELIM_R) { if(dtp->nrcols <= 0) goto clean; dtp->colv = (dbt_column_p*) shm_malloc(dtp->nrcols*sizeof(dbt_column_p)); if(!dtp->colv) goto clean; colp0 = dtp->cols; for(ccol=0; ccolnrcols && colp0; ccol++) { dtp->colv[ccol] = colp0; colp0 = colp0->next; } state = DBT_NLINE_ST; break; } while(c!=DBT_DELIM_C && c!='(' && c!=DBT_DELIM_R) { if(c==EOF) goto clean; buf[bp++] = c; c = fgetc(fin); } colp = dbt_column_new(buf, bp); if(!colp) goto clean; //DBG("DBT:dbt_load_file: new col [%.*s]\n", bp, buf); while(c==DBT_DELIM_C) c = fgetc(fin); if(c!='(') goto clean; c = fgetc(fin); while(c==DBT_DELIM_C) c = fgetc(fin); switch(c) { case 's': case 'S': colp->type = DB_STR; DBG("DBT: column[%d] is STR!\n", ccol+1); break; case 'i': case 'I': colp->type = DB_INT; DBG("DBT: column[%d] is INT!\n", ccol+1); break; case 'f': case 'F': colp->type = DB_FLOAT; DBG("DBT: column[%d] is FLOAT!\n", ccol+1); break; case 'd': case 'D': colp->type = DB_DOUBLE; DBG("DBT: column[%d] is DOUBLE!\n", ccol+1); break; default: DBG("DBT: wrong column type!\n"); goto clean; } while(c!=')' && c!= ',') c = fgetc(fin); if(c==',') { //DBG("DBT: c=%c!\n", c); c = fgetc(fin); while(c==DBT_DELIM_C) c = fgetc(fin); if(c=='N' || c=='n') { //DBG("DBT:dbt_load_file: NULL flag set!\n"); colp->flag |= DBT_FLAG_NULL; } else if(colp->type==DB_INT && dtp->auto_col<0 && (c=='A' || c=='a')) { //DBG("DBT:dbt_load_file: AUTO flag set!\n"); colp->flag |= DBT_FLAG_AUTO; dtp->auto_col = ccol+1; } else goto clean; while(c!=')' && c!=DBT_DELIM_R && c!=EOF) c = fgetc(fin); } if(c == ')') { //DBG("DBT: c=%c!\n", c); if(colp0) { colp->prev = colp0; colp0->next = colp; } else dtp->cols = colp; colp0 = colp; dtp->nrcols++; c = fgetc(fin); } else goto clean; ccol++; break; case DBT_NLINE_ST: //DBG("DBT:dbt_load_file: state NLINE!\n"); while(c==DBT_DELIM_R) c = fgetc(fin); if(rowp) { if(dbt_table_check_row(dtp, rowp)) goto clean; if(!rowp0) dtp->rows = rowp; else { rowp0->next = rowp; rowp->prev = rowp0; } rowp0 = rowp; dtp->nrrows++; } if(c==EOF) break; crow++; ccol = 0; rowp = dbt_row_new(dtp->nrcols); if(!rowp) goto clean; state = DBT_DATA_ST; break; case DBT_DATA_ST: //DBG("DBT:dbt_load_file: state DATA!\n"); //while(c==DBT_DELIM) // c = fgetc(fin); if(ccol == dtp->nrcols && (c==DBT_DELIM_R || c==EOF)) { state = DBT_NLINE_ST; break; } if(ccol>= dtp->nrcols) goto clean; switch(dtp->colv[ccol]->type) { case DB_INT: //DBG("DBT:dbt_load_file: INT value!\n"); dtval.val.int_val = 0; dtval.type = DB_INT; if(c==DBT_DELIM || (ccol==dtp->nrcols-1 && (c==DBT_DELIM_R || c==EOF))) dtval.nul = 1; else { dtval.nul = 0; sign = 1; if(c=='-') { sign = -1; c = fgetc(fin); } if(c<'0' || c>'9') goto clean; while(c>='0' && c<='9') { dtval.val.int_val=dtval.val.int_val*10+c-'0'; c = fgetc(fin); } dtval.val.int_val *= sign; //DBG("DBT:dbt_load_file: data[%d,%d]=%d\n", crow, // ccol, dtval.val.int_val); } if(c!=DBT_DELIM && c!=DBT_DELIM_R && c!=EOF) goto clean; if(dbt_row_set_val(rowp,&dtval,DB_INT,ccol)) goto clean; if(ccol == dtp->auto_col) max_auto = (max_autonrcols-1 && (c==DBT_DELIM_R || c==EOF))) dtval.nul = 1; else { dtval.nul = 0; sign = 1; if(c=='-') { sign = -1; c = fgetc(fin); } if(c<'0' || c>'9') goto clean; while(c>='0' && c<='9') { dtval.val.float_val = dtval.val.float_val*10 + c - '0'; c = fgetc(fin); } if(c=='.') { c = fgetc(fin); bp = 1; while(c>='0' && c<='9') { bp *= 10; dtval.val.float_val+=((float)(c-'0'))/bp; c = fgetc(fin); } } dtval.val.float_val *= sign; //DBG("DBT:dbt_load_file: data[%d,%d]=%10.2f\n", // crow, ccol, dtval.val.float_val); } if(c!=DBT_DELIM && c!=DBT_DELIM_R && c!=EOF) goto clean; if(dbt_row_set_val(rowp,&dtval,DB_FLOAT,ccol)) goto clean; break; case DB_DOUBLE: //DBG("DBT:dbt_load_file: DOUBLE value!\n"); dtval.val.double_val = 0.0; dtval.type = DB_DOUBLE; if(c==DBT_DELIM || (ccol==dtp->nrcols-1 && (c==DBT_DELIM_R || c==EOF))) dtval.nul = 1; else { dtval.nul = 0; sign = 1; if(c=='-') { sign = -1; c = fgetc(fin); } if(c<'0' || c>'9') goto clean; while(c>='0' && c<='9') { dtval.val.double_val = dtval.val.double_val*10 + c - '0'; c = fgetc(fin); } if(c=='.') { c = fgetc(fin); bp = 1; while(c>='0' && c<='9') { bp *= 10; dtval.val.double_val+=((double)(c-'0'))/bp; c = fgetc(fin); } } dtval.val.double_val *= sign; //DBG("DBT:dbt_load_file: data[%d,%d]=%10.2f\n", // crow, ccol, dtval.val.double_val); } if(c!=DBT_DELIM && c!=DBT_DELIM_R && c!=EOF) goto clean; if(dbt_row_set_val(rowp,&dtval,DB_DOUBLE,ccol)) goto clean; break; case DB_STR: //DBG("DBT:dbt_load_file: STR value!\n"); dtval.val.str_val.s = NULL; dtval.val.str_val.len = 0; dtval.type = DB_STR; bp = 0; if(c==DBT_DELIM || (ccol == dtp->nrcols-1 && (c == DBT_DELIM_R || c==EOF))) dtval.nul = 1; else { dtval.nul = 0; while(c!=DBT_DELIM && c!=DBT_DELIM_R && c!=EOF) { if(c=='\\') { c = fgetc(fin); switch(c) { case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case '\\': c = '\\'; break; case DBT_DELIM: c = DBT_DELIM; break; case '0': c = 0; break; default: goto clean; } } buf[bp++] = c; c = fgetc(fin); } dtval.val.str_val.s = buf; dtval.val.str_val.len = bp; //DBG("DBT:dbt_load_file: data[%d,%d]=%.*s\n", /// crow, ccol, bp, buf); } if(c!=DBT_DELIM && c!=DBT_DELIM_R && c!=EOF) goto clean; if(dbt_row_set_val(rowp,&dtval,DB_STR,ccol)) goto clean; break; default: goto clean; } if(c==DBT_DELIM) c = fgetc(fin); ccol++; break; // state DBT_DATA_ST } } if(max_auto) dtp->auto_val = max_auto; done: if(fin) fclose(fin); return dtp; clean: /// ????? FILL IT IN - incomplete row/column // memory leak?!?! with last incomplete row DBG("DBT:dbt_load_file: error at row=%d col=%d c=%c\n", crow+1, ccol+1, c); if(dtp) dbt_table_free(dtp); return NULL; } /** * */ int dbt_print_table(dbt_table_p _dtp, str *_dbn) { dbt_column_p colp = NULL; dbt_row_p rowp = NULL; FILE *fout = NULL; int ccol; char *p, path[512]; if(!_dtp || !_dtp->name.s || _dtp->name.len <= 0) return -1; if(!_dbn || !_dbn->s || _dbn->len <= 0) { fout = stdout; fprintf(fout, "\n Content of [%.*s]\n", _dtp->name.len, _dtp->name.s); } else { if(_dtp->name.len+_dbn->len > 510) return -1; strncpy(path, _dbn->s, _dbn->len); path[_dbn->len] = '/'; strncpy(path+_dbn->len+1, _dtp->name.s, _dtp->name.len); path[_dbn->len+_dtp->name.len+1] = 0; fout = fopen(path, "wt"); if(!fout) return -1; } colp = _dtp->cols; while(colp) { switch(colp->type) { case DB_INT: fprintf(fout, "%.*s(int", colp->name.len, colp->name.s); break; case DB_FLOAT: fprintf(fout, "%.*s(float", colp->name.len, colp->name.s); break; case DB_DOUBLE: fprintf(fout, "%.*s(double", colp->name.len, colp->name.s); break; case DB_STR: fprintf(fout, "%.*s(str", colp->name.len, colp->name.s); break; default: if(fout!=stdout) fclose(fout); return -1; } if(colp->flag & DBT_FLAG_NULL) fprintf(fout,",null"); else if(colp->type==DB_INT && colp->flag & DBT_FLAG_AUTO) fprintf(fout,",auto"); fprintf(fout,")"); colp = colp->next; if(colp) fprintf(fout,"%c", DBT_DELIM_C); } fprintf(fout, "%c", DBT_DELIM_R); rowp = _dtp->rows; while(rowp) { for(ccol=0; ccol<_dtp->nrcols; ccol++) { switch(_dtp->colv[ccol]->type) { case DB_INT: if(!rowp->fields[ccol].nul) fprintf(fout,"%d", rowp->fields[ccol].val.int_val); break; case DB_FLOAT: if(!rowp->fields[ccol].nul) fprintf(fout, "%.2f", rowp->fields[ccol].val.float_val); break; case DB_DOUBLE: if(!rowp->fields[ccol].nul) fprintf(fout, "%.2f", rowp->fields[ccol].val.double_val); break; case DB_STR: if(!rowp->fields[ccol].nul) { p = rowp->fields[ccol].val.str_val.s; while(p < rowp->fields[ccol].val.str_val.s + rowp->fields[ccol].val.str_val.len) { switch(*p) { case '\n': fprintf(fout, "\\n"); break; case '\r': fprintf(fout, "\\r"); break; case '\t': fprintf(fout, "\\t"); break; case '\\': fprintf(fout, "\\\\"); break; case DBT_DELIM: fprintf(fout, "\\%c", DBT_DELIM); break; case '\0': fprintf(fout, "\\0"); break; default: fprintf(fout, "%c", *p); } p++; } } break; default: if(fout!=stdout) fclose(fout); return -1; } if(ccol<_dtp->nrcols-1) fprintf(fout, "%c",DBT_DELIM); } fprintf(fout, "%c", DBT_DELIM_R); rowp = rowp->next; } if(fout!=stdout) fclose(fout); return 0; } kamailio-4.0.4/obsolete/dbtext/dbt_util.h0000644000000000000000000000231312223032460017061 0ustar rootroot/* * $Id$ * * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText library * * 2003-01-30 created by Daniel * */ #ifndef _DBT_UTIL_H_ #define _DBT_UTIL_H_ #include "../../str.h" int dbt_is_database(str *); #endif kamailio-4.0.4/obsolete/dbtext/Makefile0000644000000000000000000000052612223032460016546 0ustar rootroot# $Id$ # # example module makefile # # # WARNING: do not run this directly, it should be run by the master Makefile # extra debug messages DEFS+=-DDBT_EXTRA_DEBUG include ../../Makefile.defs auto_gen= NAME=dbtext.so LIBS= DEFS+=-DSER_MOD_INTERFACE SERLIBPATH=../../lib SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/obsolete/dbtext/dbt_util.c0000644000000000000000000000270712223032460017063 0ustar rootroot/* * $Id$ * * DBText library * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText library * * 2003-01-30 created by Daniel * */ #include #include #include #include "dbt_util.h" /** * */ int dbt_is_database(str *_s) { DIR *dirp = NULL; char buf[512]; if(!_s || !_s->s || _s->len <= 0 || _s->len > 510) return 0; strncpy(buf, _s->s, _s->len); buf[_s->len] = 0; dirp = opendir(buf); if(!dirp) return 0; closedir(dirp); return 1; } kamailio-4.0.4/obsolete/dbtext/dbtext.h0000644000000000000000000000425612223032460016555 0ustar rootroot/* * $Id$ * * DBText module core functions * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * DBText module interface * * 2003-01-30 created by Daniel * */ #ifndef _DBTEXT_H_ #define _DBTEXT_H_ #include "../../lib/srdb2/db_con.h" #include "../../lib/srdb2/db_res.h" #include "../../db/db_key.h" #include "../../db/db_op.h" #include "../../db/db_val.h" /* * Initialize database connection */ db_con_t* dbt_init(const char* _sqlurl); /* * Close a database connection */ void dbt_close(db_con_t* _h); /* * Free all memory allocated by get_result */ int dbt_free_query(db_con_t* _h, db_res_t* _r); /* * Do a query */ int dbt_query(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc, db_key_t _o, db_res_t** _r); /* * Raw SQL query */ int dbt_raw_query(db_con_t* _h, char* _s, db_res_t** _r); /* * Insert a row into table */ int dbt_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n); /* * Delete a row from table */ int dbt_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n); /* * Update a row in table */ int dbt_update(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un); #endif kamailio-4.0.4/obsolete/presence_b2b/0000755000000000000000000000000012223032460016142 5ustar rootrootkamailio-4.0.4/obsolete/presence_b2b/euac_funcs.h0000644000000000000000000000340312223032460020426 0ustar rootroot#ifndef __EUAC_FUNCS_H #define __EUAC_FUNCS_H #include "events_uac.h" /* manipulation with message */ void extract_contact(struct sip_msg *m, str *dst); /* creating/recreating subscriptions * if failover_time > 0 it calls euac_set_timer to this value * noth these functions returns nonzero on error - this MUST * be handled everywhere */ int new_subscription(events_uac_t *uac, str *contact_to_send, int failover_time); int renew_subscription(events_uac_t *uac, int expires, int failover_time); /* */ events_uac_t *find_euac_nolock(struct sip_msg *m); void euac_set_timer(events_uac_t *uac, int seconds); void euac_clear_timer(events_uac_t *uac); int remove_euac_reference_nolock(events_uac_t *uac); void remove_uac_from_list(events_uac_t *uac); void insert_uac_to_list(events_uac_t *uac); /* processing NOTIFY requests (always sends OK response) */ void do_notification(events_uac_t *uac, struct sip_msg *m); void discard_notification(events_uac_t *uac, struct sip_msg *m, int res_code, char *msg); void refresh_dialog(events_uac_t *uac, struct sip_msg *m); void refresh_dialog_resp(events_uac_t *uac, struct sip_msg *m); /* waiting time after error (before new attempt about subscription) */ extern int resubscribe_timeout_on_err; /* time specifying how long wait for terminating NOTIFY * after 2xx response on SUBSCRIBE with 0 expires*/ extern int waiting_for_notify_time; /* default subscription duration */ extern int subscribe_time; /* time interval before expiration when should be the subscription refreshed * (recommended are some seconds before) */ extern int resubscribe_delta; /* minimum time for resubscriptions */ extern int min_resubscribe_time; extern int failover_timeout; /* for randomized start of subscriptions */ extern int max_subscribe_delay; #endif kamailio-4.0.4/obsolete/presence_b2b/qsa_events.c0000644000000000000000000002360312223032460020462 0ustar rootroot#include "qsa_events.h" #include #include #include #include #include #include "euac_internals.h" #include #include #include #include #include #include "../../parser/parse_content.h" /* typedef struct { qsa_subscription_t *s; enum { internal_subscribe, internal_unsubscribe } action; } internal_subscription_msg_t; typedef struct { msg_queue_t s_msgs; internal_rl_subscription_t *first, *last; } rls_internal_data_t; */ static notifier_domain_t *domain = NULL; static notifier_t *presence_notifier = NULL; /* package for SIP subscriptions */ static str presence_events_package = { s: "presence", len: 8 }; /* package for internal subscriptions */ static str presence_qsa_package = { s: "presence", len: 8 }; /* b2b UA notifier name*/ /* static str notifier_name = STR_STATIC_INIT("presence_b2b"); */ static int handle_presence_subscriptions = 0; static qsa_content_type_t *ct_presence_info = NULL; /* default route for presence UAC */ str presence_route = {s: "", len: 0 }; /* outbound proxy for presence UAC */ str presence_outbound_proxy = {s: "", len: 0 }; /* additional headers for presence subscriptions */ str presence_headers = {s: "", len: 0 }; typedef struct _events_subscription_t { events_uac_t *uac; /* SIP subscription */ qsa_subscription_t *internal_subscription; struct _events_subscription_t *next, *prev; } events_subscription_t; typedef struct { events_subscription_t *presence_subscriptions_first, *presence_subscriptions_last; hash_table_t presence_ht; cds_mutex_t mutex; } event_qsa_internals_t; static event_qsa_internals_t *internals = NULL; void lock_events_qsa() { if (internals) cds_mutex_lock(&internals->mutex); } void unlock_events_qsa() { if (internals) cds_mutex_unlock(&internals->mutex); } /* ************** presence operations *************** */ static events_subscription_t *find_presence_subscription(qsa_subscription_t *subscription) { events_subscription_t *es; /* ERR("searching ES %.*s, %.*s\n", FMT_STR(subscription->record_id), FMT_STR(subscription->subscriber_id));*/ es = (events_subscription_t *)ht_find(&internals->presence_ht, subscription); return es; } static qsa_subscription_status_t get_subscription_status(struct sip_msg *m) { struct hdr_field *h; static str ss = STR_STATIC_INIT("Subscription-State"); static str terminated = STR_STATIC_INIT("terminated"); static str active = STR_STATIC_INIT("active"); static str pending = STR_STATIC_INIT("pending"); /* FIXME: correct Subscription-Status parsing */ if (parse_headers(m, HDR_EOH_F, 0) == -1) { ERR("can't parse message\n"); return qsa_subscription_pending; } h = m->headers; while (h) { /* try to find Subscription-Status with "terminated" */ if (str_nocase_equals(&h->name, &ss) == 0) { if (str_str(&h->body, &terminated)) return qsa_subscription_terminated; if (str_str(&h->body, &active)) return qsa_subscription_active; if (str_str(&h->body, &pending)) return qsa_subscription_pending; else return qsa_subscription_pending; } h = h->next; } /* header not found */ return qsa_subscription_pending; } static void presence_notification_cb(events_uac_t *uac, struct sip_msg *m, void *param) { qsa_subscription_t *subscription = (qsa_subscription_t*)param; char *body = NULL; int len = 0; presentity_info_t *p = NULL; qsa_subscription_status_t status = qsa_subscription_active; if (!subscription) return; DBG("received notification for %p\n", subscription); /* get body */ if (m) { body = get_body(m); if (parse_headers(m, HDR_CONTENTLENGTH_F, 0) < 0) len = 0; else { if (m->content_length) len = get_content_length(m); } status = get_subscription_status(m); /* parse as PIDF if given */ if (len > 0) { if (parse_pidf_document(&p, body, len) != 0) { ERR("can't parse PIDF document\n"); return; } } else DBG("received empty notification - s: %p, usr data: %p\n", subscription, get_subscriber_data(subscription)); } else { /* notify about status */ switch (uac->status) { case euac_unconfirmed: case euac_unconfirmed_destroy: status = qsa_subscription_pending; break; case euac_confirmed: case euac_resubscription: case euac_resubscription_destroy: case euac_predestroyed: status = qsa_subscription_active; break; case euac_destroyed: case euac_waiting: case euac_waiting_for_termination: status = qsa_subscription_terminated; break; } } /* send the message to internal subscriber */ notify_subscriber(subscription, presence_notifier, ct_presence_info, p, status); } static events_subscription_t *create_presence_subscription(qsa_subscription_t *subscription) { events_subscription_t *es; es = (events_subscription_t*) mem_alloc(sizeof(*es)); if (!es) { ERR("can't allocate memory\n"); return es; } memset(es, 0, sizeof(*es)); es->internal_subscription = subscription; es->uac = create_events_uac(get_record_id(subscription), get_subscriber_id(subscription), &presence_events_package, presence_notification_cb, /* callback function */ subscription, /* parameter for callback */ &presence_headers, /* additional headers */ &presence_route, &presence_outbound_proxy); if (!es->uac) { mem_free(es); return NULL; } /* ERR("created new ES (%p, uac = %p) %.*s, %.*s\n", es, es->uac, FMT_STR(subscription->record_id), FMT_STR(subscription->subscriber_id)); */ return es; } static int add_presence_subscription(events_subscription_t *es) { DBG("adding events_subscription into HT\n"); if (ht_add(&internals->presence_ht, es->internal_subscription, es) != 0) { ERR("can't add ES %p into hash table\n", es); return -1; } DOUBLE_LINKED_LIST_ADD(internals->presence_subscriptions_first, internals->presence_subscriptions_last, es); return 0; } static void destroy_presence_subscription(events_subscription_t *es) { ht_remove(&internals->presence_ht, es->internal_subscription); DOUBLE_LINKED_LIST_REMOVE(internals->presence_subscriptions_first, internals->presence_subscriptions_last, es); mem_free(es); } static int presence_subscribe(notifier_t *n, qsa_subscription_t *subscription) { events_subscription_t *es; str *record_id = NULL; if (!handle_presence_subscriptions) return 0; record_id = get_record_id(subscription); if (record_id) { DBG("internal subscribe to presence_b2b for %.*s [%.*s]\n", FMT_STR(*record_id), FMT_STR(subscription->package->name)); } else { ERR("BUG: subscription to empty record id\n"); return -1; } lock_events_qsa(); es = create_presence_subscription(subscription); if (!es) { ERR("can't create subscription to presence_b2b\n"); unlock_events_qsa(); return -1; } if (add_presence_subscription(es) != 0) { if (es->uac) destroy_events_uac(es->uac); mem_free(es); /* not linked nor in hash table -> free only */ unlock_events_qsa(); return -1; } unlock_events_qsa(); DBG("internal subscribe to presence_b2b finished\n"); return 0; } static void presence_unsubscribe(notifier_t *n, qsa_subscription_t *subscription) { events_subscription_t *es; if (!handle_presence_subscriptions) return; DBG("internal unsubscribe to presence_b2b for %p\n", subscription); lock_events_qsa(); /* try to find internal structure with this record, subscriber */ es = find_presence_subscription(subscription); if (!es) { /* subscription doesn't exist */ INFO("unsubscribe to nonexisting ES\n"); unlock_events_qsa(); return; } /* destroy SIP subscription */ if (es->uac) destroy_events_uac(es->uac); /* destroy this events subscription */ destroy_presence_subscription(es); unlock_events_qsa(); DBG("internal unsubscribe to presence_b2b finished\n"); } /************************************************************/ /* initialization / destruction + helper functions for that */ static unsigned int hash_events_subscription(qsa_subscription_t *s) { if (s) return rshash((char *)&s, sizeof(s)); else return 0; } static int cmp_events_subscription(qsa_subscription_t *a, qsa_subscription_t *b) { if (a == b) return 0; else return 1; } int events_qsa_interface_init(int _handle_presence_subscriptions) { static str presence_info = STR_STATIC_INIT(CT_PRESENCE_INFO); domain = qsa_get_default_domain(); if (!domain) { ERR("can't register notifier domain\n"); return -1; } ct_presence_info = register_content_type(domain, &presence_info, (destroy_function_f)free_presentity_info); if (!ct_presence_info) { ERR("can't register QSA content type\n"); return -1; } else TRACE("b2b_CONTENT_TYPE: %p\n", ct_presence_info); handle_presence_subscriptions = _handle_presence_subscriptions; presence_notifier = register_notifier(domain, &presence_qsa_package, presence_subscribe, presence_unsubscribe, NULL); if (!presence_notifier) { ERR("can't register notifier for presence\n"); return -1; } internals = (event_qsa_internals_t*)mem_alloc(sizeof(*internals)); if (!internals) { ERR("can't allocate memory\n"); return -1; } memset(internals, 0, sizeof(*internals)); cds_mutex_init(&internals->mutex); ht_init(&internals->presence_ht, (hash_func_t)hash_events_subscription, (key_cmp_func_t)cmp_events_subscription, 16603); return 0; } void events_qsa_interface_destroy() { if (domain) { if (presence_notifier) unregister_notifier(domain, presence_notifier); } presence_notifier = NULL; /* no new qsa subscriptions will be created now - now can be * released all existing ones */ if (domain) qsa_release_domain(domain); /* no QSA operations should be done there (don't send * notification messages, ...) only subscriptions may * be released */ domain = NULL; if (internals) { /* TODO: destroy all remaining subscriptions */ cds_mutex_destroy(&internals->mutex); ht_destroy(&internals->presence_ht); mem_free(internals); internals = NULL; } } kamailio-4.0.4/obsolete/presence_b2b/events_uac.h0000644000000000000000000000557412223032460020462 0ustar rootroot#ifndef __SIP_EVENTS_UAC_H #define __SIP_EVENTS_UAC_H /* SIP UAC able to generate SIP events subscriptions and process NOTIFY */ #include "../dialog/dlg_mod.h" /* this will be the dialog core in the future */ #include "../../timer.h" #include "../../timer_ticks.h" #include #include #include #include "trace.h" //typedef enum { // subscription_unconfirmed, /* after sent SUBSCRIBE */ // subscription_confirmed, /* after confirmation with 200 OK (enable NOTIFY for it too!) */ // subscription_resubscribing, /* after sent SUBSCRIBE */ // subscription_predestroyed, /* after 200 OK on unSUBSCRIBE */ // subscription_destroyed /* after last NOTIFY (may arrive in destroying status !) */ //} events_uac_status_t; typedef enum { euac_unconfirmed, /* 0 */ euac_unconfirmed_destroy, /* 1 */ euac_confirmed, /* 2 */ euac_waiting, /* 3 */ euac_resubscription, /* 4 */ euac_resubscription_destroy, /* 5 */ euac_waiting_for_termination, /* 6 */ euac_predestroyed, /* 7 */ euac_destroyed, /* 8 */ } events_uac_status_t; struct _events_uac_t; typedef struct _events_uac_t events_uac_t; typedef void (*notify_callback_func)(events_uac_t *uac, struct sip_msg *, void *param); struct _events_uac_t { /* SUBSCRIBE-NOTIFY dialog */ dlg_t *dialog; /* str aor, local_uri, contact; */ /* callback function for NOTIFY messages (don't use locking here !!! * it is always locked using events_uac mutex) */ notify_callback_func cb; /* parameter for callback function */ void *cbp; /* data needed for resubscriptinos */ str headers; str local_uri; str remote_uri; str route; str outbound_proxy; struct _events_uac_t *prev, *next; /* linked list ? */ events_uac_status_t status; /* reference counter - needed for freeing memory if * reference stored on more places */ reference_counter_data_t ref_cntr; struct timer_ln timer; int timer_started; /* debugging */ char id[64]; }; /* creates structure in shm and adds it into internal list */ events_uac_t *create_events_uac(str *remote_uri, str *local_uri, const str *events, notify_callback_func cb, /* callback function for processing NOTIFY messages (parsing, ...) */ void *cbp, /* parameter for callback function */ const str *other_headers, str *route, str *outbound_proxy); void free_events_uac(events_uac_t *uac); /* removes structure from memory and from internal lists*/ int destroy_events_uac(events_uac_t *uac); /* adds a reference to events_uac_t */ events_uac_t *find_events_uac(dlg_id_t *id); /* removes reference created by find_events_uac */ void remove_uac_reference(events_uac_t *uac); /* intitialize internal structures */ int events_uac_init(); /* destroy internal structures */ void events_uac_destroy(); /* tries to process given notify message */ int process_euac_notify(struct sip_msg* m); #endif kamailio-4.0.4/obsolete/presence_b2b/events_mod.c0000644000000000000000000000567512223032460020466 0ustar rootroot#include "events_mod.h" #include "../../sr_module.h" #include "../../mem/mem.h" #include "../../mem/shm_mem.h" #include #include #include #include #include #include #include "qsa_events.h" #include "rpc.h" #include #include "events_uac.h" #include "euac_funcs.h" MODULE_VERSION int events_mod_init(void); void events_mod_destroy(void); int events_child_init(int _rank); static int handle_notify(struct sip_msg* m) { int res; PROF_START(b2b_handle_notify) if (process_euac_notify(m) == 0) res = 1; else res = -1; PROF_STOP(b2b_handle_notify) return res; } /** Exported functions */ static cmd_export_t cmds[]={ {"handle_notify", (cmd_function)handle_notify, 0, 0, REQUEST_ROUTE}, {0, 0, 0, 0, 0} }; static int handle_presence_subscriptions = 1; /** Exported parameters */ static param_export_t params[]={ {"default_expiration", PARAM_INT, &subscribe_time }, {"on_error_retry_time", PARAM_INT, &resubscribe_timeout_on_err }, {"presence_route", PARAM_STR, &presence_route }, {"additional_presence_headers", PARAM_STR, &presence_headers }, {"wait_for_term_notify", PARAM_INT, &waiting_for_notify_time }, {"resubscribe_delta", PARAM_INT, &resubscribe_delta }, {"min_resubscribe_time", PARAM_INT, &min_resubscribe_time }, {"handle_presence_subscriptions", PARAM_INT, &handle_presence_subscriptions }, {"presence_outbound_proxy", PARAM_STR, &presence_outbound_proxy }, {"max_subscribe_delay", PARAM_INT, &max_subscribe_delay }, /* for randomized sent of SUBSCRIBE requests */ {0, 0, 0} }; struct module_exports exports = { "presence_b2b", cmds, /* Exported functions */ events_rpc_methods, /* RPC methods */ params, /* Exported parameters */ events_mod_init, /* module initialization function */ 0, /* response function*/ events_mod_destroy, /* pa_destroy, / * destroy function */ 0, /* oncancel function */ events_child_init /* per-child init function */ }; int events_mod_init(void) { DEBUG_LOG("presence_b2b module initialization\n"); /* ??? if other module uses this libraries it might be a problem ??? */ xmlInitParser(); DEBUG_LOG(" ... common libraries\n"); qsa_initialize(); if (events_uac_init() != 0) { return -1; } if (!handle_presence_subscriptions) { WARN("NOT handling presence subscriptions\n"); } if (events_qsa_interface_init(handle_presence_subscriptions) != 0) return -1; return 0; } int events_child_init(int _rank) { return 0; } void events_mod_destroy(void) { /*int i, cnt; char *s;*/ DEBUG_LOG("presence_b2b module cleanup\n"); DEBUG_LOG(" ... events UAC\n"); events_uac_destroy(); DEBUG_LOG(" ... qsa interface\n"); events_qsa_interface_destroy(); DEBUG_LOG(" ... common libs\n"); qsa_cleanup(); /* ??? if other module uses this libraries it might be a problem ??? */ /* xmlCleanupParser(); */ DEBUG_LOG("presence_b2b module cleanup finished\n"); } kamailio-4.0.4/obsolete/presence_b2b/euac_internals.c0000644000000000000000000000614712223032460021312 0ustar rootroot#include "euac_internals.h" #include "euac_funcs.h" events_uacs_internals_t *euac_internals = NULL; /* this function should move into dialog module ! */ static int cmp_unconfirmed_local_dlg_ids(dlg_id_t *a, dlg_id_t *b) { if (!a) { if (!b) return -1; else return 0; } if (!b) return 1; if (str_case_equals(&a->call_id, &b->call_id) != 0) return 1; if (str_case_equals(&a->loc_tag, &b->loc_tag) != 0) return 1; /* case sensitive ? */ /* FIXME: is really rem_tag the empty one? */ return 0; } int cmp_uac_unconfirmed_dlg(ht_key_t a, ht_key_t b) { events_uac_t *uac_a = (events_uac_t*)a; events_uac_t *uac_b = (events_uac_t*)b; dlg_t *dlg_a = NULL; dlg_t *dlg_b = NULL; if (uac_a) dlg_a = uac_a->dialog; if (uac_b) dlg_b = uac_b->dialog; if (dlg_a) { if (!dlg_b) return -1; else return cmp_unconfirmed_local_dlg_ids(&dlg_a->id, &dlg_b->id); } else { if (!dlg_b) return 0; } return -1; } int init_events_uac_internals() { load_tm_f load_tm; bind_dlg_mod_f bind_dlg; /* must be called only once */ euac_internals = mem_alloc(sizeof(*euac_internals)); if (!euac_internals) { ERR("can't allocate memory for internal UAC structures\n"); return -1; } /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) { ERR("Can't import tm!\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm(&euac_internals->tmb)==-1) { ERR("load_tm() failed\n"); return -1; } /* import the dialog auto-loading function */ bind_dlg = (bind_dlg_mod_f)find_export("bind_dlg_mod", -1, 0); if (!bind_dlg) { LOG(L_ERR, "Can't import dlg\n"); return -1; } if (bind_dlg(&euac_internals->dlgb) != 0) { ERR("bind_dlg_mod() failed\n"); return -1; } euac_internals->first_uac = NULL; euac_internals->last_uac = NULL; cds_mutex_init(&euac_internals->mutex); ht_init(&euac_internals->ht_confirmed, (hash_func_t)euac_internals->dlgb.hash_dlg_id, (key_cmp_func_t)euac_internals->dlgb.cmp_dlg_ids, 16603); ht_init(&euac_internals->ht_unconfirmed, (hash_func_t)euac_internals->dlgb.hash_dlg_id, (key_cmp_func_t)cmp_unconfirmed_local_dlg_ids, 2039); euac_internals->create_cnt = 0; euac_internals->destroy_cnt = 0; euac_internals->rc_grp = create_reference_counter_group(16); return 0; } void destroy_events_uac_internals() { events_uac_t *uac; if (euac_internals) { uac = euac_internals->first_uac; euac_internals->first_uac = NULL; euac_internals->last_uac = NULL; while (uac) { /* make sure that it will not change prev and next elements * during remove_euac_reference */ uac->prev = NULL; uac->next = NULL; remove_euac_reference_nolock(uac); uac = uac->next; } ht_destroy(&euac_internals->ht_confirmed); ht_destroy(&euac_internals->ht_unconfirmed); cds_mutex_destroy(&euac_internals->mutex); mem_free(euac_internals); euac_internals = NULL; } /* TRACE("Events uac destroyed\n"); */ } void lock_events_uac() { if (euac_internals) cds_mutex_lock(&euac_internals->mutex); } void unlock_events_uac() { if (euac_internals) cds_mutex_unlock(&euac_internals->mutex); } kamailio-4.0.4/obsolete/presence_b2b/qsa_events.h0000644000000000000000000000047512223032460020471 0ustar rootroot#ifndef __QSA_EVETNS_H #define __QSA_EVETNS_H #include "../../str.h" int events_qsa_interface_init(int _handle_presence_subscriptions); void events_qsa_interface_destroy(); /* default route for Events: presence */ extern str presence_route; extern str presence_outbound_proxy; extern str presence_headers; #endif kamailio-4.0.4/obsolete/presence_b2b/doc/0000755000000000000000000000000012223032460016707 5ustar rootrootkamailio-4.0.4/obsolete/presence_b2b/doc/presence_b2b.xml0000644000000000000000000000767712223032460022003 0ustar rootroot %docentities; ]>
Presence B2B UA Vaclav Kubart Iptel/Tekelec vaclav.kubart@iptel.org This module acts as back to back user agent for presence events. In the future it will do subscriptions to reg events and presence with resource lists too. It is accessible only using the internal Query Status API (QSA). Everywhere (in the C code) where you need subscriptions to presentity status, you only create an internal subscription to it and the rest is done by this module. It processes the internal subscription and creates a SIP subscription (SUBSCRIBE-NOTIFY dialog) to requested presentity. Every NOTIFY request produces new QSA message with status information into destination message queue. Instead of this module can be used PA module with parameter accept_internal_subscriptions set to 1. In that case the subscription will be only to internal status hold by PA. For every requested (record, subscriber) a new SIP subscription is created. This is due to authorization of such subscriptions - B2B UA can't decide about authorization rules, thus it has to leave it on the presence server to which it creates the subscription and this means, that it has to create the subscription as a subscriber. There is a possibility to do subscriptions with the same To + From only once, but this is left for future optimalizations (it will probably not help a lot).
Dependencies Modules tm dialog Libraries libcds (internal) libpresence (internal)
Usage This module can be used by RLS module (e.g. RLS module does internal subscriptions to "presence" which can be handled by this module or PA module). In near future it will be used by PA module for subscribing to users; this could be useful with UACs capable to process SUBSCRIBE/NOTIFY but not using PUBLISH for presence state publication (like Windows Messenger).
Configuration example ") modparam("presence_b2b", "on_error_retry_time", 600) modparam("presence_b2b", "wait_for_term_notify", 33) modparam("presence_b2b", "resubscribe_delta", 15) modparam("presence_b2b", "min_resubscribe_time", 55) modparam("presence_b2b", "default_expiration", 667) ... route { ... if (uri==myself) { ... if (method=="NOTIFY") { if (!t_newtran()) { log(1, "newtran error\n"); sl_reply_error(); }; if (!handle_notify()) { t_reply("481", "Unable to handle notification"); } break; }; ... } ... } ]]>
kamailio-4.0.4/obsolete/presence_b2b/doc/functions.xml0000644000000000000000000000141612223032460021443 0ustar rootroot
Functions handle_notify() Handles NOTIFY request. The module tries to find the subscription to which the NOTIFY belongs to (it tries both - confirmed and unconfirmed subscriptions because NOTIFY may arrive before 2xx response on SUBSCRIBE request). If no such subscription exists, the function returns -1, otherwise it processes the request (generates internal notification, ...) and returns 1.
kamailio-4.0.4/obsolete/presence_b2b/doc/Makefile0000644000000000000000000000013512223032460020346 0ustar rootrootdocs = presence_b2b.xml docbook_dir=../../../docbook include $(docbook_dir)/Makefile.module kamailio-4.0.4/obsolete/presence_b2b/doc/params.xml0000644000000000000000000000663412223032460020725 0ustar rootroot %docentities; ]>
Parameters default_expiration (integer) Default subscription expiration timeout in seconds. Default value is 3600. on_error_retry_time (integer) Time in seconds to next attempt at subscription creation after receiving an error from peer (for example 404 response on SUBSCRIBE request). Default value is 120. presence_route (string) Route header put into first generated SUBSCRIBE request. Default value: empty. additional_presence_headers (string) Additional headers put into sent SUBSCRIBE requests. Default value: empty. wait_for_term_notify (integer) Default timeout for receiving terminating NOTIFY after unsubscribe (SUBSCRIBE with Expires: 0). If no NOTIFY is received during this time, the subscription is destroyed without processing last notify. Default value is 30. resubscribe_delta (integer) Number of seconds before subscription expiration when should be sent resubscription request. For example if this value is 60 and the subscription is for 3600 seconds (Expires in 2xx response), next SUBSCRIBE request will be sent after 3540 seconds. Default value is 30. min_resubscribe_time (integer) Minimum number of seconds between consequent SUBSCRIBE requests. (Renewal SUBSCRIBE is sent at least after this number of seconds.) Default value is 30. handle_presence_subscriptions (integer) If set to nonzero value the module will handle internal (QSA) subscriptions to presence events. If not set, these internal subscriptions are ignored. Default value is 1. presence_outbound_proxy URI where to send all presence SUBSCRIBEs. It is better to use this than presence_route. Empty by default. max_subscribe_delay If set to value greater than zero SUBSCRIBE requests are sent randomly at most after max_subscribe_delay seconds. SUBSCRIBE requests are sent immediately when processing internal (QSA) subscription when set to 0. Default value is 0.
kamailio-4.0.4/obsolete/presence_b2b/euac_state_machine.c0000644000000000000000000003627312223032460022122 0ustar rootroot#include "euac_state_machine.h" #include "euac_internals.h" #include "euac_funcs.h" #include /* if set to number > 0 SUBSCRIBE requests are sent randomly, at last * after max_subscribe_delay seconds */ int max_subscribe_delay = 0; /* waiting time after error (before new attempt about subscription) */ int resubscribe_timeout_on_err = 120; /* time specifying how long wait for terminating NOTIFY * after 2xx response on SUBSCRIBE with 0 expires*/ int waiting_for_notify_time = 30; int subscribe_time = 3600; /* time interval before expiration when should be the subscription refreshed * (recommended are some seconds before) */ int resubscribe_delta = 30; /* minimum time for resubscriptions */ int min_resubscribe_time = 30; /* timeout for failover */ int failover_timeout = 60; /* helper functions for internal usage */ static void accept_response(events_uac_t *uac, euac_action_t action) { if (action != act_1xx) { /* remove reference reserved for cb (final responses * only or timer tick instead of 408) */ remove_euac_reference_nolock(uac); } } static void decline_response(events_uac_t *uac, euac_action_t action) { ERR("[%s]: out of order response action = %d) (BUG?)\n", uac->id, action); if (action != act_1xx) { remove_euac_reference_nolock(uac); } } static int get_resubscribe_time(struct sip_msg *m) { int expiration; if (get_expiration_value(m, &expiration) != 0) expiration = 0; expiration -= resubscribe_delta; if (expiration < min_resubscribe_time) expiration = min_resubscribe_time; return expiration; } static void send_error_notification(events_uac_t *uac) { /* sends innternal notification about arror status */ } static void confirm_dialog(events_uac_t *uac, struct sip_msg *m) { /* remove dialog from, unconfirmed */ ht_remove(&euac_internals->ht_unconfirmed, &uac->dialog->id); /* process confirmation response */ euac_internals->tmb.dlg_response_uac(uac->dialog, m, IS_TARGET_REFRESH); /* add to confirmed dialogs */ DBG("adding into confirmed EUACs\n"); ht_add(&euac_internals->ht_confirmed, &uac->dialog->id, uac); /* TRACE("confirmed dialog: %.*s * %.*s * %.*s\n", FMT_STR(uac->dialog->id.loc_tag), FMT_STR(uac->dialog->id.rem_tag), FMT_STR(uac->dialog->id.call_id)); */ } static void destroy_unconfirmed_dialog(events_uac_t *uac) { /* remove dialog from, unconfirmed */ ht_remove(&euac_internals->ht_unconfirmed, &uac->dialog->id); if (uac->dialog) { euac_internals->tmb.free_dlg(uac->dialog); uac->dialog = NULL; } } static void destroy_confirmed_dialog(events_uac_t *uac) { /* remove dialog from, confirmed */ ht_remove(&euac_internals->ht_confirmed, &uac->dialog->id); if (uac->dialog) { euac_internals->tmb.free_dlg(uac->dialog); uac->dialog = NULL; } } /* -------------------------------------------------------------------- */ /* changing state machine status functions */ void do_step_unconfirmed(euac_action_t action, struct sip_msg *m, events_uac_t *uac) { str contact = STR_NULL; int expires = 0; switch (action) { case act_notify: refresh_dialog(uac, m); /* ? */ do_notification(uac, m); break; case act_1xx: accept_response(uac, action); break; case act_2xx: uac->status = euac_confirmed; euac_clear_timer(uac); confirm_dialog(uac, m); expires = get_resubscribe_time(m); /* DBG("expires after %d seconds\n", expires); */ euac_set_timer(uac, expires); accept_response(uac, action); break; case act_3xx: accept_response(uac, action); euac_clear_timer(uac); destroy_unconfirmed_dialog(uac); extract_contact(m, &contact); if (!is_str_empty(&contact)) { if (new_subscription(uac, &contact, failover_timeout) != 0) { /* error */ uac->status = euac_waiting; send_error_notification(uac); euac_set_timer(uac, resubscribe_timeout_on_err); } str_free_content(&contact); } else { /* redirect, but no contact given => process like error */ uac->status = euac_waiting; send_error_notification(uac); euac_set_timer(uac, resubscribe_timeout_on_err); } break; case act_tick: case act_4xx: /* 4xx, 5xx, ... */ uac->status = euac_waiting; euac_clear_timer(uac); destroy_unconfirmed_dialog(uac); send_error_notification(uac); euac_set_timer(uac, resubscribe_timeout_on_err); accept_response(uac, action); break; case act_destroy: uac->status = euac_unconfirmed_destroy; break; } } void do_step_unconfirmed_destroy(euac_action_t action, struct sip_msg *m, events_uac_t *uac) { int expires = 0; switch (action) { case act_1xx: accept_response(uac, action); break; case act_destroy: break; case act_notify: refresh_dialog(uac, m); discard_notification(uac, m, 200, "OK"); break; case act_2xx: accept_response(uac, action); euac_clear_timer(uac); confirm_dialog(uac, m); expires = get_resubscribe_time(m); /* if (expires == 0) wait_for_terminating_notify(uac); else */ uac->status = euac_predestroyed; if (renew_subscription(uac, 0, failover_timeout) != 0) { /* error */ uac->status = euac_destroyed; destroy_confirmed_dialog(uac); remove_euac_reference_nolock(uac); /* free EUAC */ } break; case act_3xx: case act_4xx: case act_tick: /* accept response too! */ uac->status = euac_destroyed; accept_response(uac, action); euac_clear_timer(uac); destroy_unconfirmed_dialog(uac); remove_euac_reference_nolock(uac); /* free EUAC */ break; } } void do_step_resubscription_destroy(euac_action_t action, struct sip_msg *m, events_uac_t *uac) { switch (action) { case act_1xx: accept_response(uac, action); break; case act_destroy: break; case act_notify: refresh_dialog(uac, m); discard_notification(uac, m, 200, "OK"); break; case act_2xx: accept_response(uac, action); euac_clear_timer(uac); /* expires = get_expiration_value(m); if (expires == 0) wait_for_terminating_notify(uac); else */ uac->status = euac_predestroyed; if (renew_subscription(uac, 0, failover_timeout) != 0) { /* error */ uac->status = euac_destroyed; destroy_confirmed_dialog(uac); remove_euac_reference_nolock(uac); /* free EUAC */ } break; case act_3xx: case act_4xx: case act_tick: uac->status = euac_destroyed; accept_response(uac, action); euac_clear_timer(uac); destroy_confirmed_dialog(uac); remove_euac_reference_nolock(uac); /* free EUAC */ break; } } void do_step_confirmed(euac_action_t action, struct sip_msg *m, events_uac_t *uac) { /* int expires = 0; process the value present in Subscription-State header? */ switch (action) { case act_1xx: case act_2xx: case act_3xx: case act_4xx: decline_response(uac, action); ERR("[%s]: invalid action %d (BUG?)\n", uac->id, action); break; case act_destroy: uac->status = euac_predestroyed; euac_clear_timer(uac); if (renew_subscription(uac, 0, failover_timeout) != 0) { /* error */ uac->status = euac_destroyed; destroy_confirmed_dialog(uac); remove_euac_reference_nolock(uac); /* free EUAC */ } break; case act_notify: refresh_dialog(uac, m); do_notification(uac, m); break; case act_tick: uac->status = euac_resubscription; if (renew_subscription(uac, subscribe_time, failover_timeout) != 0) { /* error */ uac->status = euac_waiting; euac_clear_timer(uac); destroy_confirmed_dialog(uac); send_error_notification(uac); euac_set_timer(uac, resubscribe_timeout_on_err); break; } break; } } void do_step_resubscription(euac_action_t action, struct sip_msg *m, events_uac_t *uac) { str contact = STR_NULL; int expires = 0; switch (action) { case act_notify: refresh_dialog(uac, m); do_notification(uac, m); break; case act_1xx: accept_response(uac, action); break; case act_2xx: uac->status = euac_confirmed; accept_response(uac, action); euac_clear_timer(uac); refresh_dialog_resp(uac, m); expires = get_resubscribe_time(m); euac_set_timer(uac, expires); break; case act_3xx: accept_response(uac, action); euac_clear_timer(uac); destroy_confirmed_dialog(uac); extract_contact(m, &contact); if (!is_str_empty(&contact)) { uac->status = euac_unconfirmed; if (new_subscription(uac, &contact, failover_timeout) != 0) { /* error */ uac->status = euac_waiting; send_error_notification(uac); euac_set_timer(uac, resubscribe_timeout_on_err); } str_free_content(&contact); } else { /* redirect, but no contact given => process like error */ uac->status = euac_waiting; send_error_notification(uac); euac_set_timer(uac, resubscribe_timeout_on_err); } break; case act_tick: case act_4xx: /* 4xx, 5xx, ... */ uac->status = euac_waiting; accept_response(uac, action); euac_clear_timer(uac); destroy_confirmed_dialog(uac); send_error_notification(uac); euac_set_timer(uac, resubscribe_timeout_on_err); break; case act_destroy: uac->status = euac_resubscription_destroy; break; } } void do_step_resubscribe_destroy(euac_action_t action, struct sip_msg *m, events_uac_t *uac) { switch (action) { case act_1xx: accept_response(uac, action); break; case act_destroy: break; case act_notify: refresh_dialog(uac, m); discard_notification(uac, m, 200, "OK"); break; case act_2xx: uac->status = euac_predestroyed; accept_response(uac, action); euac_clear_timer(uac); refresh_dialog_resp(uac, m); if (renew_subscription(uac, 0, failover_timeout) != 0) { uac->status = euac_destroyed; destroy_confirmed_dialog(uac); remove_euac_reference_nolock(uac); /* free EUAC */ } break; case act_tick: case act_3xx: case act_4xx: uac->status = euac_destroyed; accept_response(uac, action); euac_clear_timer(uac); destroy_confirmed_dialog(uac); remove_euac_reference_nolock(uac); /* free EUAC */ break; } } void do_step_destroyed(euac_action_t action, struct sip_msg *m, events_uac_t *uac) { switch (action) { case act_destroy: break; case act_notify: WARN("[%s]: received NOTIFY for destroyed dialog !\n", uac->id); discard_notification(uac, m, 481, "Subscription does not exist"); break; /* response can be received because the step predestroyed -> destroyed could * be done after receiving terminating NOTIFY (before response) */ case act_2xx: case act_3xx: case act_4xx: accept_response(uac, action); break; default: ERR("[%s]: action not allowed (%d) (BUG?)\n", uac->id, action); break; } } void do_step_predestroyed(euac_action_t action, struct sip_msg *m, events_uac_t *uac) { switch (action) { case act_notify: refresh_dialog(uac, m); discard_notification(uac, m, 200, "OK"); if (is_terminating_notify(m)) { destroy_confirmed_dialog(uac); euac_clear_timer(uac); uac->status = euac_destroyed; /* DBG("destroying dialog (NOTIFY)\n"); */ remove_euac_reference_nolock(uac); /* free EUAC */ } break; case act_1xx: accept_response(uac, action); break; case act_2xx: uac->status = euac_waiting_for_termination; euac_clear_timer(uac); euac_set_timer(uac, waiting_for_notify_time); accept_response(uac, action); break; case act_tick: case act_3xx: case act_4xx: uac->status = euac_destroyed; euac_clear_timer(uac); destroy_confirmed_dialog(uac); accept_response(uac, action); remove_euac_reference_nolock(uac); /* free EUAC */ break; case act_destroy: break; } } void do_step_waiting_for_term_notify(euac_action_t action, struct sip_msg *m, events_uac_t *uac) { switch (action) { case act_notify: discard_notification(uac, m, 200, "OK"); if (is_terminating_notify(m)) { destroy_confirmed_dialog(uac); euac_clear_timer(uac); uac->status = euac_destroyed; /* DBG("destroying dialog (NOTIFY)\n"); */ remove_euac_reference_nolock(uac); /* free EUAC */ } else { DBG("discarding NOTIFY (not terminating)\n"); } break; case act_tick: /* wait no more */ if (!uac->dialog) WARN("[%s]: destroying dialog with timer (no term NOTIFY)!\n", uac->id); else WARN("[%s]: destroying dialog with timer (no term NOTIFY; %.*s, %.*s, %.*s)!\n", uac->id, FMT_STR(uac->dialog->id.loc_tag), FMT_STR(uac->dialog->id.rem_tag), FMT_STR(uac->dialog->id.call_id)); uac->status = euac_destroyed; destroy_confirmed_dialog(uac); remove_euac_reference_nolock(uac); /* free EUAC */ break; case act_1xx: case act_2xx: case act_3xx: case act_4xx: decline_response(uac, action); ERR("[%s]: action not allowed (%d) (BUG?)\n", uac->id, action); break; case act_destroy: break; } } void do_step_waiting(euac_action_t action, struct sip_msg *m, events_uac_t *uac) { switch (action) { case act_1xx: case act_2xx: case act_3xx: case act_4xx: decline_response(uac, action); ERR("[%s]: action not allowed (%d) (BUG?)\n", uac->id, action); break; case act_notify: ERR("[%s]: action not allowed (%d) (BUG?)- " "discarding NOTIFY for non established subscription\n", uac->id, action); discard_notification(uac, m, 500, "Internal error"); break; case act_destroy: uac->status = euac_destroyed; euac_clear_timer(uac); remove_euac_reference_nolock(uac); /* free EUAC */ break; case act_tick: uac->status = euac_unconfirmed; if (new_subscription(uac, NULL, failover_timeout) != 0) { /* error */ uac->status = euac_waiting; euac_set_timer(uac, resubscribe_timeout_on_err); } break; } } /* this function can remove the uac from memory as a side effect * thus it should be the last action done on UAC (or you have to * add reference before this call)!!! */ void euac_do_step(euac_action_t action, struct sip_msg *m, events_uac_t *uac) { /* TRACE("STEP [%s]: %d ---(%d)---> ...\n", uac->id, uac->status, action);*/ switch (uac->status) { case euac_unconfirmed: do_step_unconfirmed(action, m, uac); break; case euac_unconfirmed_destroy: do_step_unconfirmed_destroy(action, m, uac); break; case euac_confirmed: do_step_confirmed(action, m, uac); break; case euac_waiting: do_step_waiting(action, m, uac); break; case euac_resubscription: do_step_resubscription(action, m, uac); break; case euac_resubscription_destroy: do_step_resubscription_destroy(action, m, uac); break; case euac_waiting_for_termination: do_step_waiting_for_term_notify(action, m, uac); break; case euac_predestroyed: do_step_predestroyed(action, m, uac); break; case euac_destroyed: do_step_destroyed(action, m, uac); break; } } void euac_start(events_uac_t *uac) { int subscribe_delay = 0; if (max_subscribe_delay > 0) { uac->status = euac_waiting; /* subscribe_delay = (double)rand() / (double)RAND_MAX * max_subscribe_delay; */ subscribe_delay = rand() % (max_subscribe_delay) + 1; /* dissallow timer with 0 expiration */ euac_set_timer(uac, subscribe_delay); } else { uac->status = euac_unconfirmed; if (new_subscription(uac, NULL, failover_timeout) != 0) { uac->status = euac_waiting; euac_set_timer(uac, resubscribe_timeout_on_err); } } } kamailio-4.0.4/obsolete/presence_b2b/euac_internals.h0000644000000000000000000000165212223032460021313 0ustar rootroot#ifndef __EUAC_INTERNALS_H #define __EUAC_INTERNALS_H /* internal structures */ #include "events_uac.h" #include #include #include "../../modules/tm/tm_load.h" #include "trace.h" typedef struct { events_uac_t *first_uac; events_uac_t *last_uac; cds_mutex_t mutex; /* two hash tables for established-dialogs(from, to, callid) * and for non-established dialogs (from, callid) * as key is used dlg_id_t* !!! */ hash_table_t ht_confirmed; /* hashed according dialog ids */ hash_table_t ht_unconfirmed; /* hashed according partial dialog ids */ struct tm_binds tmb; dlg_func_t dlgb; /* members for trace */ int create_cnt; int destroy_cnt; reference_counter_group_t *rc_grp; } events_uacs_internals_t; extern events_uacs_internals_t *euac_internals; int init_events_uac_internals(); void destroy_events_uac_internals(); void lock_events_uac(); void unlock_events_uac(); #endif kamailio-4.0.4/obsolete/presence_b2b/euac_funcs.c0000644000000000000000000002642612223032460020433 0ustar rootroot#include "euac_funcs.h" #include "euac_internals.h" #include "euac_state_machine.h" #include "../../parser/parse_expires.h" #include "../../modules/tm/ut.h" #include "../../parser/contact/parse_contact.h" #include "../../parser/parse_from.h" #include #include void extract_contact(struct sip_msg *m, str *dst) { contact_t *sip_contact = NULL; str_clear(dst); contact_iterator(&sip_contact, m, NULL); if (sip_contact) str_dup(dst, &sip_contact->uri); /* FIXME: and what the other contacts? */ } events_uac_t *find_euac_nolock(struct sip_msg *m) { dlg_id_t id; events_uac_t *uac; int tmp; if (parse_headers(m, HDR_FROM_F | HDR_TO_F | HDR_CALLID_F, 0) < 0) { ERR("can't parse headers\n"); return NULL; } parse_from_header(m); memset(&id, 0, sizeof(id)); if (m->to && m->to->parsed) id.loc_tag = ((struct to_body*)m->to->parsed)->tag_value; if (parse_from_header(m)==0 && m->from->parsed) id.rem_tag = ((struct to_body*)m->from->parsed)->tag_value; if (m->callid) id.call_id = m->callid->body; uac = (events_uac_t*)ht_find(&euac_internals->ht_confirmed, &id); if (!uac) { /* INFO("confirmed dialog not found for arriving NOTIFY: " "%.*s * %.*s * %.*s\n", FMT_STR(id.loc_tag), FMT_STR(id.rem_tag), FMT_STR(id.call_id));*/ tmp = id.rem_tag.len; id.rem_tag.len = 0; uac = (events_uac_t*)ht_find(&euac_internals->ht_unconfirmed, &id); if (!uac) { id.rem_tag.len = tmp; /* for printing whole dlg id */ WARN("events UAC not found for arriving NOTIFY: " "%.*s, %.*s, %.*s\n", FMT_STR(id.loc_tag), FMT_STR(id.rem_tag), FMT_STR(id.call_id)); } /* else INFO("received NOTIFY for unconfirmed dialog!\n");*/ } return uac; } int remove_euac_reference_nolock(events_uac_t *uac) { /* must be called from locked section !! */ /* TRACE("[%s]: removing reference (%d)\n", uac->id, uac->ref_cntr.cntr - 1);*/ if (remove_reference(&uac->ref_cntr)) { /* all other references are freed - we can remove uac from the list */ if (uac->status == euac_destroyed) { /* TRACE("freeing uac %p\n", uac); */ } else { ERR("BUG: freeing uac %p in incorrect status (%d)\n", uac, uac->status); } remove_uac_from_list(uac); free_events_uac(uac); return 1; } return 0; } /* void rls_notify_cb(struct cell* t, struct sip_msg* msg, int code, void *param) */ static void subscribe_cb(struct cell* t, int type, struct tmcb_params* params) { events_uac_t *uac = NULL; euac_action_t action; if (!params) return; /* FIXME: Problems * 1. sometimes no response arrives (neither generated 408) * 2. sometimes are more responses going here !!! * => try to find uac, if exists then process the response * otherwise ignore it */ if (params->param) uac = (events_uac_t *)*(params->param); if (!uac) { ERR("something wrong - empty uac parameter given to callback function\n"); return; } /* TRACE("%d response on SUBSCRIBE %p [%s]\n", params->code, uac, uac->id); */ action = act_4xx; if ((params->code >= 100) && (params->code < 200)) action = act_1xx; if ((params->code >= 200) && (params->code < 300)) action = act_2xx; if ((params->code >= 300) && (params->code < 400)) action = act_3xx; lock_events_uac(); euac_do_step(action, params->rpl, uac); /* in euac_do_step MUST be called accept_response or decline_response */ unlock_events_uac(); } const char *proto2uri_param(int proto) { static char *udp = ""; /* static char *udp = ";transport=udp"; */ static char *tcp = ";transport=tcp"; static char *tls = ";transport=tls"; static char *sctp = ";transport=sctp"; switch (proto) { case PROTO_NONE: case PROTO_UDP: return udp; case PROTO_TCP: return tcp; case PROTO_TLS: return tls; case PROTO_SCTP: return sctp; } return udp; } /* returns length of added string */ static int get_contact_hdr(char *dst, int max_size, dlg_t *dialog) { struct dest_info dst_info; int port = 5060; const char *proto; int len; #ifdef USE_DNS_FAILOVER if (!uri2dst(NULL, &dst_info, NULL /* msg */, dialog->hooks.next_hop, PROTO_NONE)) { return 0; /* error */ } #else if (!uri2dst(&dst_info, 0 /* msg */, dialog->hooks.next_hop, PROTO_NONE)) { return 0; /* error */ } #endif if (!dst_info.send_sock) { /* error */ return 0; } /* send_sock = get_send_socket(NULL, to, proto); */ proto = proto2uri_param(dst_info.send_sock->proto); if (dst_info.send_sock->port_no) port = dst_info.send_sock->port_no; len = snprintf(dst, max_size, "Contact: \r\n", FMT_STR(dst_info.send_sock->address_str), port, proto); /* DBG("%.*s (len = %d)\n", len, dst, len); */ return len; } static int prepare_hdrs(events_uac_t *uac, str *hdr, str *contact_to_send) { char tmp[256]; str tmps; int res, contact_len; str warning = STR_STATIC_INIT("P-Hint: trying new subscription after 3xx\r\n"); str_clear(hdr); res = 0; tmps.len = sprintf(tmp, "Expires: %d\r\n", subscribe_time); tmps.s = tmp; contact_len = get_contact_hdr(tmps.s + tmps.len, sizeof(tmp) - tmps.len, uac->dialog); if (contact_len <= 0) { ERR("BUG: can't send SUBSCRIBE without contact\n"); res = -1; } if (res == 0) { tmps.len += contact_len; res = str_concat(hdr, &uac->headers, &tmps); } if (!is_str_empty(contact_to_send)) { /* FIXME - only testing */ str s = *hdr; if (res == 0) res = str_concat(hdr, &s, &warning); } if (res != 0) { str_free_content(hdr); } return res; } int new_subscription(events_uac_t *uac, str *contact_to_send, int failover_time) { static str method = STR_STATIC_INIT("SUBSCRIBE"); unsigned int cseq = 1; str hdr = STR_NULL; str body = STR_STATIC_INIT(""); str *uri; DBG("sending new SUBSCRIBE request\n"); if (!is_str_empty(contact_to_send)) uri = contact_to_send; else uri = &uac->remote_uri; /* create new dialog */ if (euac_internals->tmb.new_dlg_uac(NULL /* will be generated */, NULL /* will be generated */, cseq, &uac->local_uri, uri, &uac->dialog) < 0) { ERR("can't create dialog for URI \'%.*s\'\n", FMT_STR(uac->remote_uri)); goto ns_err_nodlg; } /* preset route for created dialog */ if (!is_str_empty(&uac->route)) if (euac_internals->dlgb.preset_dialog_route(uac->dialog, &uac->route) < 0) goto ns_err_dlg; /* preset outbound proxy */ if (!is_str_empty(&uac->outbound_proxy)) uac->dialog->hooks.next_hop = &uac->outbound_proxy; if (prepare_hdrs(uac, &hdr, contact_to_send) < 0) goto ns_err_dlg; add_reference(&uac->ref_cntr); /* add reference for callback function */ /*TRACE("[%s]: added reference (%d)\n", uac->id, uac->ref_cntr.cntr);*/ /* add to hash table (hash acording to dialog id) */ DBG("adding into unconfirmed EUACs\n"); if (ht_add(&euac_internals->ht_unconfirmed, &uac->dialog->id, uac) != 0) goto ns_err_ref; /* TRACE("new subscription [%s] dlg id = %.*s, %.*s, %.*s\n", uac->id, FMT_STR(uac->dialog->id.call_id), FMT_STR(uac->dialog->id.rem_tag), FMT_STR(uac->dialog->id.loc_tag)); */ /* generate subscribe request */ if (euac_internals->dlgb.request_outside(&method, &hdr, &body, uac->dialog, subscribe_cb, uac) < 0) goto ns_err_in_ht; str_free_content(&hdr); if (failover_time > 0) euac_set_timer(uac, failover_time); return 0; ns_err_in_ht: ht_remove(&euac_internals->ht_unconfirmed, &uac->dialog->id); ns_err_ref: /* TRACE("[%s]: removing reference (%d)\n", uac->id, uac->ref_cntr.cntr - 1);*/ remove_reference(&uac->ref_cntr); ns_err_dlg: if (uac->dialog) euac_internals->tmb.free_dlg(uac->dialog); ns_err_nodlg: uac->dialog = NULL; /* euac_do_step(act_4xx, NULL, uac); */ str_free_content(&hdr); return -1; } int renew_subscription(events_uac_t *uac, int expires, int failover_time) { static str method = STR_STATIC_INIT("SUBSCRIBE"); int res, contact_len; str hdr; char tmp[256]; str tmps; str body = STR_STATIC_INIT(""); DBG("sending renewal SUBSCRIBE request\n"); tmps.len = sprintf(tmp, "Expires: %d\r\n", expires); tmps.s = tmp; contact_len = get_contact_hdr(tmps.s + tmps.len, sizeof(tmp) - tmps.len, uac->dialog); if (contact_len <= 0) { ERR("BUG: can't send SUBSCRIBE without contact\n"); /* euac_do_step(act_4xx, NULL, uac); */ return -1; } tmps.len += contact_len; if (str_concat(&hdr, &uac->headers, &tmps) < 0) { ERR("can't build headers\n"); /* euac_do_step(act_4xx, NULL, uac); */ return -1; } /* TRACE("sending resubscribe with hdrs: %.*s\n", FMT_STR(hdr)); */ /* generate subscribe request */ add_reference(&uac->ref_cntr); /* add reference for callback function */ /* TRACE("[%s]: added reference (%d)\n", uac->id, uac->ref_cntr.cntr);*/ /* generate subscribe request - don't call the TM version * (frees callback params on error!!!) */ res = euac_internals->dlgb.request_inside(&method, &hdr, &body, uac->dialog, subscribe_cb, uac); str_free_content(&hdr); if (res < 0) { /* euac_do_step(act_4xx, NULL, uac); */ /* TRACE("[%s]: removing reference (%d)\n", uac->id, uac->ref_cntr.cntr - 1);*/ remove_reference(&uac->ref_cntr); /* remove reference for cb function */ return res; } else { if (failover_time > 0) euac_set_timer(uac, failover_time); return 0; } } void do_notification(events_uac_t *uac, struct sip_msg *m) { DBG("received notification\n"); if (m) { if (euac_internals->tmb.t_reply(m, 200, "OK") == -1) { ERR("Error while sending response!\n"); } } if (uac) { if (uac->cb) uac->cb(uac, m, uac->cbp); } } void discard_notification(events_uac_t *uac, struct sip_msg *m, int res_code, char *msg) { /* might be called on destroyed dialog !*/ DBG("received notification (discard)\n"); if (!m) return; if (euac_internals->tmb.t_reply(m, res_code, msg) == -1) { ERR("Error while sending response: %d %s\n", res_code, msg); } } void refresh_dialog(events_uac_t *uac, struct sip_msg *m) { /* only NOTIFYs are here? */ if (uac->dialog) euac_internals->tmb.dlg_request_uas(uac->dialog, m, IS_TARGET_REFRESH); } void refresh_dialog_resp(events_uac_t *uac, struct sip_msg *m) { /* only responses to SUBSCRIBE are here? */ if (uac->dialog) euac_internals->tmb.dlg_response_uac(uac->dialog, m, IS_TARGET_REFRESH); } /* ----- Timer functions ----- */ static ticks_t timer_cb(ticks_t ticks, struct timer_ln* tl, void* data) { events_uac_t *uac = (events_uac_t*)data; if (!uac) { ERR("BUG: null parameter\n"); return 0; } /* TRACE("timer called at %d ticks with %p\n", ticks, data); */ uac->timer_started = 0; /* hack */ lock_events_uac(); /* uac->timer_started = 0; */ euac_do_step(act_tick, NULL, uac); remove_euac_reference_nolock(uac); unlock_events_uac(); return 0; /* one shot timer */ } void euac_set_timer(events_uac_t *uac, int seconds) { /* set timer to generate act_tick action after "seconds" seconds */ if (uac->timer_started) euac_clear_timer(uac); add_reference(&uac->ref_cntr); /* add reference for timer callback function */ /* TRACE("[%s]: added reference (%d)\n", uac->id, uac->ref_cntr.cntr);*/ timer_init(&uac->timer, timer_cb, uac, 0); if (timer_add(&uac->timer, S_TO_TICKS(seconds)) != 0) { ERR("can't set timer for [%s]!\n", uac->id); } uac->timer_started = 1; /* TRACE("timer added for %d secs\n", seconds); */ } void euac_clear_timer(events_uac_t *uac) { /* unset timer */ /* timer->data = NULL; */ /* TRACE("clearing timer\n"); */ if (uac->timer_started) { uac->timer_started = 0; timer_del(&uac->timer); remove_euac_reference_nolock(uac); } } kamailio-4.0.4/obsolete/presence_b2b/README0000644000000000000000000001344712223032460017033 0ustar rootroot1. Presence B2B UA Vaclav Kubart Iptel/Tekelec __________________________________________________________________ 1.1. Dependencies 1.2. Usage 1.3. Parameters 1.4. Functions This module acts as back to back user agent for presence events. In the future it will do subscriptions to reg events and presence with resource lists too. It is accessible only using the internal Query Status API (QSA). Everywhere (in the C code) where you need subscriptions to presentity status, you only create an internal subscription to it and the rest is done by this module. It processes the internal subscription and creates a SIP subscription (SUBSCRIBE-NOTIFY dialog) to requested presentity. Every NOTIFY request produces new QSA message with status information into destination message queue. Instead of this module can be used PA module with parameter accept_internal_subscriptions set to 1. In that case the subscription will be only to internal status hold by PA. For every requested (record, subscriber) a new SIP subscription is created. This is due to authorization of such subscriptions - B2B UA can't decide about authorization rules, thus it has to leave it on the presence server to which it creates the subscription and this means, that it has to create the subscription as a subscriber. There is a possibility to do subscriptions with the same To + From only once, but this is left for future optimalizations (it will probably not help a lot). 1.1. Dependencies Modules * tm * dialog Libraries * libcds (internal) * libpresence (internal) 1.2. Usage This module can be used by RLS module (e.g. RLS module does internal subscriptions to "presence" which can be handled by this module or PA module). In near future it will be used by PA module for subscribing to users; this could be useful with UACs capable to process SUBSCRIBE/NOTIFY but not using PUBLISH for presence state publication (like Windows Messenger). 1.3. Parameters default_expiration (integer) Default subscription expiration timeout in seconds. Default value is 3600. on_error_retry_time (integer) Time in seconds to next attempt at subscription creation after receiving an error from peer (for example 404 response on SUBSCRIBE request). Default value is 120. presence_route (string) Route header put into first generated SUBSCRIBE request. Default value: empty. additional_presence_headers (string) Additional headers put into sent SUBSCRIBE requests. Default value: empty. wait_for_term_notify (integer) Default timeout for receiving terminating NOTIFY after unsubscribe (SUBSCRIBE with Expires: 0). If no NOTIFY is received during this time, the subscription is destroyed without processing last notify. Default value is 30. resubscribe_delta (integer) Number of seconds before subscription expiration when should be sent resubscription request. For example if this value is 60 and the subscription is for 3600 seconds (Expires in 2xx response), next SUBSCRIBE request will be sent after 3540 seconds. Default value is 30. min_resubscribe_time (integer) Minimum number of seconds between consequent SUBSCRIBE requests. (Renewal SUBSCRIBE is sent at least after this number of seconds.) Default value is 30. handle_presence_subscriptions (integer) If set to nonzero value the module will handle internal (QSA) subscriptions to presence events. If not set, these internal subscriptions are ignored. Default value is 1. presence_outbound_proxy URI where to send all presence SUBSCRIBEs. It is better to use this than presence_route. Empty by default. max_subscribe_delay If set to value greater than zero SUBSCRIBE requests are sent randomly at most after max_subscribe_delay seconds. SUBSCRIBE requests are sent immediately when processing internal (QSA) subscription when set to 0. Default value is 0. 1.4. Functions handle_notify() Handles NOTIFY request. The module tries to find the subscription to which the NOTIFY belongs to (it tries both - confirmed and unconfirmed subscriptions because NOTIFY may arrive before 2xx response on SUBSCRIBE request). If no such subscription exists, the function returns -1, otherwise it processes the request (generates internal notification, ...) and returns 1. Example 1. Configuration example ... loadmodule "/home/kubartv/SER/lib/ser/modules/tm.so" loadmodule "/home/kubartv/SER/lib/ser/modules/dialog.so" loadmodule "/home/kubartv/SER/lib/ser/modules/presence_b2b.so" ... modparam("presence_b2b", "presence_route", "") modparam("presence_b2b", "on_error_retry_time", 600) modparam("presence_b2b", "wait_for_term_notify", 33) modparam("presence_b2b", "resubscribe_delta", 15) modparam("presence_b2b", "min_resubscribe_time", 55) modparam("presence_b2b", "default_expiration", 667) ... route { ... if (uri==myself) { ... if (method=="NOTIFY") { if (!t_newtran()) { log(1, "newtran error\n"); sl_reply_error(); }; if (!handle_notify()) { t_reply("481", "Unable to handle notification"); } break; }; ... } ... } kamailio-4.0.4/obsolete/presence_b2b/rpc.h0000644000000000000000000000017112223032460017076 0ustar rootroot#ifndef __EVENTS_RPC_H #define __EVENTS_RPC_H #include "../../rpc.h" extern rpc_export_t events_rpc_methods[]; #endif kamailio-4.0.4/obsolete/presence_b2b/events_uac.c0000644000000000000000000001052212223032460020442 0ustar rootroot#include "events_uac.h" #include "euac_internals.h" /* for debugging purposes */ #include "../../dprint.h" #include #include #include #include #include "euac_funcs.h" #include "euac_internals.h" #include "euac_state_machine.h" void remove_uac_from_list(events_uac_t *uac) { if (uac->next) uac->next->prev = uac->prev; else euac_internals->last_uac = uac->prev; if (uac->prev) uac->prev->next = uac->next; else euac_internals->first_uac = uac->next; } void insert_uac_to_list(events_uac_t *uac) { if (euac_internals->last_uac) euac_internals->last_uac->next = uac; else euac_internals->first_uac = uac; uac->prev = euac_internals->last_uac; uac->next = NULL; euac_internals->last_uac = uac; } void free_events_uac(events_uac_t *uac) { /* TRACE("freeing EUAC %p\n", uac); */ str_free_content(&uac->headers); str_free_content(&uac->local_uri); str_free_content(&uac->remote_uri); str_free_content(&uac->route); str_free_content(&uac->outbound_proxy); /* if the dialog is not freed we should free it */ if (uac->dialog) { euac_internals->tmb.free_dlg(uac->dialog); /* TRACE("freeing dialog for EUAC %p\n", uac); */ } mem_free(uac); } events_uac_t *create_events_uac(str *remote_uri, str *local_uri, const str *events, notify_callback_func cb, /* callback function for processing NOTIFY messages (parsing, ...) */ void *cbp, /* parameter for callback function */ const str *other_headers, str *route, str *outbound_proxy) { events_uac_t *uac; dstring_t dstr; int res = 0; if ((!remote_uri) || (!local_uri)) { ERR("invalid parameters\n"); return NULL; } uac = (events_uac_t*)mem_alloc(sizeof(*uac)); if (!uac) return NULL; /* compose headers */ dstr_init(&dstr, 256); dstr_append_zt(&dstr, "Event: "); dstr_append_str(&dstr, events); dstr_append_zt(&dstr, "\r\n"); /* required by RFC 3261 */ dstr_append_zt(&dstr, "Max-Forwards: 70\r\n"); /* needed for SUBSCRIBE via TCP - if not given the message is not parsed by ser!!! */ dstr_append_zt(&dstr, "Content-Length: 0\r\n"); if (other_headers) dstr_append_str(&dstr, other_headers); /* should get Accpet headers as parameter too? */ if (dstr_get_str(&dstr, &uac->headers) != 0) { ERR("can't generate headers (no mem)\n"); dstr_destroy(&dstr); mem_free(uac); return NULL; } dstr_destroy(&dstr); uac->dialog = NULL; init_reference_counter(euac_internals->rc_grp, &uac->ref_cntr); /* main reference - removed in "destroyed" status */ add_reference(&uac->ref_cntr); /* add reference for client */ /*TRACE("[%s]: added reference (%d)\n", "???", uac->ref_cntr.cntr);*/ uac->status = euac_unconfirmed; res = str_dup(&uac->local_uri, local_uri); if (res == 0) res = str_dup(&uac->remote_uri, remote_uri); else str_clear(&uac->remote_uri); if (res == 0) res = str_dup(&uac->route, route); else str_clear(&uac->route); if (res == 0) res = str_dup(&uac->outbound_proxy, outbound_proxy); else str_clear(&uac->outbound_proxy); uac->timer_started = 0; uac->cb = cb; uac->cbp = cbp; if (res != 0) { ERR("can't duplicate parameters\n"); free_events_uac(uac); return NULL; } lock_events_uac(); sprintf(uac->id, "%p:%x:%x", uac, (unsigned int)time(NULL), rand()); euac_internals->create_cnt++; insert_uac_to_list(uac); euac_start(uac); unlock_events_uac(); return uac; } int destroy_events_uac(events_uac_t *uac) { if (!uac) { ERR("BUG: destroying empty uac\n"); return -1; } lock_events_uac(); euac_internals->destroy_cnt++; DBG("destroying uac %d from: %d\n", euac_internals->destroy_cnt, euac_internals->create_cnt); /* do not receive any other status/service messages */ uac->cb = NULL; uac->cbp = NULL; /* remove our reference, after unlock can be * the uac structure removed from memory by anybody !*/ if (!remove_euac_reference_nolock(uac)) { euac_do_step(act_destroy, NULL, uac); } unlock_events_uac(); return 0; } int process_euac_notify(struct sip_msg* m) { events_uac_t *uac; lock_events_uac(); uac = find_euac_nolock(m); if (!uac) { unlock_events_uac(); return -1; } euac_do_step(act_notify, m, uac); unlock_events_uac(); return 0; } /* ---- INITIALIZATION/DESTRUCTION ---- */ int events_uac_init() { if (!euac_internals) return init_events_uac_internals(); else return 0; } void events_uac_destroy() { if (euac_internals) destroy_events_uac_internals(); } kamailio-4.0.4/obsolete/presence_b2b/trace.h0000644000000000000000000000041412223032460017410 0ustar rootroot#ifndef __TRACE_H #define __TRACE_H #include #include #include #define mem_alloc cds_malloc #define mem_free cds_free #define TRACE(...) TRACE_LOG("presence_b2b: " __VA_ARGS__) /* #define TRACE(args...) */ #endif kamailio-4.0.4/obsolete/presence_b2b/rpc.c0000644000000000000000000000656212223032460017103 0ustar rootroot#include "rpc.h" #include "../../dprint.h" #include "events_uac.h" #include "euac_internals.h" #include /* #define rpc_lf(rpc, c) rpc->add(c, "s","") rpc->printf(c, " %.*s contact=\'%.*s\' exp=%u status=%d published=%d (id=%.*s)", FMT_STR(t->id), FMT_STR(t->contact), t->expires - time(NULL), (int)t->state, t->is_published, FMT_STR(t->published_id)); rpc_lf(rpc, c); */ #if 0 /* method for UAC testing */ static void test1(rpc_t *rpc, void *c) { /*events_uac_t *uac = NULL; str presence = STR_STATIC_INIT("presence"); str to = STR_STATIC_INIT(""); str from = STR_STATIC_INIT(""); str route = STR_STATIC_INIT(""); rpc->add(c, "s", "test called"); rpc->send(c); uac = create_events_uac(&to, &from, &presence, NULL, NULL, NULL, &route); sleep(5); destroy_events_uac(uac); */ } static void test2(rpc_t *rpc, void *c) { /* void *st; */ int i; /*rpc->add(c, "s", "test called"); rpc->send(c);*/ /* if (rpc->add(c, "{", &st) < 0) return; rpc->struct_add(st, "d", "watcher_t", sizeof(watcher_t)); rpc->struct_add(st, "d", "dlg_t", sizeof(dlg_t)); rpc->struct_add(st, "d", "presentity_t", sizeof(presentity_t)); rpc->send(c); */ for (i = 0; i < 1000; i++) { rpc->printf(c, "element %d with very long text\n", i); rpc->add(c, "s",""); } rpc->send(c); } #endif static void test(rpc_t *rpc, void *c) { /* void *st; */ int i, sum; char *x; /* int sizes[] = { 785, -1 }; */ int sizes[] = { 4, 24, 9, 4, 12, 12, 16, 12, 11, 4, -1 }; sum = 0; for (i = 0; sizes[i] >= 0; i++) { sum += sizes[i]; x = (char*) shm_malloc(sizes[i]); if (!x) rpc->fault(c, 500, "allocation error"); } rpc->add(c, "sd", "allocated bytes", sum); rpc->add(c, "sd", "allocated blocks", i); rpc->send(c); } /* Trace method */ #define rpc_lf(rpc, c) do { } while (0) /* #define rpc_lf(rpc, c) rpc->add(c, "s","") */ #define rpc_printf(rpc, c, buf, args...) sprintf(buf, ##args); rpc->add(c, "s", buf); static void trace(rpc_t *rpc, void *c) { int i = 0; events_uac_t *uac; char tmp[2048]; int detailed = 0; if (rpc->scan(c, "d", &detailed) <= 0) detailed = 0; rpc->fault(c, 200, "OK"); rpc->printf(c, "%s", "Presence B2BUA Trace:"); rpc_lf(rpc, c); if (euac_internals) { if (detailed) { uac = euac_internals->first_uac; while (uac) { rpc_printf(rpc, c, tmp, "[%s]: %d, refcnt: %d, timer started: %d", uac->id, uac->status, uac->ref_cntr.cntr, uac->timer_started); rpc_lf(rpc, c); uac = uac->next; i++; } } rpc_printf(rpc, c, tmp, "EUAC count: %d", i); rpc_lf(rpc, c); rpc_printf(rpc, c, tmp, "create_cnt: %d", euac_internals->create_cnt); rpc_lf(rpc, c); rpc_printf(rpc, c, tmp, "destroy_cnt: %d", euac_internals->destroy_cnt); rpc_lf(rpc, c); } else { rpc->printf(c, "euac_internals not set!"); } rpc->send(c); } /* ----- exported data structure with methods ----- */ static const char* test_doc[] = { "Testing events.", /* Documentation string */ 0 /* Method signature(s) */ }; static const char* trace_doc[] = { "Trace events.", /* Documentation string */ 0 /* Method signature(s) */ }; /* * RPC Methods exported by this module */ rpc_export_t events_rpc_methods[] = { {"presence_b2b.test", test, test_doc, 0}, {"presence_b2b.trace", trace, trace_doc, 0}, {0, 0, 0, 0} }; kamailio-4.0.4/obsolete/presence_b2b/events_mod.h0000644000000000000000000000036112223032460020456 0ustar rootroot#ifndef __EVENTS_MOD_H #define __EVENTS_MOD_H #include "../../modules/tm/tm_load.h" #include "../../lib/srdb2/db.h" #include "../dialog/dlg_mod.h" #include "trace.h" /** default expiration timeout */ extern int default_expiration; #endif kamailio-4.0.4/obsolete/presence_b2b/ChangeLog0000644000000000000000000000375012223032460017721 0ustar rootroot2006-07-24 * corrected memory leak 2006-07-12 * improved dialog refreshing 2006-06-13 * few common functions moved into cds/sip_utils 2006-06-06 * corrected BUG reported by Luis Silva - added missing call to parse_from_header 2006-05-25 * added Max-Forwards header into SUBSCRIBE requests * removed unnecessary TM/DIALOG binding function call in module initialization 2006-04-27 * added parameter for delayed sending of SUBSCRIBE requests (max_subscribe_delay) 2006-04-21 * added parameter presence_outbound_proxy (may be used instead of presence_route or both together) - presence subscriptions are sent there if set 2006-04-11 * changed QSA (reduced memory allocations) 2006-04-10 * changed QSA - corrected content-type propagation * added parameter additional_presence_headers which holds additional headers put into SUBSCRIBE requests for presence events 2006-04-04 * sending empty string in SUBSCRIBE bodies - TCP requires content-length and if sending message with NULL body, it is not set 2006-03-22 * using get_content_length instead of strlen(body) * added "Content-Length: 0" into outgoing SUBSCRIBE requests (such message is otherwise not parsed by SER when using TCP transport) 2006-03-21 * subscription status propagation through QSA 2006-03-17 * trace function not writing into file (was due to memory leaks in fifo) 2006-03-09 * improved behavior if low memory * added new error transitions into state machine * corrected memory leaks in predestroyed state 2006-03-07 * improved robustness 2006-02-17 * corrected BUG (resubscribe without contact) * added parameters: wait_for_term_notify, resubscribe_delta, min_resubscribe_time * added documentation 2006-02-16 * corrected BUG (bad and missing state machine transitions) * added parameters: default_expiration, on_error_retry_time 2006-02-14 * created module * QSA notifier for "presence" - does SIP subscription as result of QSA subscription, SUBSCRIBE can use preset route (parameter presence_route) kamailio-4.0.4/obsolete/presence_b2b/euac_state_machine.h0000644000000000000000000000077612223032460022126 0ustar rootroot#ifndef __EUAC_STATE_MACHINE_H #define __EUAC_STATE_MACHINE_H #include "events_uac.h" typedef enum { act_1xx, /* not final responses */ act_2xx, /* all ok responses */ act_3xx, /* redirect responses */ act_4xx, /* all error responses 4xx, 5xx, 6xx, ... */ act_notify, /* NOTIFY arrives */ act_destroy, /* called destroy from client (like create) */ act_tick } euac_action_t; void euac_do_step(euac_action_t action, struct sip_msg *m, events_uac_t *uac); void euac_start(events_uac_t *uac); #endif kamailio-4.0.4/obsolete/presence_b2b/Makefile0000644000000000000000000000130412223032460017600 0ustar rootroot# $Id$ # # Registrar Presence User Agent # # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME = presence_b2b.so # if using libcds, the directive SER must be defined ! # and root ser directory must be in include directories DEFS+=-DSER INCLUDES += -I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 \ -I$(LOCALBASE)/include -I../../lib -I../.. LIBS += -L$(LOCALBASE)/lib -L/usr/pkg/lib -lxml2 SERLIBPATH=../../lib SER_LIBS=$(SERLIBPATH)/presence/ser_presence $(SERLIBPATH)/cds/ser_cds \ # not used: $(SERLIBPATH)/xcap/ser_xcap DEFS+=-DSER_MOD_INTERFACE SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2 include ../../Makefile.modules kamailio-4.0.4/md5.h0000644000000000000000000000314012223032460012631 0ustar rootroot/* $OpenBSD: md5.h,v 1.15 2004/05/03 17:30:14 millert Exp $ */ /* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. */ #ifndef _MD5_H_ #define _MD5_H_ #define MD5_BLOCK_LENGTH 64 #define MD5_DIGEST_LENGTH 16 #define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1) /* Probably not the proper place, but will do for Debian: */ #include /* fix types for Sun Solaris */ #if defined(__SVR4) || defined(__sun) typedef uint8_t u_int8_t; typedef uint32_t u_int32_t; typedef uint64_t u_int64_t; #endif typedef struct MD5Context { u_int32_t state[4]; /* state */ u_int64_t count; /* number of bits, mod 2^64 */ unsigned char buffer[MD5_BLOCK_LENGTH]; /* input buffer */ } MD5_CTX; void MD5Init(MD5_CTX *); void U_MD5Update(MD5_CTX *, const unsigned char *, size_t); void MD5Pad(MD5_CTX *); void U_MD5Final(unsigned char [MD5_DIGEST_LENGTH], MD5_CTX *); void MD5Transform(u_int32_t [4], const unsigned char [MD5_BLOCK_LENGTH]); static inline void MD5Update(MD5_CTX *ctx, const char *str, size_t len) { U_MD5Update(ctx, (const unsigned char *)str, len); } static inline void MD5Final(char buf[MD5_DIGEST_LENGTH], MD5_CTX *ctx) { U_MD5Final((unsigned char *)buf, ctx); } #endif /* _MD5_H_ */ kamailio-4.0.4/COPYING0000644000000000000000000004003512223032457013040 0ustar rootroot ------------------------------------------------------------------------- IMPORTANT NOTES 1) The GPL applies to this copy of SIP Router software (sip-router). 2) SIP-router software allows programmers to plug-in external modules to the core part. Note that GPL mandates all plug-ins developed for the SIP-router software are to be released under GPL license to be GPL-ed or use a GPL compatible free software license. (see http://www.gnu.org/copyleft/gpl-faq.html#GPLAndPlugins for a detailed explanation) 3) Note that the GPL bellow is copyrighted by the Free Software Foundation, but the SIP-router software is copyrighted by multiple individuals and companies. ------------------------------------------------------------------------- GNU Licence FAQ This FAQ provides answers to most frequently asked questions. To fully understand implications of the GNU license, read it. - you can run SIP-router for any purpose - you can redistribute it as long as you include source code and license conditions with the distribution - you cannot release programs derived from SIP-router without releasing their source code - you can not change the copyright of any part of SIP-router ------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS kamailio-4.0.4/pass_fd.h0000644000000000000000000000315112223032460013565 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _pass_fd_h #define _pass_fd_h #ifdef __OS_cygwin /* check if MSG_WAITALL is defined */ #include #include #ifndef MSG_WAITALL #define NO_MSG_WAITALL #define MSG_WAITALL 0x80000000 #endif /* MSG_WAITALL */ #ifndef MSG_DONTWAIT #define NO_MSG_DONTWAIT #endif /* MSG_DONT_WAIT */ #endif /* __OS_cygwin */ int send_fd(int unix_socket, void* data, int data_len, int fd); int receive_fd(int unix_socket, void* data, int data_len, int* fd, int flags); int recv_all(int socket, void* data, int data_len, int flags); int send_all(int socket, void* data, int data_len); #endif kamailio-4.0.4/endianness.c0000644000000000000000000000256712223032460014302 0ustar rootroot/* * $Id$ * * Copyright (C) 2008 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * endianness compile and runtime tests * * History: * -------- * 2008-06-13 created by andrei */ /*! * \file * \brief SIP-router core :: * \ingroup core * Module: \ref core */ #include "endianness.h" int _endian_test_int=1 /* used for the runtime endian tests */; /* return 0 on success, -1 on error (compile time detected endianness is * different from run time) */ int endianness_sanity_check() { #ifdef __IS_LITTLE_ENDIAN return is_little_endian()-1; #elif defined __IS_BIG_ENDIAN return is_big_endian()-1; #else #warning BUG: endianness macro are not defined return -1; #endif } kamailio-4.0.4/str_hash.h0000644000000000000000000000472412223032460013770 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * History: * -------- * 2006-02-02 created by andrei * 2006-11-24 added numeric string optimized hash function (andrei) * 2006-12-13 split into hashes.h (more generic) and str_hash.h (andrei) */ #ifndef _str_hashs_h #define _str_hashs_h #include "str.h" #include "hashes.h" #include "mem/mem.h" #include "clist.h" #include /* generic, simple str keyed hash */ struct str_hash_entry{ struct str_hash_entry* next; struct str_hash_entry* prev; str key; unsigned int flags; union{ void* p; char* s; int n; char data[sizeof(void*)]; }u; }; struct str_hash_head{ struct str_hash_entry* next; struct str_hash_entry* prev; }; struct str_hash_table{ struct str_hash_head* table; int size; }; /* returns 0 on success, <0 on failure */ inline static int str_hash_alloc(struct str_hash_table* ht, int size) { ht->table=(struct str_hash_head*)pkg_malloc(sizeof(struct str_hash_head)*size); if (ht->table==0) return -1; ht->size=size; return 0; } inline static void str_hash_init(struct str_hash_table* ht) { int r; for (r=0; rsize; r++) clist_init(&(ht->table[r]), next, prev); } inline static void str_hash_add(struct str_hash_table* ht, struct str_hash_entry* e) { int h; h=get_hash1_raw(e->key.s, e->key.len) % ht->size; clist_insert(&ht->table[h], e, next, prev); } inline static struct str_hash_entry* str_hash_get(struct str_hash_table* ht, const char* key, int len) { int h; struct str_hash_entry* e; h=get_hash1_raw(key, len) % ht->size; clist_foreach(&ht->table[h], e, next){ if ((e->key.len==len) && (memcmp(e->key.s, key, len)==0)) return e; } return 0; } #define str_hash_del(e) clist_rm(e, next, prev) #endif kamailio-4.0.4/rand/0000755000000000000000000000000012223032460012721 5ustar rootrootkamailio-4.0.4/rand/fastrand.c0000644000000000000000000000272512223032460014675 0ustar rootroot/* * fast pseudo random generation * * $Id$ * * Copyright (C) 2007 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* *History: *-------- * 2007-06-15 wrapper around isaac (see * http://www.burtleburtle.net/bob/rand/isaacafa.html) (andrei) */ #include "fastrand.h" #include #include "isaac/rand.h" #define FASTRAND_MAX ((unsigned int)(-1)) static randctx is_ctx; /* side effect: seeds also random w/ seed */ void fastrand_seed(unsigned int seed) { int i; srandom(seed); for (i=0; i # define STDIO # endif # ifndef STDDEF # include # define STDDEF # endif typedef unsigned long long ub8; #define UB8MAXVAL 0xffffffffffffffffLL #define UB8BITS 64 typedef signed long long sb8; #define SB8MAXVAL 0x7fffffffffffffffLL typedef unsigned int ub4; /* unsigned 4-byte quantities */ #define UB4MAXVAL 0xffffffff typedef signed int sb4; #define UB4BITS 32 #define SB4MAXVAL 0x7fffffff typedef unsigned short int ub2; #define UB2MAXVAL 0xffff #define UB2BITS 16 typedef signed short int sb2; #define SB2MAXVAL 0x7fff typedef unsigned char ub1; #define UB1MAXVAL 0xff #define UB1BITS 8 typedef signed char sb1; /* signed 1-byte quantities */ #define SB1MAXVAL 0x7f typedef int word; /* fastest type available */ #define bis(target,mask) ((target) |= (mask)) #define bic(target,mask) ((target) &= ~(mask)) #define bit(target,mask) ((target) & (mask)) #ifndef min # define min(a,b) (((a)<(b)) ? (a) : (b)) #endif /* min */ #ifndef max # define max(a,b) (((a)<(b)) ? (b) : (a)) #endif /* max */ #ifndef align # define align(a) (((ub4)a+(sizeof(void *)-1))&(~(sizeof(void *)-1))) #endif /* align */ #ifndef abs # define abs(a) (((a)>0) ? (a) : -(a)) #endif #define TRUE 1 #define FALSE 0 #define SUCCESS 0 /* 1 on VAX */ #endif /* STANDARD */ kamailio-4.0.4/rand/isaac/rand.c0000644000000000000000000000670512223032460015101 0ustar rootroot/* ------------------------------------------------------------------------------ rand.c: By Bob Jenkins. My random number generator, ISAAC. Public Domain. MODIFIED: 960327: Creation (addition of randinit, really) 970719: use context, not global variables, for internal state 980324: added main (ifdef'ed out), also rearranged randinit() 010626: Note that this is public domain ------------------------------------------------------------------------------ */ #ifndef STANDARD #include "standard.h" #endif #ifndef RAND #include "rand.h" #endif #define ind(mm,x) (*(ub4 *)((ub1 *)(mm) + ((x) & ((RANDSIZ-1)<<2)))) #define rngstep(mix,a,b,mm,m,m2,r,x) \ { \ x = *m; \ a = (a^(mix)) + *(m2++); \ *(m++) = y = ind(mm,x) + a + b; \ *(r++) = b = ind(mm,y>>RANDSIZL) + x; \ } void isaac(ctx) randctx *ctx; { register ub4 a,b,x,y,*m,*mm,*m2,*r,*mend; mm=ctx->randmem; r=ctx->randrsl; a = ctx->randa; b = ctx->randb + (++ctx->randc); for (m = mm, mend = m2 = m+(RANDSIZ/2); m>6 , a, b, mm, m, m2, r, x); rngstep( a<<2 , a, b, mm, m, m2, r, x); rngstep( a>>16, a, b, mm, m, m2, r, x); } for (m2 = mm; m2>6 , a, b, mm, m, m2, r, x); rngstep( a<<2 , a, b, mm, m, m2, r, x); rngstep( a>>16, a, b, mm, m, m2, r, x); } ctx->randb = b; ctx->randa = a; } #define mix(a,b,c,d,e,f,g,h) \ { \ a^=b<<11; d+=a; b+=c; \ b^=c>>2; e+=b; c+=d; \ c^=d<<8; f+=c; d+=e; \ d^=e>>16; g+=d; e+=f; \ e^=f<<10; h+=e; f+=g; \ f^=g>>4; a+=f; g+=h; \ g^=h<<8; b+=g; h+=a; \ h^=a>>9; c+=h; a+=b; \ } /* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */ void randinit(ctx, flag) randctx *ctx; word flag; { word i; ub4 a,b,c,d,e,f,g,h; ub4 *m,*r; ctx->randa = ctx->randb = ctx->randc = 0; m=ctx->randmem; r=ctx->randrsl; a=b=c=d=e=f=g=h=0x9e3779b9; /* the golden ratio */ for (i=0; i<4; ++i) /* scramble it */ { mix(a,b,c,d,e,f,g,h); } if (flag) { /* initialize using the contents of r[] as the seed */ for (i=0; irandcnt=RANDSIZ; /* prepare to use the first set of results */ } #ifdef NEVER int main() { ub4 i,j; randctx ctx; ctx.randa=ctx.randb=ctx.randc=(ub4)0; for (i=0; i<256; ++i) ctx.randrsl[i]=(ub4)0; randinit(&ctx, TRUE); for (i=0; i<2; ++i) { isaac(&ctx); for (j=0; j<256; ++j) { printf("%.8lx",ctx.randrsl[j]); if ((j&7)==7) printf("\n"); } } } #endif kamailio-4.0.4/rand/isaac/rand.h0000644000000000000000000000306112223032460015076 0ustar rootroot/* ------------------------------------------------------------------------------ rand.h: definitions for a random number generator By Bob Jenkins, 1996, Public Domain MODIFIED: 960327: Creation (addition of randinit, really) 970719: use context, not global variables, for internal state 980324: renamed seed to flag 980605: recommend RANDSIZL=4 for noncryptography. 010626: note this is public domain ------------------------------------------------------------------------------ */ #ifndef STANDARD #include "standard.h" #endif #ifndef RAND #define RAND #define RANDSIZL (8) /* I recommend 8 for crypto, 4 for simulations */ #define RANDSIZ (1<randcnt-- ? \ (isaac(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->randcnt]) : \ (r)->randrsl[(r)->randcnt]) #endif /* RAND */ kamailio-4.0.4/timer.h0000644000000000000000000001236112223032460013271 0ustar rootroot/* * $Id$ * * * timer related functions (public interface) * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of SIP-router, a free SIP server. * * SIP-router is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * SIP-router is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* History: * -------- * 2005-07-27 complete re-design/re-implementation (andrei) */ /** * @file * @brief SIP-router core :: timer related functions (public interface) * @ingroup core * * Module: \ref core * * - \ref TimerDoc */ /** * @page TimerDoc SIP-router's timer documentation * @verbinclude timers.txt * */ #ifndef timer_h #define timer_h #define USE_SLOW_TIMER /* use another process to run the timer handlers marked "slow" */ /*#define TIMER_DEBUG -- compile with -DTIMER_DEBUG*/ #include "clist.h" #include "dprint.h" #include "timer_ticks.h" #ifdef USE_SLOW_TIMER #include typedef unsigned short slow_idx_t; /* type fot the slow index */ extern pid_t slow_timer_pid; #endif /* deprecated, old, kept for compatibility */ typedef void (timer_function)(unsigned int ticks, void* param); /* deprecated, old, kept for compatibility get_ticks()*TIMER_TICK used to be the time in s for new code, use get_ticks_raw() and one of the macros defined in timer_ticks.h (.e.g TICKS_TO_S(tick) to convert to s or ms )*/ #define TIMER_TICK 1 /* 1 s, kept for compatibility */ /*function prototype to execute on mili-second based basic timers */ typedef void (utimer_function)(unsigned int uticks, void* param); struct timer_ln; /* forward decl */ /* new * params: * - handle pointer to the corresponding struct timer_ln * return: 0 if the timer is one shot, new expire interval if not, -1 * if periodic * e.g.: - a periodic timer would return: (ticks_t)(-1) or * ((struct timer_ln*)handle)->initial_timeout * - a timer which wants to expire again in x ms would return: * (x * TICKS_HZ + 999)/1000 */ typedef ticks_t (timer_handler_f)(ticks_t t, struct timer_ln* tl, void* data); /* timer flags */ #define F_TIMER_FAST 1 #define F_TIMER_ON_SLOW_LIST 0x100 #define F_TIMER_ACTIVE 0x200 /* timer is running or has run and expired (one shot) */ #ifdef TIMER_DEBUG #define F_TIMER_DELETED 0x400 #endif struct timer_ln{ /* timer_link already used in tm */ struct timer_ln* next; struct timer_ln* prev; ticks_t expire; ticks_t initial_timeout; void* data; timer_handler_f* f; volatile unsigned short flags; #ifdef USE_SLOW_TIMER volatile slow_idx_t slow_idx; #else unsigned short reserved; #endif #ifdef TIMER_DEBUG unsigned int expires_no; /* timer handler calls */ const char* add_file; const char* add_func; unsigned add_line; unsigned add_calls; const char* del_file; const char* del_func; unsigned del_line; unsigned int del_calls; unsigned int init; /* how many times was init/re-init */ #endif }; void timer_main(void); /* timer main loop, never exists */ int init_timer(void); int arm_timer(void); void destroy_timer(void); #ifdef USE_SLOW_TIMER int arm_slow_timer(void); void slow_timer_main(void); #endif struct timer_ln* timer_alloc(void); void timer_free(struct timer_ln* t); #ifdef TIMER_DEBUG /* use for a deleted/expired timer that you want to add again */ #define timer_reinit(tl) \ do{ \ (tl)->flags&=~((unsigned short)(F_TIMER_ON_SLOW_LIST | \ F_TIMER_ACTIVE));\ (tl)->init++; \ }while(0) #else /* use for a deleted/expired timer that you want to add again */ #define timer_reinit(tl) \ (tl)->flags&=~((unsigned short)(F_TIMER_ON_SLOW_LIST | \ F_TIMER_ACTIVE)) #endif #define timer_init(tl, fun, param, flgs) \ do{ \ memset((tl), 0, sizeof(struct timer_ln)); \ (tl)->f=(fun); \ (tl)->data=(param); \ (tl)->flags=(flgs); \ timer_reinit(tl); \ }while(0) #ifdef TIMER_DEBUG int timer_add_safe(struct timer_ln *tl, ticks_t delta, const char*, const char*, unsigned); int timer_del_safe(struct timer_ln *tl, const char*, const char*, unsigned); #define timer_add(tl, d) \ timer_add_safe((tl), (d), __FILE__, __FUNCTION__, __LINE__) #define timer_del(tl) \ timer_del_safe((tl), __FILE__, __FUNCTION__, __LINE__) #else int timer_add_safe(struct timer_ln *tl, ticks_t delta); int timer_del_safe(struct timer_ln *tl); #define timer_add timer_add_safe #define timer_del timer_del_safe #endif void timer_allow_del(void); /* old timer compatibility functions & structure */ struct sr_timer{ struct timer_ln tl; int id; timer_function* timer_f; void* t_param; }; /*register a periodic timer; * ret: <0 on error*/ int register_timer(timer_function f, void* param, unsigned int interval); ticks_t get_ticks(void); ticks_t get_ticks_raw(void); #endif kamailio-4.0.4/cfg_parser.c0000644000000000000000000005650312223032457014273 0ustar rootroot/* * $Id$ * Standalone Configuration File Parser * * Copyright (C) 2008 iptelorg GmbH * Written by Jan Janak * * This file is part of SER, a free SIP server. * * SER is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * SER is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*! * \file * \brief SIP-router core :: * \ingroup core * * Module: \ref core * * See \ref ConfigEngine * * \page ConfigEngine * In file \ref cfg_parser.c * Configuration examples * - \ref ConfigExample1 * - \ref ConfigExample2 * - \ref ConfigExample3 * - \ref ConfigExample4 * - \ref ConfigExample5 * - \ref ConfigExample6 * - \ref ConfigExample7 * - \ref ConfigExample8 * * Run-time Allocated Destination Variables * - the destination variable pointers in arrays are assigned at compile time * - The address of variables allocated on the heap (typically in dynamically allocated * structures) is not know and thus we need to assign NULL initially and change the pointer * at runtime. * (provide an example). * * Built-in parsing functions * * *_val functions parse the whole option body (including =) */ /*! \page ConfigExample1 Configuration engine Example 1: Options without values * \verbatim * str file = STR_STATIC_INIT("test.cfg"); * cfg_parser_t* parser; * int feature_a = 0, feature_b = 0; * * cfg_option_t options[] = { * {"enable_feature_a", .param = &feature_a, .val = 1}, * {"disable_feature_a", .param = &feature_a, .val = 0}, * {"feature_b", .param = &feature_b, .val = 1}, * {0} * }; * * if ((parser = cfg_parser_init(&file)) == NULL) { * ERR("Error while creating parser\n"); * return -1; * } * * cfg_set_options(parser, options); * * if (sr_cfg_parse(parser) < 0) { * ERR("Error while parsing configuration file\n"); * cfg_parser_close(parser); * return -1; * } * * cfg_parser_close(parser); \endverbatim */ /*! \page ConfigExample2 Configuration engine Example 2: Options with integer values \verbatim * cfg_option_t options[] = { * {"max_number", .param = &max_number, .f = cfg_parse_int_val }, * {"extra_checks", .param = &extra_checks, .f = cfg_parse_bool_val}, * {0} * }; \endverbatim */ /*! \page ConfigExample3 Configuration engine Example 3: Enum options \verbatim * int scope; * * cfg_option_t scopes[] = { * {"base", .param = &scope, .val = 1}, * {"onelevel", .param = &scope, .val = 2}, * {"one", .param = &scope, .val = 3}, * {"subtree", .param = &scope, .val = 4}, * {"sub", .param = &scope, .val = 5}, * {"children", .param = &scope, .val = 6}, * {0} * }; * * cfg_option_t options[] = { * {"scope", .param = scopes, .f = cfg_parse_enum_val}, * {0} * }; \endverbatim */ /*! \page ConfigExample4 Configuration engine Example 4: Options with string values \verbatim * str filename = STR_NULL; * * cfg_option_t options[] = { * {"filename", .param = &filename, .f = cfg_parse_str_val}, * {0} * }; * * - By default the function returns a pointer to an internal buffer which will be destroyed * by a subsequent call * - There are flags to tell the function to copy the resuting string in a pkg, shm, glibc, * or static buffers \endverbatim */ /*! \page ConfigExample5 Configuration engine Example 5: Custom value parsing * TBD */ /*! \page ConfigExample6 Configuration engine Example 6: Parsing Sections * TBD */ /*! \page ConfigExample7 Configuration engine Example 7: Default Options * TBD */ /*! \page ConfigExample8 Configuration engine Example 8: Memory management of strings * * Data types with fixed size are easy, they can be copied into a pre-allocated memory * buffer, strings cannot because their length is unknown. */ #include "cfg_parser.h" #include "mem/mem.h" #include "mem/shm_mem.h" #include "dprint.h" #include "trim.h" #include "ut.h" #include #include #include #include /*! \brief The states of the lexical scanner */ enum st { ST_S, /*!< Begin */ ST_A, /*!< Alphanumeric */ ST_AE, /*!< Alphanumeric escaped */ ST_Q, /*!< Quoted */ ST_QE, /*!< Quoted escaped */ ST_C, /*!< Comment */ ST_CE, /*!< Comment escaped */ ST_E, /*!< Escaped */ }; /*! \brief Test for alphanumeric characters */ #define IS_ALPHA(c) \ (((c) >= 'a' && (c) <= 'z') || \ ((c) >= 'A' && (c) <= 'Z') || \ ((c) >= '0' && (c) <= '9') || \ (c) == '_') /*! \brief Test for delimiter characters */ #define IS_DELIM(c) \ ((c) == '=' || \ (c) == ':' || \ (c) == ';' || \ (c) == '.' || \ (c) == ',' || \ (c) == '?' || \ (c) == '[' || \ (c) == ']' || \ (c) == '/' || \ (c) == '@' || \ (c) == '!' || \ (c) == '$' || \ (c) == '%' || \ (c) == '&' || \ (c) == '*' || \ (c) == '(' || \ (c) == ')' || \ (c) == '-' || \ (c) == '+' || \ (c) == '|' || \ (c) == '\'') /*! \brief Whitespace characters */ #define IS_WHITESPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r') #define IS_QUOTE(c) ((c) == '\"') /* Quote characters */ #define IS_COMMENT(c) ((c) == '#') /* Characters that start comments */ #define IS_ESCAPE(c) ((c) == '\\') /* Escape characters */ #define IS_EOL(c) ((c) == '\n') /* End of line */ /*! \brief * Append character to the value of current token */ #define PUSH(c) \ if (token->val.len >= MAX_TOKEN_LEN) { \ ERR("%s:%d:%d: Token too long\n", \ st->file, st->line, st->col); \ return -1; \ } \ if (token->val.len == 0) { \ token->start.line = st->line; \ token->start.col = st->col; \ } \ token->val.s[token->val.len++] = (c); /*! \brief * Return current token from the lexical analyzer */ #define RETURN(c) \ token->end.line = st->line; \ token->end.col = st->col; \ token->type = (c); \ print_token(token); \ return 0; /*! \brief * Get next character and update counters */ #define READ_CHAR \ c = fgetc(st->f); \ if (IS_EOL(c)) { \ st->line++; \ st->col = 0; \ } else { \ st->col++; \ } cfg_option_t cfg_bool_values[] = { {"yes", .val = 1}, {"true", .val = 1}, {"enable", .val = 1}, {"enabled", .val = 1}, {"1", .val = 1}, {"on", .val = 1}, {"no", .val = 0}, {"false", .val = 0}, {"disable", .val = 0}, {"disabled", .val = 0}, {"0", .val = 0}, {"off", .val = 0}, {0} }; static void print_token(cfg_token_t* token) { #ifdef EXTRA_DEBUG int i, j; char* buf; if ((buf = pkg_malloc(token->val.len * 2)) == NULL) { DBG("token(%d, '%.*s', <%d,%d>-<%d,%d>)\n", token->type, STR_FMT(&token->val), token->start.line, token->start.col, token->end.line, token->end.col); } else { for(i = 0, j = 0; i < token->val.len; i++) { switch(token->val.s[i]) { case '\n': buf[j++] = '\\'; buf[j++] = 'n'; break; case '\r': buf[j++] = '\\'; buf[j++] = 'r'; break; case '\t': buf[j++] = '\\'; buf[j++] = 't'; break; case '\0': buf[j++] = '\\'; buf[j++] = '0'; break; case '\\': buf[j++] = '\\'; buf[j++] = '\\'; break; default: buf[j++] = token->val.s[i]; } } DBG("token(%d, '%.*s', <%d,%d>-<%d,%d>)\n", token->type, j, buf, token->start.line, token->start.col, token->end.line, token->end.col); pkg_free(buf); } #endif /* EXTRA_DEBUG */ } int cfg_get_token(cfg_token_t* token, cfg_parser_t* st, unsigned int flags) { static int look_ahead = EOF; int c; enum st state; state = ST_S; token->val.s = token->buf; token->val.len = 0; if (look_ahead != EOF) { c = look_ahead; look_ahead = EOF; } else { READ_CHAR; } while(c != EOF) { switch(state) { case ST_S: if (flags & CFG_EXTENDED_ALPHA) { if (IS_WHITESPACE(c)) { /* Do nothing */ } else if (IS_ALPHA(c) || IS_ESCAPE(c) || IS_DELIM(c)) { PUSH(c); state = ST_A; } else if (IS_QUOTE(c)) { state = ST_Q; } else if (IS_COMMENT(c)) { state = ST_C; } else if (IS_EOL(c)) { PUSH(c); RETURN(c); } else { ERR("%s:%d:%d: Invalid character 0x%x\n", st->file, st->line, st->col, c); return -1; } } else { if (IS_WHITESPACE(c)) { /* Do nothing */ } else if (IS_ALPHA(c)) { PUSH(c); state = ST_A; } else if (IS_QUOTE(c)) { state = ST_Q; } else if (IS_COMMENT(c)) { state = ST_C; } else if (IS_ESCAPE(c)) { state = ST_E; } else if (IS_DELIM(c) || IS_EOL(c)) { PUSH(c); RETURN(c); } else { ERR("%s:%d:%d: Invalid character 0x%x\n", st->file, st->line, st->col, c); return -1; } } break; case ST_A: if (flags & CFG_EXTENDED_ALPHA) { if (IS_ALPHA(c) || IS_DELIM(c) || IS_QUOTE(c)) { PUSH(c); } else if (IS_ESCAPE(c)) { state = ST_AE; } else if (IS_COMMENT(c) || IS_EOL(c) || IS_WHITESPACE(c)) { look_ahead = c; RETURN(CFG_TOKEN_ALPHA); } else { ERR("%s:%d:%d: Invalid character 0x%x\n", st->file, st->line, st->col, c); return -1; } } else { if (IS_ALPHA(c)) { PUSH(c); } else if (IS_ESCAPE(c)) { state = ST_AE; } else if (IS_WHITESPACE(c) || IS_DELIM(c) || IS_QUOTE(c) || IS_COMMENT(c) || IS_EOL(c)) { look_ahead = c; RETURN(CFG_TOKEN_ALPHA); } else { ERR("%s:%d:%d: Invalid character 0x%x\n", st->file, st->line, st->col, c); return -1; } } break; case ST_AE: if (IS_COMMENT(c) || IS_QUOTE(c) || IS_ESCAPE(c)) { PUSH(c); } else if (c == 'r') { PUSH('\r'); } else if (c == 'n') { PUSH('\n'); } else if (c == 't') { PUSH('\t'); } else if (c == ' ') { PUSH(' '); } else if (IS_EOL(c)) { /* Do nothing */ } else { ERR("%s:%d:%d: Unsupported escape character 0x%x\n", st->file, st->line, st->col, c); return -1; } state = ST_A; break; case ST_Q: if (IS_QUOTE(c)) { RETURN(CFG_TOKEN_STRING); } else if (IS_ESCAPE(c)) { state = ST_QE; break; } else { PUSH(c); } break; case ST_QE: if (IS_ESCAPE(c) || IS_QUOTE(c)) { PUSH(c); } else if (c == 'n') { PUSH('\n'); } else if (c == 'r') { PUSH('\r'); } else if (c == 't') { PUSH('\t'); } else if (IS_EOL(c)) { /* Do nothing */ } else { ERR("%s:%d:%d: Unsupported escape character 0x%x\n", st->file, st->line, st->col, c); return -1; } state = ST_Q; break; case ST_C: if (IS_ESCAPE(c)) { state = ST_CE; } else if (IS_EOL(c)) { state = ST_S; continue; /* Do not read a new char, return EOL */ } else { /* Do nothing */ } break; case ST_CE: state = ST_C; break; case ST_E: if (IS_COMMENT(c) || IS_QUOTE(c) || IS_ESCAPE(c)) { PUSH(c); RETURN(c); } else if (c == 'r') { PUSH('\r'); RETURN('\r'); } else if (c == 'n') { PUSH('\n'); RETURN('\n'); } else if (c == 't') { PUSH('\t'); RETURN('\t'); } else if (c == ' ') { PUSH(' '); RETURN(' '); } else if (IS_EOL(c)) { /* Escped eol means no eol */ state = ST_S; } else { ERR("%s:%d:%d: Unsupported escape character 0x%x\n", st->file, st->line, st->col, c); return -1; } break; } READ_CHAR; }; switch(state) { case ST_S: case ST_C: case ST_CE: return 1; case ST_A: RETURN(CFG_TOKEN_ALPHA); case ST_Q: ERR("%s:%d:%d: Premature end of file, missing closing quote in" " string constant\n", st->file, st->line, st->col); return -1; case ST_QE: case ST_E: case ST_AE: ERR("%s:%d:%d: Premature end of file, missing escaped character\n", st->file, st->line, st->col); return -1; } BUG("%s:%d:%d: Invalid state %d\n", st->file, st->line, st->col, state); return -1; } int cfg_parse_section(void* param, cfg_parser_t* st, unsigned int flags) { cfg_token_t t; int ret; ret = cfg_parse_str(param, st, flags); if (ret < 0) return ret; if (ret > 0) { ERR("%s:%d:%d: Section name missing.\n", st->file, st->line, st->col); return ret; } ret = cfg_get_token(&t, st, flags); if (ret < 0) goto error; if (ret > 0) { ERR("%s:%d:%d: Closing ']' missing\n", st->file, st->line, st->col); goto error; } if (t.type != ']') { ERR("%s:%d:%d: Syntax error, ']' expected\n", st->file, t.start.line, t.start.col); goto error; } if (cfg_eat_eol(st, flags)) goto error; return 0; error: if (param && ((str*)param)->s) { if (flags & CFG_STR_PKGMEM) { pkg_free(((str*)param)->s); ((str*)param)->s = NULL; } else if (flags & CFG_STR_SHMMEM) { shm_free(((str*)param)->s); ((str*)param)->s = NULL; } else if (flags & CFG_STR_MALLOC) { free(((str*)param)->s); ((str*)param)->s = NULL; } } return -1; } static char* get_base_name(str* filename) { char* tmp1, *tmp2, *res; int len; res = NULL; if ((tmp1 = as_asciiz(filename)) == NULL) { ERR("cfg_parser: No memory left\n"); goto error; } if ((tmp2 = basename(tmp1)) == NULL) { ERR("cfg_parser: Error in basename\n"); goto error; } len = strlen(tmp2); if ((res = pkg_malloc(len + 1)) == NULL) { ERR("cfg_parser: No memory left"); goto error; } memcpy(res, tmp2, len + 1); pkg_free(tmp1); return res; error: if (tmp1) pkg_free(tmp1); return NULL; } /** intialize the config parser. * @param basedir - path to the config file name. If 0 the path * (base directory) of the main ser.cfg file will be used, else * basedir will be concatenated to the filename. It will be * used only if filename is not an absolute path. * @param filename - config filename (can include path elements). * @return 0 on error, !=0 on success. */ cfg_parser_t* cfg_parser_init(str* basedir, str* filename) { cfg_parser_t* st; char* pathname, *base, *abs_pathname; abs_pathname = NULL; pathname = filename->s; st = NULL; base = NULL; /* if basedir == 0 or != "" get_abs_pathname */ if (basedir == 0 || basedir->len != 0) { if ((abs_pathname = get_abs_pathname(basedir, filename)) == NULL) { ERR("cfg_parser: Error while converting %.*s to absolute" " pathname\n", STR_FMT(filename)); goto error; } pathname = abs_pathname; } if ((base = get_base_name(filename)) == NULL) goto error; if ((st = (cfg_parser_t*)pkg_malloc(sizeof(*st))) == NULL) { ERR("cfg_parser: No memory left\n"); goto error; } memset(st, '\0', sizeof(*st)); if ((st->f = fopen(pathname, "r")) == NULL) { ERR("cfg_parser: Unable to open file '%s'\n", pathname); goto error; } if (abs_pathname) pkg_free(abs_pathname); st->file = base; st->line = 1; st->col = 0; return st; error: if (st) { if (st->f) fclose(st->f); pkg_free(st); } if (base) pkg_free(base); if (abs_pathname) pkg_free(abs_pathname); return NULL; } void cfg_parser_close(cfg_parser_t* st) { if (!st) return; if (st->f) fclose(st->f); if (st->file) pkg_free(st->file); pkg_free(st); } void cfg_section_parser(cfg_parser_t* st, cfg_func_f parser, void* param) { if (st == NULL) return; st->section.parser = parser; st->section.param = param; } void cfg_set_options(cfg_parser_t* st, cfg_option_t* options) { if (st) st->options = options; } static int process_option(cfg_parser_t* st, cfg_option_t* opt) { if (opt->f) { /* We have a function so store it and pass opt->dst to it */ if (opt->f(opt->param, st, opt->flags) < 0) return -1; } else { /* We have no function, so if we have a pointer to some * variable in opt->param then store the value of opt->i * there, the variable is assumed to be of type i */ if (opt->param) *(int*)opt->param = opt->val; } return 0; } int sr_cfg_parse(cfg_parser_t* st) { int ret; cfg_token_t t; cfg_option_t* opt; while(1) { ret = cfg_get_token(&t, st, 0); if (ret < 0) return ret; if (ret > 0) break; switch(t.type) { case CFG_TOKEN_ALPHA: /* Lookup the option name */ if ((opt = cfg_lookup_token(st->options, &t.val)) == NULL) { ERR("%s:%d:%d: Unsupported option '%.*s'\n", st->file, t.start.line, t.start.col, STR_FMT(&t.val)); return -1; } st->cur_opt = &t; if (process_option(st, opt) < 0) return -1; break; case '[': if (st->section.parser == NULL) { ERR("%s:%d:%d: Syntax error\n", st->file, t.start.line, t.start.col); return -1; } if (st->section.parser(st->section.param, st, 0) < 0) return -1; break; /* Empty line */ case '\n': continue; default: ERR("%s:%d:%d: Syntax error\n", st->file, t.start.line, t.start.col); return -1; } } return 0; } cfg_option_t* cfg_lookup_token(cfg_option_t* table, str* token) { int len, i; int (*cmp)(const char* s1, const char* s2, size_t n) = NULL; if (table == NULL) return NULL; for(i = 0; table[i].name; i++) { len = strlen(table[i].name); if (table[i].flags & CFG_PREFIX) { if (token->len < len) continue; } else { if (token->len != len) continue; } if (table[i].flags & CFG_CASE_SENSITIVE) cmp = strncmp; else cmp = strncasecmp; if (cmp(token->s, table[i].name, len)) continue; return table + i; } if (table[i].flags & CFG_DEFAULT) { return table + i; } return NULL; } int cfg_eat_equal(cfg_parser_t* st, unsigned int flags) { cfg_token_t t; int ret; ret = cfg_get_token(&t, st, flags); if (ret < 0) return ret; if (ret > 0) { ERR("%s:%d:%d: Delimiter '=' missing\n", st->file, st->line, st->col); return ret; } if (t.type != '=') { ERR("%s:%d:%d: Syntax error, '=' expected\n", st->file, t.start.line, t.start.col); return -1; } return 0; } int cfg_eat_eol(cfg_parser_t* st, unsigned int flags) { cfg_token_t t; int ret; /* Skip EOL */ ret = cfg_get_token(&t, st, 0); if (ret < 0) return ret; if (ret > 0) return 0; if (t.type != '\n') { ERR("%s:%d:%d: End of line expected\n", st->file, t.start.line, t.start.col); return -1; } return 0; } int cfg_parse_enum(void* param, cfg_parser_t* st, unsigned int flags) { int ret; cfg_token_t t; cfg_option_t* values, *val; values = (cfg_option_t*)param; ret = cfg_get_token(&t, st, flags); if (ret != 0) return ret; if (t.type != CFG_TOKEN_ALPHA && t.type != CFG_TOKEN_STRING) { ERR("%s:%d:%d: Invalid enum value '%.*s'\n", st->file, t.start.line, t.start.col, STR_FMT(&t.val)); return -1; } if (values) { if ((val = cfg_lookup_token(values, &t.val)) == NULL) { ERR("%s:%d:%d Unsupported enum value '%.*s'\n", st->file, t.start.line, t.start.col, STR_FMT(&t.val)); return -1; } return process_option(st, val); } else { return 0; } } int cfg_parse_enum_opt(void* param, cfg_parser_t* st, unsigned int flags) { int ret; if (cfg_eat_equal(st, flags)) return -1; ret = cfg_parse_enum(param, st, CFG_EXTENDED_ALPHA | flags); if (ret > 0) { ERR("%s:%d:%d: Option value missing\n", st->file, st->line, st->col); return ret; } else if (ret < 0) return ret; if (cfg_eat_eol(st, flags)) return -1; return 0; } int cfg_parse_str(void* param, cfg_parser_t* st, unsigned int flags) { str* val; int ret; char* buf; cfg_token_t t; ret = cfg_get_token(&t, st, flags); if (ret != 0) return ret; if (t.type != CFG_TOKEN_ALPHA && t.type != CFG_TOKEN_STRING) { ERR("%s:%d:%d: Invalid string value '%.*s', a string expected.\n", st->file, t.start.line, t.start.col, STR_FMT(&t.val)); return -1; } if (!param) return 0; val = (str*)param; if (flags & CFG_STR_STATIC) { if (!val->s || val->len <= t.val.len) { ERR("%s:%d:%d: Destination string buffer too small\n", st->file, t.start.line, t.start.col); return -1; } buf = val->s; } else if (flags & CFG_STR_SHMMEM) { if ((buf = shm_malloc(t.val.len + 1)) == NULL) { ERR("%s:%d:%d: Out of shared memory\n", st->file, t.start.line, t.start.col); return -1; } if (val->s) shm_free(val->s); } else if (flags & CFG_STR_MALLOC) { if ((buf = malloc(t.val.len + 1)) == NULL) { ERR("%s:%d:%d: Out of malloc memory\n", st->file, t.start.line, t.start.col); return -1; } if (val->s) free(val->s); } else if (flags & CFG_STR_PKGMEM) { if ((buf = pkg_malloc(t.val.len + 1)) == NULL) { ERR("%s:%d:%d: Out of private memory\n", st->file, t.start.line, t.start.col); return -1; } if (val->s) pkg_free(val->s); } else { *val = t.val; return 0; } memcpy(buf, t.val.s, t.val.len); buf[t.val.len] = '\0'; val->s = buf; val->len = t.val.len; return 0; } int cfg_parse_str_opt(void* param, cfg_parser_t* st, unsigned int flags) { int ret; if (cfg_eat_equal(st, flags)) return -1; ret = cfg_parse_str(param, st, flags | CFG_EXTENDED_ALPHA); if (ret > 0) { ERR("%s:%d:%d: Option value missing\n", st->file, st->line, st->col); } else if (ret < 0) return ret; if (cfg_eat_eol(st, flags)) return -1; return 0; } int cfg_parse_int(void* param, cfg_parser_t* st, unsigned int flags) { int* val; int ret, tmp; cfg_token_t t; val = (int*)param; ret = cfg_get_token(&t, st, flags); if (ret != 0) return ret; if (t.type != CFG_TOKEN_ALPHA && t.type != CFG_TOKEN_STRING) { ERR("%s:%d:%d: Invalid integer value '%.*s'\n", st->file, t.start.line, t.start.col, STR_FMT(&t.val)); return -1; } if (str2sint(&t.val, &tmp) < 0) { ERR("%s:%d:%d: Invalid integer value '%.*s'\n", st->file, t.start.line, t.start.col, STR_FMT(&t.val)); return -1; } if (val) *val = tmp; return 0; } int cfg_parse_int_opt(void* param, cfg_parser_t* st, unsigned int flags) { int ret; if (cfg_eat_equal(st, flags)) return -1; ret = cfg_parse_int(param, st, flags); if (ret > 0) { ERR("%s:%d:%d: Option value missing\n", st->file, st->line, st->col); } else if (ret < 0) return ret; if (cfg_eat_eol(st, flags)) return -1; return 0; } int cfg_parse_bool(void* param, cfg_parser_t* st, unsigned int flags) { int ret, *val; cfg_token_t t; cfg_option_t* map; val = (int*)param; ret = cfg_get_token(&t, st, flags); if (ret != 0) return ret; if (t.type != CFG_TOKEN_ALPHA && t.type != CFG_TOKEN_STRING) { ERR("%s:%d:%d: Invalid option value '%.*s', boolean expected\n", st->file, t.start.line, t.start.col, STR_FMT(&t.val)); return -1; } if ((map = cfg_lookup_token(cfg_bool_values, &t.val)) == NULL) { ERR("%s:%d:%d: Invalid option value '%.*s', boolean expected\n", st->file, t.start.line, t.start.col, STR_FMT(&t.val)); return -1; } if (val) *val = map->val; return 0; } int cfg_parse_bool_opt(void* param, cfg_parser_t* st, unsigned int flags) { int ret; if (cfg_eat_equal(st, flags)) return -1; ret = cfg_parse_bool(param, st, CFG_EXTENDED_ALPHA | flags); if (ret > 0) { ERR("%s:%d:%d: Option value missing\n", st->file, st->line, st->col); } else if (ret < 0) return ret; if (cfg_eat_eol(st, flags)) return -1; return 0; } kamailio-4.0.4/dset.h0000644000000000000000000001444612223032461013117 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2004 FhG FOKUS * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*! * \file * \brief SIP-router core :: Destination set handling * \ingroup core * Module: \ref core */ #ifndef _DSET_H #define _DSET_H #include "ip_addr.h" #include "qvalue.h" #include "flags.h" #include "parser/msg_parser.h" extern unsigned int nr_branches; extern int ruri_is_new; /*! \brief * Structure for storing branch attributes */ struct branch { char uri[MAX_URI_SIZE]; unsigned int len; /* Real destination of the request */ char dst_uri[MAX_URI_SIZE]; unsigned int dst_uri_len; /* Path set */ char path[MAX_PATH_SIZE]; unsigned int path_len; int q; /* Preference of the contact among * contact within the array */ struct socket_info* force_send_socket; /* +sip.instance contact header param value */ char instance[MAX_INSTANCE_SIZE]; unsigned int instance_len; /* reg-id contact header param value */ unsigned int reg_id; /* Branch flags */ flag_t flags; }; typedef struct branch branch_t; /*! \brief * Return pointer to branch[idx] structure */ branch_t *get_sip_branch(int idx); /*! \brief * Drop branch[idx] */ int drop_sip_branch(int idx); /*! \brief * Add a new branch to current transaction */ int append_branch(struct sip_msg* msg, str* uri, str* dst_uri, str* path, qvalue_t q, unsigned int flags, struct socket_info* force_socket, str* instance, unsigned int reg_id); /*! \brief kamailio compatible version */ #define km_append_branch(msg, uri, dst_uri, path, q, flags, force_socket) \ append_branch(msg, uri, dst_uri, path, q, flags, force_socket, 0, 0) /*! \brief ser compatible append_branch version. * append_branch version compatible with ser: no path or branch flags support * and no str parameters. */ static inline int ser_append_branch(struct sip_msg* msg, char* uri, int uri_len, char* dst_uri, int dst_uri_len, qvalue_t q, struct socket_info* force_socket) { str s_uri, s_dst_uri; s_uri.s=uri; s_uri.len=uri_len; s_dst_uri.s=dst_uri; s_dst_uri.len=dst_uri_len; return append_branch(msg, &s_uri, &s_dst_uri, 0, q, 0, force_socket, 0, 0); } /*! \brief * Init the index to iterate through the list of transaction branches */ void init_branch_iterator(void); /*! \brief * Return branch iterator position */ int get_branch_iterator(void); /*! \brief * Set branch iterator position */ void set_branch_iterator(int n); /*! \brief Get the next branch in the current transaction. * @return pointer to the uri of the next branch (which the length written in * *len) or 0 if there are no more branches. */ char* next_branch(int* len, qvalue_t* q, str* dst_uri, str* path, unsigned int* flags, struct socket_info** force_socket); char* get_branch( unsigned int i, int* len, qvalue_t* q, str* dst_uri, str* path, unsigned int *flags, struct socket_info** force_socket); /*! \brief * Empty the array of branches */ void clear_branches(void); /*! \brief * Create a Contact header field from the * list of current branches */ char* print_dset(struct sip_msg* msg, int* len); /*! \brief * Set the q value of the Request-URI */ void set_ruri_q(qvalue_t q); /*! \brief * Get the q value of the Request-URI */ qvalue_t get_ruri_q(void); /* * Get actual Request-URI */ inline static int get_request_uri(struct sip_msg* _m, str* _u) { *_u=*GET_RURI(_m); return 0; } #define ruri_mark_new() (ruri_is_new = 1) #define ruri_mark_consumed() (ruri_is_new = 0) /** returns whether or not ruri should be used when forking. * (usefull for serial forking) * @return 0 if already marked as consumed, 1 if not. */ #define ruri_get_forking_state() (ruri_is_new) int rewrite_uri(struct sip_msg* _m, str* _s); /*! \brief * Set a per-branch flag to 1. * * This function sets the value of one particular branch flag to 1. * @param branch Number of the branch (0 for the main Request-URI branch) * @param flag Number of the flag to be set (starting with 0) * @return 1 on success, -1 on failure. */ int setbflag(unsigned int branch, flag_t flag); /*! \brief * Reset a per-branch flag value to 0. * * This function resets the value of one particular branch flag to 0. * @param branch Number of the branch (0 for the main Request-URI branch) * @param flag Number of the flag to be reset (starting with 0) * @return 1 on success, -1 on failure. */ int resetbflag(unsigned int branch, flag_t flag); /*! \brief * Determine if a branch flag is set. * * This function tests the value of one particular per-branch flag. * @param branch Number of the branch (0 for the main Request-URI branch) * @param flag Number of the flag to be tested (starting with 0) * @return 1 if the branch flag is set, -1 if not or on failure. */ int isbflagset(unsigned int branch, flag_t flag); /*! \brief * Get the value of all branch flags for a branch * * This function returns the value of all branch flags * combined in a single variable. * @param branch Number of the branch (0 for the main Request-URI branch) * @param res A pointer to a variable to store the result * @return 1 on success, -1 on failure */ int getbflagsval(unsigned int branch, flag_t* res); /*! \brief * Set the value of all branch flags at once for a given branch. * * This function sets the value of all branch flags for a given * branch at once. * @param branch Number of the branch (0 for the main Request-URI branch) * @param val All branch flags combined into a single variable * @return 1 on success, -1 on failure */ int setbflagsval(unsigned int branch, flag_t val); #endif /* _DSET_H */ kamailio-4.0.4/tcp_stats.h0000644000000000000000000000722612223032460014161 0ustar rootroot/* * $Id$ * * Copyright (C) 2009 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * tcp_stats.h - tcp statistics macros */ /* * History: * -------- * 2009-04-08 initial version (andrei) */ #ifndef __tcp_stats_h #define __tcp_stats_h /* enable tcp stats by default */ #ifndef NO_TCP_STATS #define USE_TCP_STATS #endif #ifndef USE_TCP_STATS #define INIT_TCP_STATS() 0 /* success */ #define DESTROY_TCP_STATS() #define TCP_STATS_ESTABLISHED(state) #define TCP_STATS_CONNECT_FAILED() #define TCP_STATS_LOCAL_REJECT() #define TCP_STATS_CON_TIMEOUT() #define TCP_STATS_CON_RESET() #define TCP_STATS_SEND_TIMEOUT() #define TCP_STATS_SENDQ_FULL() #else /* USE_TCP_STATS */ #include "counters.h" struct tcp_counters_h { counter_handle_t established; counter_handle_t passive_open; counter_handle_t connect_success; counter_handle_t connect_failed; counter_handle_t local_reject; counter_handle_t con_timeout; counter_handle_t con_reset; counter_handle_t send_timeout; counter_handle_t sendq_full; }; extern struct tcp_counters_h tcp_cnts_h; int tcp_stats_init(void); void tcp_stats_destroy(void); #define INIT_TCP_STATS() tcp_stats_init() #define DESTROY_TCP_STATS() tcp_stats_destroy() /** called each time a new tcp connection is established. * @param state - S_CONN_ACCEPT if it was the result of an accept() * - S_CONN_CONNECT if it was the result of a connect() * Note: in general it will be called when the first packet was received or * sent on the new connection and not immediately after accept() or * connect() */ #define TCP_STATS_ESTABLISHED(state) \ do { \ counter_inc(tcp_cnts_h.established); \ if (state == S_CONN_ACCEPT) \ counter_inc(tcp_cnts_h.passive_open); \ else \ counter_inc(tcp_cnts_h.connect_success); \ }while(0) /** called each time a new outgoing connection fails. */ #define TCP_STATS_CONNECT_FAILED() \ counter_inc(tcp_cnts_h.connect_failed) /** called each time a new incoming connection is rejected. * (accept() denied due to maximum number of TCP connections being exceeded) */ #define TCP_STATS_LOCAL_REJECT() \ counter_inc(tcp_cnts_h.local_reject) /** called each time a connection lifetime expires. * (the connection is closed for being idle for too long) */ #define TCP_STATS_CON_TIMEOUT() \ counter_inc(tcp_cnts_h.con_timeout) /** called each time a TCP RST is received on an established connection. */ #define TCP_STATS_CON_RESET() \ counter_inc(tcp_cnts_h.con_reset) /** called each time a send operation fails due to a timeout. * FIXME: it works only in async mode (in sync. mode a send might timeout * but the stats won't be increased). */ #define TCP_STATS_SEND_TIMEOUT() \ counter_inc(tcp_cnts_h.send_timeout) /** called each time a send fails due to the buffering capacity being exceeded. * (used only in tcp async mode) */ #define TCP_STATS_SENDQ_FULL() \ counter_inc(tcp_cnts_h.sendq_full) #endif /* USE_TCP_STATS */ #endif /*__tcp_stats_h*/ /* vi: set ts=4 sw=4 tw=79:ai:cindent: */ kamailio-4.0.4/ccopts.sh0000755000000000000000000000442512223032457013642 0ustar rootroot#!/bin/sh #$Id$ # # returns the CFLAGS for the given compiler (maximum optimizations) # ARCH=`uname -m |sed -e s/i.86/i386/ -e s/sun4u/sparc64/ ` # gcc 3.x optimize for: x86CPU=athlon WARN_ARCH="WARNING: Not tested on architecture $ARCH, using default flags" if [ $# -lt 1 ] then echo "ERROR: you must specify the compiler name" 1>&2 exit 1 fi if [ "$1" = "-h" ] then echo "Usage: " echo " $0 compiler_name" exit 1 fi if CCVER=`./ccver.sh $1` then NAME=`echo "$CCVER"|cut -d" " -f 1` VER=`echo "$CCVER"|cut -d" " -f 2` MAJOR_VER=`echo "$CCVER"|cut -d" " -f 3` else echo "ERROR executing ./ccver.sh" 2>&1 exit 1 fi echo "name=$NAME, ver=$VER, mver=$MAJOR_VER" case $NAME in gcc) #common stuff CFLAGS="-O9 -funroll-loops -Winline -Wall" case $MAJOR_VER in 3) case $ARCH in i386) CFLAGS="$CFLAGS -minline-all-stringops -malign-double" CFLAGS="$CFLAGS -falign-loops -march=$x86CPU" ;; sparc64) CFLAGS="$CFLAGS -mcpu=ultrasparc -mtune=ultrasparc" CFLAGS="$CFLAGS -m32" #other interesting options: # -mcpu=v9 or ultrasparc? # -mtune implied by -mcpu #-mno-epilogue #try to inline function exit code #-mflat # omit save/restore #-faster-structs #faster non Sparc ABI structure copy ;; armv4l) CFLAGS="$CFLAGS -mcpu=strongarm1100" ;; *) echo "$WARN_ARCH" 1>&2 ;; esac ;; 2|*) case $ARCH in i386) CFLAGS="$CFLAGS -m486 -malign-loops=4" ;; sparc64) CFLAGS="$CFLAGS -mv8 -Wa,-xarch=v8plus" ;; armv4l) ;; *) echo "$WARN_ARCH" 1>&2 ;; esac ;; esac ;; icc) CFLAGS="-O3 -ipo -ipo_obj -unroll" case $ARCH in i386) CFLAGS="$CFLAGS -tpp6 -xK" #-openmp #optimize for PIII # -prefetch doesn't seem to work #( ty to inline acroos files, unroll loops,prefetch, # optimize for PIII, use PIII instructions & vect., # mutlithread loops) ;; *) echo "$WARN_ARCH" 1>&2 ;; esac ;; suncc) CFLAGS="-xO5 -fast -native -xCC -xc99" case $ARCH in sparc64) CFLAGS="$CFLAGS -xarch=v8plusa" ;; *) echo "$WARN_ARCH" 1>&2 ;; esac ;; *) echo "WARNING: unknown compiler $NAME, trying _very_ generic flags" 1>&2 CFLAGS="-O2" esac echo "CFLAGS=$CFLAGS" kamailio-4.0.4/trim.h0000644000000000000000000000604512223032460013126 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef TRIM_H #define TRIM_H #include "str.h" /* * This switch-case statement is used in * trim_leading and trim_trailing. You can * define characters that should be skipped * here. */ #define TRIM_SWITCH(c) switch(c) { \ case ' ': \ case '\t': \ case '\r': \ case '\n': \ break; \ \ default: \ return; \ } /* * Remove any leading whitechars, like spaces, * horizontal tabs, carriage returns and line * feeds * * WARNING: String descriptor structure will be * modified ! Make a copy otherwise you * might be unable to free _s->s for * example ! * */ static inline void trim_leading(str* _s) { for(; _s->len > 0; _s->len--, _s->s++) { TRIM_SWITCH(*(_s->s)); } } /* * Remove any trailing white char, like spaces, * horizontal tabs, carriage returns and line feeds * * WARNING: String descriptor structure will be * modified ! Make a copy otherwise you * might be unable to free _s->s for * example ! */ static inline void trim_trailing(str* _s) { for(; _s->len > 0; _s->len--) { TRIM_SWITCH(_s->s[_s->len - 1]); } } /* * Do trim_leading and trim_trailing * * WARNING: String structure will be modified ! * Make a copy otherwise you might be * unable to free _s->s for example ! */ static inline void trim(str* _s) { trim_leading(_s); trim_trailing(_s); } /* * right and left space trimming * * WARNING: String structure will be modified ! * Make a copy otherwise you might be * unable to free _s_->s for example ! */ #define trim_spaces_lr(_s_) \ do{ \ for(;(_s_).s[(_s_).len-1]==' ';(_s_).s[--(_s_).len]=0); \ for(;(_s_).s[0]==' ';(_s_).s=(_s_).s+1,(_s_).len--); \ \ } while(0); #endif /* TRIM_H */ kamailio-4.0.4/mod_fix.c0000644000000000000000000003446712223032460013604 0ustar rootroot/* * Copyright (C) 2008 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * History: * -------- * 2008-11-25 initial version (andrei) */ /*! * \file * \brief SIP-router core :: kamailio compatible fixups * \ingroup core * Module: \ref core */ #include "mod_fix.h" #include "mem/mem.h" #include "trim.h" #if 0 /* TODO: */ int fixup_regexpNL_null(void** param, int param_no); /* not used */ int fixup_regexpNL_none(void** param, int param_no); /* textops */ #endif #define FREE_FIXUP_FP(suffix, minp, maxp) \ int fixup_free_##suffix(void** param, int param_no) \ { \ if ((param_no > (maxp)) || (param_no < (minp))) \ return E_UNSPEC; \ if (*param) \ fparam_free_restore(param); \ return 0; \ } /** macro for declaring a fixup and the corresponding free_fixup * for a function which fixes to fparam_t and expects 2 different types. * * The result (in *param) will be a fparam_t. * * @param suffix - function suffix (fixup_ will be pre-pended to it * @param minp - minimum parameter number acceptable * @param maxp - maximum parameter number * @param no1 - number of parameters of type1 * @param type1 - fix_param type for the 1st param * @param type2 - fix_param type for all the other params */ #define FIXUP_F2FP(suffix, minp, maxp, no1, type1, type2) \ int fixup_##suffix (void** param, int param_no) \ { \ if ((param_no > (maxp)) || (param_no <(minp))) \ return E_UNSPEC; \ if (param_no <= (no1)){ \ if (fix_param_types((type1), param)!=0) {\ ERR("Cannot convert function parameter %d to" #type1 "\n", \ param_no);\ return E_UNSPEC; \ } \ }else{ \ if (fix_param_types((type2), param)!=0) {\ ERR("Cannot convert function parameter %d to" #type2 "\n", \ param_no); \ return E_UNSPEC; \ } \ }\ return 0; \ } \ FREE_FIXUP_FP(suffix, minp, maxp) /** macro for declaring a fixup and the corresponding free_fixup * for a function which fixes directly to the requested type. * * @see FIXUP_F2FP for the parameters * Side effect: declares also some _fp_helper functions */ #define FIXUP_F2T(suffix, minp, maxp, no1, type1, type2) \ FIXUP_F2FP(fp_##suffix, minp, maxp, no1, type1, type2) \ int fixup_##suffix (void** param, int param_no) \ { \ int ret; \ if ((ret=fixup_fp_##suffix (param, param_no))!=0) \ return ret; \ *param=((fparam_t*)*param)->fixed; \ return 0; \ } \ int fixup_free_##suffix (void** param, int param_no) \ { \ void* p; \ int ret; \ if (param && *param){ \ p=*param - (long)&((fparam_t*)0)->v; \ if ((ret=fixup_free_fp_##suffix(&p, param_no))==0) *param=p; \ return ret; \ } \ return 0; \ } /** macro for declaring a fixup and the corresponding free_fixup * for a function expecting first no1 params as fparamt_t and the * rest as direct type. * * @see FIXUP_F2FP for the parameters with the exception * that only the first no1 parameters are converted to * fparamt_t and the rest directly to the correponding type * * Side effect: declares also some _fpt_helper functions */ #define FIXUP_F2FP_T(suffix, minp, maxp, no1, type1, type2) \ FIXUP_F2FP(fpt_##suffix, minp, maxp, no1, type1, type2) \ int fixup_##suffix (void** param, int param_no) \ { \ int ret; \ if ((ret=fixup_fpt_##suffix(param, param_no))!=0) \ return ret; \ if (param_no>(no1)) *param=&((fparam_t*)*param)->v; \ return 0; \ } \ int fixup_free_##suffix (void** param, int param_no) \ { \ void* p; \ int ret; \ if (param && *param){ \ p=(param_no>(no1))? *param - (long)&((fparam_t*)0)->v : *param;\ if ((ret=fixup_free_fpt_##suffix(&p, param_no))==0) *param=p; \ return ret; \ } \ return 0; \ } /** macro for declaring a fixup which fixes all the paremeters to the same * type. * * @see FIXUP_F2T. */ #define FIXUP_F1T(suffix, minp, maxp, type) \ FIXUP_F2T(suffix, minp, maxp, maxp, type, 0) FIXUP_F1T(str_null, 1, 1, FPARAM_STR) FIXUP_F1T(str_str, 1, 2, FPARAM_STR) FIXUP_F1T(str_all, 1, 100, FPARAM_STR) /* no free fixups possible for unit_* (they overwrite the pointer with the converted number => the original value cannot be recovered) FIXUP_F1T(uint_null, 1, 1, FPARAM_INT) FIXUP_F1T(uint_uint, 1, 2, FPARAM_INT) */ int fixup_uint_uint(void** param, int param_no) { str s; unsigned int num; s.s = *param; s.len = strlen(s.s); if (likely(str2int(&s, &num) == 0)) { *param = (void*)(long)num; } else /* not a number */ return E_UNSPEC; return 0; } int fixup_uint_null(void** param, int param_no) { if (param_no == 1) return fixup_uint_uint(param, param_no); return E_UNSPEC; } /* fixup_regexp_null() has to be written "by hand", since it needs to save the original pointer (the fixup users expects a pointer to the regex in *param and hence the original value needed on free cannot be recovered directly). FIXUP_F1T(regexp_null, 1, 1, FPARAM_REGEX) */ struct regex_fixup { regex_t regex; /* compiled regex */ void* orig; /* original pointer */ }; int fixup_regexp_null(void** param, int param_no) { struct regex_fixup* re; if (param_no != 1) return E_UNSPEC; if ((re=pkg_malloc(sizeof(*re))) ==0) { ERR("No memory left\n"); goto error; } if (regcomp(&re->regex, *param, REG_EXTENDED|REG_ICASE|REG_NEWLINE)) goto error; re->orig = *param; *param = re; return 0; error: if (re) pkg_free(re); return E_UNSPEC; } int fixup_free_regexp_null(void** param, int param_no) { struct regex_fixup* re; if (param_no != 1) return E_UNSPEC; if (*param) { re = *param; *param = re->orig; regfree(&re->regex); pkg_free(re); } return 0; } /* fixup_pvar_*() has to be written "by hand", since it needs to save the original pointer (the fixup users expects a pointer to the pv_spec_t in *param and hence the original value needed on free cannot be recovered directly). FIXUP_F1T(pvar_null, 1, 1, FPARAM_PVS) FIXUP_F1T(pvar_pvar, 1, 2, FPARAM_PVS) */ struct pvs_fixup { pv_spec_t pvs; /* parsed pv spec */ void* orig; /* original pointer */ }; int fixup_pvar_all(void** param, int param_no) { struct pvs_fixup* pvs_f; str name; pvs_f = 0; name.s = *param; name.len = strlen(name.s); trim(&name); if (name.len == 0 || name.s[0] != '$') /* not a pvs id */ goto error; if ((pvs_f=pkg_malloc(sizeof(*pvs_f))) == 0) { ERR("No memory left\n"); goto error; } if (pv_parse_spec2(&name, &pvs_f->pvs, 1) == 0) /* not a valid pvs identifier */ goto error; pvs_f->orig = *param; *param = pvs_f; return 0; error: if (pvs_f) pkg_free(pvs_f); return E_UNSPEC; } int fixup_free_pvar_all(void** param, int param_no) { struct pvs_fixup* pvs_f; if (*param) { pvs_f = *param; *param = pvs_f->orig; /* free only the contents (don't attempt to free &pvs_f->pvs)*/ pv_spec_destroy(&pvs_f->pvs); /* free the whole pvs_fixup */ pkg_free(pvs_f); } return 0; } int fixup_pvar_pvar(void** param, int param_no) { if (param_no > 2) return E_UNSPEC; return fixup_pvar_all(param, param_no); } int fixup_free_pvar_pvar(void** param, int param_no) { if (param_no > 2) return E_UNSPEC; return fixup_free_pvar_all(param, param_no); } int fixup_pvar_null(void** param, int param_no) { if (param_no != 1) return E_UNSPEC; return fixup_pvar_all(param, param_no); } int fixup_free_pvar_null(void** param, int param_no) { if (param_no != 1) return E_UNSPEC; return fixup_free_pvar_all(param, param_no); } int fixup_pvar_none(void** param, int param_no) { if (param_no == 1) return fixup_pvar_all(param, param_no); return 0; } int fixup_free_pvar_none(void** param, int param_no) { if (param_no == 1) return fixup_free_pvar_all(param, param_no); return 0; } /* must be written "by hand", see above (fixup_pvar_pvar). FIXUP_F2T(pvar_str, 1, 2, 1, FPARAM_PVS, FPARAM_STR) FIXUP_F2T(pvar_str_str, 1, 3, 1, FPARAM_PVS, FPARAM_STR) */ int fixup_pvar_str(void** param, int param_no) { if (param_no == 1) return fixup_pvar_all(param, param_no); else if (param_no == 2) return fixup_str_str(param, param_no); return E_UNSPEC; } int fixup_free_pvar_str(void** param, int param_no) { if (param_no == 1) return fixup_free_pvar_all(param, param_no); else if (param_no == 2) return fixup_free_str_str(param, param_no); return E_UNSPEC; } int fixup_pvar_str_str(void** param, int param_no) { if (param_no == 1) return fixup_pvar_all(param, param_no); else if (param_no == 2 || param_no == 3) return fixup_str_all(param, param_no); return E_UNSPEC; } int fixup_free_pvar_str_str(void** param, int param_no) { if (param_no == 1) return fixup_free_pvar_all(param, param_no); else if (param_no == 2 || param_no == 3) return fixup_free_str_all(param, param_no); return E_UNSPEC; } int fixup_pvar_uint(void** param, int param_no) { if (param_no == 1) return fixup_pvar_all(param, param_no); else if (param_no == 2) return fixup_uint_uint(param, param_no); return E_UNSPEC; } int fixup_free_pvar_uint(void** param, int param_no) { if (param_no == 1) return fixup_free_pvar_all(param, param_no); return E_UNSPEC; } FIXUP_F2FP(igp_null, 1, 1, 1, FPARAM_INT|FPARAM_PVS, 0) FIXUP_F2FP(igp_igp, 1, 2, 2, FPARAM_INT|FPARAM_PVS, 0) /* must be declared by hand, because of the pvar special handling (see above) FIXUP_F2FP(igp_pvar, 1, 2, 1, FPARAM_INT|FPARAM_PVS, FPARAM_PVS) FIXUP_F2FP_T(igp_pvar_pvar, 1, 3, 1, FPARAM_INT|FPARAM_PVS, FPARAM_PVS) */ int fixup_igp_pvar(void** param, int param_no) { if (param_no == 1) return fixup_igp_null(param, param_no); else if (param_no == 2) return fixup_pvar_all(param, param_no); return E_UNSPEC; } int fixup_free_igp_pvar(void** param, int param_no) { if (param_no == 1) return fixup_free_igp_null(param, param_no); else if (param_no == 2) return fixup_free_pvar_all(param, param_no); return E_UNSPEC; } int fixup_igp_pvar_pvar(void** param, int param_no) { if (param_no == 1) return fixup_igp_null(param, param_no); else if (param_no == 2 || param_no == 3) return fixup_pvar_all(param, param_no); return E_UNSPEC; } int fixup_free_igp_pvar_pvar(void** param, int param_no) { if (param_no == 1) return fixup_free_igp_null(param, param_no); else if (param_no == 2 || param_no == 3) return fixup_free_pvar_all(param, param_no); return E_UNSPEC; } /** macro for declaring a spve fixup and the corresponding free_fixup * for a function expecting first no1 params as fparam converted spve * and the * rest as direct type. * * @see FIXUP_F2FP for the parameters with the exception * that the first no1 parameters are converted to fparam_t from spve * and the rest directly to the corresponding type * * Side effect: declares also some _spvet_helper functions */ #define FIXUP_F_SPVE_T(suffix, minp, maxp, no1, type2) \ FIXUP_F1T(spvet_##suffix, minp, maxp, type2) \ int fixup_##suffix (void** param, int param_no) \ { \ int ret; \ fparam_t* fp; \ if (param_no<=(no1)){ \ if ((ret=fix_param_types(FPARAM_PVE, param))<0){ \ ERR("Cannot convert function parameter %d to spve \n", \ param_no);\ return E_UNSPEC; \ } else{ \ fp=(fparam_t*)*param; \ if ((ret==0) && (fp->v.pve->spec==0 \ || fp->v.pve->spec->getf==0)){ \ fparam_free_restore(param); \ return fix_param_types(FPARAM_STR, param); \ } else if (ret==1) \ return fix_param_types(FPARAM_STR, param); \ return ret; \ } \ } else return fixup_spvet_##suffix(param, param_no); \ return 0; \ } \ int fixup_free_##suffix (void** param, int param_no) \ { \ if (param && *param){ \ if (param_no<=(no1)) \ fparam_free_restore(param); \ else \ return fixup_free_spvet_##suffix(param, param_no); \ } \ return 0; \ } /* format: name, minp, maxp, no_of_spve_params, type_for_rest_params */ FIXUP_F_SPVE_T(spve_spve, 1, 2, 2, 0) FIXUP_F_SPVE_T(spve_uint, 1, 2, 1, FPARAM_INT) FIXUP_F_SPVE_T(spve_str, 1, 2, 1, FPARAM_STR) FIXUP_F_SPVE_T(spve_null, 1, 1, 1, 0) /** get the corresp. fixup_free* function. * @param f -fixup function pointer. * @return - pointer to free_fixup function if known, 0 otherwise. */ free_fixup_function mod_fix_get_fixup_free(fixup_function f) { if (f == fixup_str_null) return fixup_free_str_null; if (f == fixup_str_str) return fixup_free_str_str; /* no free fixup for fixup_uint_* (they overwrite the pointer value with a number and the original value cannot be recovered) */ if (f == fixup_uint_null) return 0; if (f == fixup_uint_uint) return 0; if (f == fixup_regexp_null) return fixup_free_regexp_null; if (f == fixup_pvar_null) return fixup_free_pvar_null; if (f == fixup_pvar_pvar) return fixup_free_pvar_pvar; if (f == fixup_pvar_str) return fixup_free_pvar_str; if (f == fixup_pvar_str_str) return fixup_free_pvar_str_str; if (f == fixup_igp_igp) return fixup_free_igp_igp; if (f == fixup_igp_null) return fixup_free_igp_null; if (f == fixup_igp_pvar) return fixup_free_igp_pvar; if (f == fixup_igp_pvar_pvar) return fixup_free_igp_pvar_pvar; if (f == fixup_spve_spve) return fixup_free_spve_spve; if (f == fixup_spve_null) return fixup_free_spve_null; /* no free fixup, because of the uint part (the uint cannot be freed, see above fixup_uint_null) */ if (f == fixup_spve_uint) return 0; if (f == fixup_spve_str) return fixup_free_spve_str; return 0; } /** * */ int fixup_spve_all(void** param, int param_no) { return fixup_spve_null(param, 1); } /** * */ int fixup_igp_all(void** param, int param_no) { return fixup_igp_null(param, 1); } /** * */ int fixup_spve_igp(void** param, int param_no) { if(param_no==1) return fixup_spve_null(param, 1); if(param_no==2) return fixup_igp_null(param, 1); return E_UNSPEC; } /** * */ int fixup_free_spve_igp(void** param, int param_no) { if(param_no==1) return fixup_free_spve_null(param, 1); if(param_no==2) return fixup_free_igp_null(param, 1); return E_UNSPEC; } kamailio-4.0.4/Makefile.shared0000644000000000000000000000301712223032457014711 0ustar rootroot# # $Id$ # # shared functions # # # History: # -------- # 2008-06-27 initial version (andrei) # escape_values=$(subst $$,\$$,$(subst ",\", $(1))) # generates echo "$(1)=escape($(1))" cfg_gen_var=echo "$(1)=$(call escape_values,$($(1)))" # similar to cfg_gen_var, but for FOO=1 var=$(FOO) # it would generate "echo var=$(FOO)" and not "echo var=1" cfg_gen_var2=echo "$(1)=$(call escape_values,$(value $(1)))" # generate a var from itself concatenated with another one cfg_gen_var3=echo "$(1)=$(call escape_values,$(value $(1))) \$$($(2))" # generates echo "$(1)=escape($(1)) >> $(2)" cfg_save_var=$(call cfg_gen_var,$(1)) >>$(2); # same as above but uses cfg_gen_var2 cfg_save_var2=$(call cfg_gen_var2,$(1)) >>$(2); # concatenate another var to first var cfg_save_var3=$(call cfg_gen_var3,$(1),$(2)) >>$(3); # map function $(1) on variable list $($(2)) (the function is used with only # one arg) mapf1=$(foreach v,$($(2)),$(call $(1),$v)) # map function $(1) on variable list $($(2)), passing also $(3) as second # arg mapf2=$(foreach v,$($(2)),$(call $(1),$v,$(3))) #cfg_fixed_to_file=$(call mapf2,cfg_save_var,saved_fixed_vars,$(1)) #cfg_chg_to_file=$(call mapf2,cfg_save_var2,saved_chg_vars,$(1)) ifeq (${err_fail},1) sh_err_fail=set -e; try_err=$(1) ; if [ $$? != 0 ]; then echo ERROR: $(1) failed ; exit 1 ; fi else sh_err_fail= try_err=$(1) ; if [ $$? != 0 ]; then echo ERROR: $(1) failed ; fi endif DOCBOOK = $(shell which docbook2x-man 2>/dev/null) ifeq ($(DOCBOOK),) DOCBOOK = $(shell which db2x_docbook2man 2>/dev/null) endif kamailio-4.0.4/select.c0000644000000000000000000003017212223032460013423 0ustar rootroot/* * $Id$ * * Copyright (C) 2005-2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2005-12-19 select framework (mma) * 2006-01-19 multiple nested calls, IS_ALIAS -> NESTED flag renamed (mma) * DIVERSION flag checked * 2006-02-26 don't free str when changing type STR -> DIVERSION (mma) * it can't be freeable sometimes (e.g. xlog's select) * 2006-05-30 parse_select (mma) * 2006-06-02 shm_parse_select (mma) * */ /*! * \file * \brief SIP-router core :: * \ingroup core * Module: \ref core */ #include #include #include #include #include #include "select.h" #include "dprint.h" #include "select_core.h" #include "mem/mem.h" #include "mem/shm_mem.h" /* * The main parser table list placeholder * at startup use core table, modules can * add their own via register_select_table call */ static select_table_t *select_list = &select_core_table; /* the level of the select call that is beeing evaluated * by the child process */ int select_level = 0; /* pointer to the SIP uri beeing processed. * Nested function calls can pass information to each * other using this pointer. Only for performace reasons. * (Miklos) */ struct sip_uri *select_uri_p = NULL; /** parse a select identifier (internal version) * Parse select string into select structure s * moves pointer p to the first unused char. * * The select identifier must be of the form: \verbatim * [@] [ '.' ...] * * Where * = | * '[' ']' * = [a-zA-Z0-9_]+ * = | * = '"' '"' | * '\"' '\"' * * Examples: * @to.tag * @hf_value["contact"] * @msg.header["SER-Server-ID"] * @eval.pop[-1] * contact.uri.params.maddr * cfg_get.rtp_proxy.enabled \endverbatim * * @return -1 error * p points to the first unconsumed char * 0 success * p points to the first unconsumed char * s points to the select structure */ int w_parse_select(char**p, select_t* sel) { str name; char* select_name; if (**p=='@') (*p)++; select_name=*p; sel->n=0; while (isalpha((unsigned char)*(*p))) { if (sel->n > MAX_SELECT_PARAMS -2) { ERR("parse_select: select depth exceeds max\n"); goto error; } name.s=(*p); while (isalpha((unsigned char)*(*p)) || isdigit((unsigned char)*(*p)) || (*(*p)=='_')) (*p)++; name.len=(*p)-name.s; sel->params[sel->n].type=SEL_PARAM_STR; sel->params[sel->n].v.s=name; DBG("parse_select: part %d: %.*s\n", sel->n, sel->params[sel->n].v.s.len, sel->params[sel->n].v.s.s); sel->n++; if (*(*p)=='[') { (*p)++; if (*(*p)=='\\') (*p)++; if (*(*p)=='"') { (*p)++; name.s=(*p); while ((*(*p)!='\0') && (*(*p)!='"')) (*p)++; if (*(*p)!='"') { ERR("parse_select: end of string is missing\n"); goto error; } name.len=(*p)-name.s; if (*((*p)-1)=='\\') name.len--; (*p)++; if (*(*p)!=']') { ERR("parse_select: invalid string index, no closing ]\n"); goto error; }; (*p)++; sel->params[sel->n].type=SEL_PARAM_STR; sel->params[sel->n].v.s=name; DBG("parse_select: part %d: [\"%.*s\"]\n", sel->n, sel->params[sel->n].v.s.len, sel->params[sel->n].v.s.s); } else { name.s=(*p); if (*(*p)=='-') (*p)++; while (isdigit((unsigned char)*(*p))) (*p)++; name.len=(*p)-name.s; if (*(*p)!=']') { ERR("parse_select: invalid index, no closing ]\n"); goto error; }; (*p)++; sel->params[sel->n].type=SEL_PARAM_INT; sel->params[sel->n].v.i=atoi(name.s); DBG("parse_select: part %d: [%d]\n", sel->n, sel->params[sel->n].v.i); } sel->n++; } if (*(*p)!='.') break; (*p)++; }; if (sel->n==0) { ERR("parse_select: invalid select '%.*s'\n", (int)(*p - select_name), select_name); goto error; }; DBG("parse_select: end, total elements: %d, calling resolve_select\n", sel->n); if (resolve_select(sel)<0) { ERR("parse_select: error while resolve_select '%.*s'\n", (int)(*p - select_name), select_name); goto error; } return 0; error: return -1; } /** parse a select identifier. * Parse select string into select structure s and * moves pointer p to the first unused char. * \verbatim * The select identifier must be of the form: * [@] [ '.' ...] * * Where * = | * '[' ']' * = [a-zA-Z0-9_]+ * = | '-' | * = '"' '"' | * '\"' '\"' * * Examples: * @to.tag * @hf_value["contact"] * @msg.header["SER-Server-ID"] * @eval.pop[-1] * contact.uri.params.maddr * cfg_get.rtp_proxy.enabled \endverbatim * * @param p - double string (asciiz) pointer, *p is moved to the first char * after the select identifier * @param s - the result will be stored here * @return < 0 on error, 0 on success */ int parse_select (char** p, select_t** s) { select_t* sel; sel=(select_t*)pkg_malloc(sizeof(select_t)); if (!sel) { ERR("parse_select: no free memory\n"); return -1; } memset(sel, 0, sizeof(select_t)); if (w_parse_select(p, sel)<0) { pkg_free(sel); return -2; } *s=sel; return 0; } void free_select(select_t *s) { if (s) pkg_free(s); } void shm_free_select(select_t *s) { if (s) shm_free(s); } int shm_parse_select (char** p, select_t** s) { select_t* sel; sel=(select_t*)shm_malloc(sizeof(select_t)); if (!sel) { ERR("parse_select: no free shared memory\n"); return -1; } if (w_parse_select(p, sel)<0) { shm_free(sel); return -2; } *s=sel; return 0; } int resolve_select(select_t* s) { select_f f; int nested; int param_idx = 0; int table_idx = 0; select_table_t* t = NULL; int accept = 0; f = NULL; nested = 0; memset (s->f, 0, sizeof(s->f)); while (param_idxn) { accept = 0; switch (s->params[param_idx].type) { case SEL_PARAM_STR: DBG("resolve_select: '%.*s'\n", s->params[param_idx].v.s.len, s->params[param_idx].v.s.s); break; case SEL_PARAM_INT: DBG("resolve_select: [%d]\n", s->params[param_idx].v.i); break; default: /* just to avoid the warning */ break; } for (t=select_list; t; t=t->next) { table_idx = 0; if (!t->table) continue; while (t->table[table_idx].curr_f || t->table[table_idx].new_f) { if (t->table[table_idx].curr_f == f) { if ((t->table[table_idx].flags & (NESTED | CONSUME_NEXT_INT | CONSUME_NEXT_STR)) == NESTED) { accept = 1; } else if (t->table[table_idx].type == s->params[param_idx].type) { switch (t->table[table_idx].type) { case SEL_PARAM_INT: accept = 1; break; case SEL_PARAM_STR: accept = (((t->table[table_idx].name.len == s->params[param_idx].v.s.len) || !t->table[table_idx].name.len) && (!t->table[table_idx].name.s || !strncasecmp(t->table[table_idx].name.s, s->params[param_idx].v.s.s, s->params[param_idx].v.s.len))); break; default: break; } }; } if (accept) goto accepted; table_idx++; } } switch (s->params[param_idx].type) { case SEL_PARAM_STR: LOG(L_ERR, "Unable to resolve select '%.*s' at level %d\n", s->params[param_idx].v.s.len, s->params[param_idx].v.s.s, param_idx); break; case SEL_PARAM_INT: LOG(L_ERR, "Unable to resolve select [%d] at level %d\n", s->params[param_idx].v.i, param_idx); break; default: BUG ("Unable to resolve select at level %d\n", param_idx); break; break; } goto not_found; accepted: if (t->table[table_idx].flags & DIVERSION) { /* if (s->params[param_idx].type == SEL_PARAM_STR) pkg_free(s->params[param_idx].v.s.s); */ /* don't free it (the mem can leak only once at startup) * the parsed string can live inside larger string block * e.g. when xlog's select is parsed */ s->params[param_idx].type = SEL_PARAM_DIV; s->params[param_idx].v.i = t->table[table_idx].flags & DIVERSION_MASK; } if (t->table[table_idx].flags & CONSUME_NEXT_STR) { if ((param_idxn-1) && (s->params[param_idx+1].type == SEL_PARAM_STR)) { param_idx++; } else if (!(t->table[table_idx].flags & OPTIONAL)) { BUG ("Mandatory STR parameter not found\n"); goto not_found; } } if (t->table[table_idx].flags & CONSUME_NEXT_INT) { if ((param_idxn-1) && (s->params[param_idx+1].type == SEL_PARAM_INT)) { param_idx++; } else if (!(t->table[table_idx].flags & OPTIONAL)) { BUG ("Mandatory INT parameter not found\n"); goto not_found; } } if (t->table[table_idx].flags & NESTED) { if (nested < MAX_NESTED_CALLS-1) { /* need space for final function */ s->f[nested++] = f; s->param_offset[nested] = param_idx; } else { BUG("MAX_NESTED_CALLS too small to resolve select\n"); goto not_found; } } else { param_idx++; } if (t->table[table_idx].flags & FIXUP_CALL) { select_level = nested; s->param_offset[nested+1] = param_idx; if (t->table[table_idx].new_f(NULL, s, NULL)<0) goto not_found; } f = t->table[table_idx].new_f; if (t->table[table_idx].flags & CONSUME_ALL) { /* sanity checks */ if (t->table[table_idx].flags & NESTED) WARN("resolve_select: CONSUME_ALL should not be set " "together with NESTED flag!\n"); if ((t->table[table_idx].flags & FIXUP_CALL) == 0) WARN("resolve_select: FIXUP_CALL should be defined " "if CONSUME_ALL flag is set!\n"); break; } } if (t->table[table_idx].flags & SEL_PARAM_EXPECTED) { BUG ("final node has SEL_PARAM_EXPECTED set (no more parameters available)\n"); goto not_found; } if (nested >= MAX_NESTED_CALLS) { BUG("MAX_NESTED_CALLS too small, no space for finally resolved function\n"); goto not_found; } if ((nested>0) && (s->f[nested-1] == f)) { BUG("Topmost nested function equals to final function, won't call it twice\n"); } else { s->f[nested++] = f; } s->param_offset[nested] = s->n; return 0; not_found: return -1; } int run_select(str* res, select_t* s, struct sip_msg* msg) { int ret, orig_level; if (res == NULL) { BUG("Select unprepared result space\n"); return -1; } if (s == 0) { BUG("Select structure is NULL\n"); return -1; } if (s->f[0] == 0) { BUG("Select structure has not been resolved\n"); return -1; } DBG("Calling SELECT %p \n", s->f); /* reset the uri pointer */ select_uri_p = NULL; /* save and restore the original select_level * because of the nested selects */ orig_level = select_level; ret = 0; for ( select_level=0; (ret == 0) && (s->f[select_level] !=0 ) && (select_levelf[select_level](res, s, msg); } select_level = orig_level; return ret; } void print_select(select_t* s) { int i; DBG("select("); for(i = 0; i < s->n; i++) { if (s->params[i].type == SEL_PARAM_INT) { DBG("%d,", s->params[i].v.i); } else { DBG("%.*s,", s->params[i].v.s.len, s->params[i].v.s.s); } } DBG(")\n"); } int register_select_table(select_row_t* mod_tab) { select_table_t* t; t=(select_table_t*)pkg_malloc(sizeof(select_table_t)); if (!t) { ERR("No memory for new select_table structure\n"); return -1; } t->table=mod_tab; t->next=select_list; select_list=t; return 0; } kamailio-4.0.4/main.c0000644000000000000000000021577112223032461013103 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of SIP-router, a free SIP server. * * SIP-router is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * SIP-router is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * ------- * 2002-01-29 argc/argv globalized via my_{argc|argv} (jiri) * 2003-01-23 mhomed added (jiri) * 2003-03-19 replaced all malloc/frees w/ pkg_malloc/pkg_free (andrei) * 2003-03-29 pkg cleaners for fifo and script callbacks introduced (jiri) * 2003-03-31 removed snmp part (obsolete & no place in core) (andrei) * 2003-04-06 child_init called in all processes (janakj) * 2003-04-08 init_mallocs split into init_{pkg,shm}_mallocs and * init_shm_mallocs called after cmd. line parsing (andrei) * 2003-04-15 added tcp_disable support (andrei) * 2003-05-09 closelog() before openlog to force opening a new fd * (needed on solaris) (andrei) * 2003-06-11 moved all signal handlers init. in install_sigs and moved it * after daemonize (so that we won't catch anymore our own * SIGCHLD generated when becoming session leader) (andrei) * changed is_main default value to 1 (andrei) * 2003-06-28 kill_all_children is now used instead of kill(0, sig) * see comment above it for explanations. (andrei) * 2003-06-29 replaced port_no_str snprintf w/ int2str (andrei) * 2003-10-10 added switch for config check (-c) (andrei) * 2003-10-24 converted to the new socket_info lists (andrei) * 2004-03-30 core dump is enabled by default * added support for increasing the open files limit (andrei) * 2004-04-28 sock_{user,group,uid,gid,mode} added * user2uid() & user2gid() added (andrei) * 2004-09-11 added timeout on children shutdown and final cleanup * (if it takes more than 60s => something is definitely wrong * => kill all or abort) (andrei) * force a shm_unlock before cleaning-up, in case we have a * crashed childvwhich still holds the lock (andrei) * 2004-12-02 removed -p, extended -l to support [proto:]address[:port], * added parse_phostport, parse_proto (andrei) * 2005-06-16 always record the pid in pt[process_no].pid twice: once in the * parent & once in the child to avoid a short window when one * of them might use it "unset" (andrei) * 2005-07-25 use sigaction for setting the signal handlers (andrei) * 2006-07-13 added dns cache/failover init. (andrei) * 2006-10-13 added global variables stun_refresh_interval, stun_allow_stun * and stun_allow_fp (vlada) * 2006-10-25 don't log messages from signal handlers if NO_SIG_DEBUG is * defined; improved exit kill timeout (andrei) * init_childs(PROC_MAIN) before starting tcp_main, to allow * tcp usage for module started processes (andrei) * 2007-01-18 children shutdown procedure moved into shutdown_children; * safer shutdown on start-up error (andrei) * 2007-02-09 TLS support split into tls-in-core (CORE_TLS) and generic TLS * (USE_TLS) (andrei) * 2007-06-07 added support for locking pages in mem. and using real time * scheduling policies (andrei) * 2007-07-30 dst blacklist and DNS cache measurements added (Gergo) * 2008-08-08 sctp support (andrei) * 2008-08-19 -l support for mmultihomed addresses/addresses lists * (e.g. -l (eth0, 1.2.3.4, foo.bar) ) (andrei) * 2010-04-19 added daemon_status_fd pipe to communicate the parent process * with the main process in daemonize mode, so the parent process * can return the proper exit status code (ibc) * 2010-08-19 moved the daemon status stuff to daemonize.c (andrei) */ /** main file (init, daemonize, startup) * @file main.c * @ingroup core * Module: core */ /*! @defgroup core SIP-router core * * sip router core part. */ #include #include #include #include #include #include #include #include #include #if defined(HAVE_NETINET_IN_SYSTM) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SOCKIO_H #include #endif #include "config.h" #include "dprint.h" #include "daemonize.h" #include "route.h" #include "udp_server.h" #include "globals.h" #include "mem/mem.h" #ifdef SHM_MEM #include "mem/shm_mem.h" #include "shm_init.h" #endif /* SHM_MEM */ #include "sr_module.h" #include "timer.h" #include "parser/msg_parser.h" #include "ip_addr.h" #include "resolve.h" #include "parser/parse_hname2.h" #include "parser/digest/digest_parser.h" #include "name_alias.h" #include "hash_func.h" #include "pt.h" #include "script_cb.h" #include "nonsip_hooks.h" #include "ut.h" #include "signals.h" #ifdef USE_RAW_SOCKS #include "raw_sock.h" #endif /* USE_RAW_SOCKS */ #ifdef USE_TCP #include "poll_types.h" #include "tcp_init.h" #include "tcp_options.h" #ifdef CORE_TLS #include "tls/tls_init.h" #define tls_has_init_si() 1 #define tls_loaded() 1 #else #include "tls_hooks_init.h" #endif /* CORE_TLS */ #endif /* USE_TCP */ #ifdef USE_SCTP #include "sctp_options.h" #include "sctp_server.h" #endif #include "usr_avp.h" #include "rpc_lookup.h" #include "core_cmd.h" #include "flags.h" #include "lock_ops_init.h" #include "atomic_ops_init.h" #ifdef USE_DNS_CACHE #include "dns_cache.h" #endif #ifdef USE_DST_BLACKLIST #include "dst_blacklist.h" #endif #include "rand/fastrand.h" /* seed */ #include "stats.h" #include "counters.h" #include "cfg/cfg.h" #include "cfg/cfg_struct.h" #include "cfg_core.h" #include "endianness.h" /* init */ #include "basex.h" /* init */ #include "pvapi.h" /* init PV api */ #include "pv_core.h" /* register core pvars */ #include "ppcfg.h" #include "sock_ut.h" #ifdef DEBUG_DMALLOC #include #endif #include "ver.h" /* define SIG_DEBUG by default */ #ifdef NO_SIG_DEBUG #undef SIG_DEBUG #else #define SIG_DEBUG #endif static char help_msg[]= "\ Usage: " NAME " [options]\n\ Options:\n\ -f file Configuration file (default: " CFG_FILE ")\n\ -L path Modules search path (default: " MODS_DIR ")\n\ -c Check configuration file for errors\n\ -l address Listen on the specified address/interface (multiple -l\n\ mean listening on more addresses). The address format is\n\ [proto:]addr_lst[:port], where proto=udp|tcp|tls|sctp, \n\ addr_lst= addr|(addr, addr_lst) and \n\ addr= host|ip_address|interface_name. \n\ E.g: -l locahost, -l udp:127.0.0.1:5080, -l eth0:5062,\n\ -l \"sctp:(eth0)\", -l \"(eth0, eth1, 127.0.0.1):5065\".\n\ The default behaviour is to listen on all the interfaces.\n\ -n processes Number of child processes to fork per interface\n\ (default: 8)\n\ -r Use dns to check if is necessary to add a \"received=\"\n\ field to a via\n\ -R Same as `-r` but use reverse dns;\n\ (to use both use `-rR`)\n\ -K Turn on \"via:\" host checking when forwarding replies\n\ -d Debugging mode (multiple -d increase the level)\n\ -D no 1..do not fork (almost) anyway, 2..do not daemonize creator\n\ 3..daemonize (default)\n\ -E Log to stderr\n\ -e Log messages printed in terminal colors (requires -E)\n" #ifdef USE_TCP " -T Disable tcp\n\ -N Number of tcp child processes (default: equal to `-n')\n\ -W type poll method (depending on support in OS, it can be: poll,\n\ epoll_lt, epoll_et, sigio_rt, select, kqueue, /dev/poll)\n" #endif #ifdef USE_SCTP " -S disable sctp\n\ -Q Number of sctp child processes (default: equal to `-n')\n" #endif /* USE_SCTP */ " -V Version number\n\ -h This help message\n\ -b nr Maximum receive buffer size which will not be exceeded by\n\ auto-probing procedure even if OS allows\n\ -m nr Size of shared memory allocated in Megabytes\n\ -M nr Size of private memory allocated, in Megabytes\n\ -w dir Change the working directory to \"dir\" (default: \"/\")\n\ -t dir Chroot to \"dir\"\n\ -u uid Change uid \n\ -g gid Change gid \n\ -P file Create a pid file\n\ -G file Create a pgid file\n\ -O nr Script optimization level (debugging option)\n\ -a mode Auto aliases mode: enable with yes or on,\n\ disable with no or off\n\ -A define Add config pre-processor define (e.g., -A WITH_AUTH)\n" #ifdef STATS " -s file File to which statistics is dumped (disabled otherwise)\n" #endif ; /* print compile-time constants */ void print_ct_constants(void) { #ifdef ADAPTIVE_WAIT printf("ADAPTIVE_WAIT_LOOPS=%d, ", ADAPTIVE_WAIT_LOOPS); #endif /* #ifdef SHM_MEM printf("SHM_MEM_SIZE=%d, ", SHM_MEM_SIZE); #endif */ printf("MAX_RECV_BUFFER_SIZE %d, MAX_LISTEN %d," " MAX_URI_SIZE %d, BUF_SIZE %d, DEFAULT PKG_SIZE %uMB\n", MAX_RECV_BUFFER_SIZE, MAX_LISTEN, MAX_URI_SIZE, BUF_SIZE, PKG_MEM_SIZE); #ifdef USE_TCP printf("poll method support: %s.\n", poll_support); #endif } /* print compile-time constants */ void print_internals(void) { printf("Print out of %s internals\n", NAME); printf(" Version: %s\n", full_version); printf(" Default config: %s\n", CFG_FILE); printf(" Default paths to modules: %s\n", MODS_DIR); printf(" Compile flags: %s\n", ver_flags ); printf(" MAX_RECV_BUFFER_SIZE=%d\n", MAX_RECV_BUFFER_SIZE); printf(" MAX_LISTEN=%d\n", MAX_LISTEN); printf(" MAX_URI_SIZE=%d\n", MAX_URI_SIZE); printf(" BUF_SIZE=%d\n", BUF_SIZE); printf(" DEFAULT PKG_SIZE=%uMB\n", PKG_MEM_SIZE); #ifdef SHM_MEM printf(" DEFAULT SHM_SIZE=%uMB\n", SHM_MEM_SIZE); #endif #ifdef ADAPTIVE_WAIT printf(" ADAPTIVE_WAIT_LOOPS=%d\n", ADAPTIVE_WAIT_LOOPS); #endif #ifdef USE_TCP printf(" TCP poll methods: %s\n", poll_support); #endif printf(" Source code revision ID: %s\n", ver_id); printf(" Compiled with: %s\n", ver_compiler); printf(" Compiled on: %s\n", ver_compiled_time); printf("Thank you for flying %s!\n", NAME); } /* debugging function */ /* void receive_stdin_loop(void) { #define BSIZE 1024 char buf[BSIZE+1]; int len; while(1){ len=fread(buf,1,BSIZE,stdin); buf[len+1]=0; receive_msg(buf, len); printf("-------------------------\n"); } } */ /* global vars */ int own_pgid = 0; /* whether or not we have our own pgid (and it's ok to use kill(0, sig) */ char* mods_dir = MODS_DIR; /* search path for dyn. loadable modules */ char* cfg_file = 0; unsigned int maxbuffer = MAX_RECV_BUFFER_SIZE; /* maximum buffer size we do not want to exceed during the auto-probing procedure; may be re-configured */ unsigned int sql_buffer_size = 65535; /* Size for the SQL buffer. Defaults to 64k. This may be re-configured */ int socket_workers = 0; /* number of workers processing requests for a socket - it's reset everytime with a new listen socket */ int children_no = 0; /* number of children processing requests */ #ifdef USE_TCP int tcp_cfg_children_no = 0; /* set via config or command line option */ int tcp_children_no = 0; /* based on socket_workers and tcp_cfg_children_no */ int tcp_disable = 0; /* 1 if tcp is disabled */ #endif #ifdef USE_TLS #ifdef CORE_TLS int tls_disable = 0; /* tls enabled by default */ #else int tls_disable = 1; /* tls disabled by default */ #endif /* CORE_TLS */ #endif /* USE_TLS */ #ifdef USE_SCTP int sctp_children_no = 0; int sctp_disable = 2; /* 1 if sctp is disabled, 2 if auto mode, 0 enabled */ #endif /* USE_SCTP */ struct process_table *pt=0; /*array with children pids, 0= main proc, alloc'ed in shared mem if possible*/ int *process_count = 0; /* Total number of SER processes currently running */ gen_lock_t* process_lock; /* lock on the process table */ int process_no = 0; /* index of process in the pt */ time_t up_since; int sig_flag = 0; /* last signal received */ int dont_fork = 0; int dont_daemonize = 0; int log_stderr = 0; int log_color = 0; /* set custom app name for syslog printing */ char *log_name = 0; pid_t creator_pid = (pid_t) -1; int config_check = 0; /* check if reply first via host==us */ int check_via = 0; /* translate user=phone URIs to TEL URIs */ int phone2tel = 1; /* shall use stateful synonym branches? faster but not reboot-safe */ int syn_branch = 1; /* debugging level for timer debugging */ int timerlog = L_WARN; /* should replies include extensive warnings? by default no, good for trouble-shooting */ int sip_warning = 0; /* should localy-generated messages include server's signature? be default yes, good for trouble-shooting */ int server_signature=1; str server_hdr = {SERVER_HDR, SERVER_HDR_LEN}; str user_agent_hdr = {USER_AGENT, USER_AGENT_LEN}; str version_table = {VERSION_TABLE, VERSION_TABLE_LEN}; /* should ser try to locate outbound interface on multihomed * host? by default not -- too expensive */ int mhomed=0; /* use dns and/or rdns or to see if we need to add a ;received=x.x.x.x to via: */ int received_dns = 0; /* add or not the rev dns names to aliases list */ int sr_auto_aliases=1; char* working_dir = 0; char* chroot_dir = 0; char* user=0; char* group=0; int uid = 0; int gid = 0; char* sock_user=0; char* sock_group=0; int sock_uid= -1; int sock_gid= -1; int sock_mode= S_IRUSR| S_IWUSR| S_IRGRP| S_IWGRP; /* rw-rw---- */ int server_id = 0; /* Configurable unique ID of the server */ /* set timeval for each received sip message */ int sr_msg_time = 1; /* more config stuff */ int disable_core_dump=0; /* by default enabled */ int open_files_limit=-1; /* don't touch it by default */ /* memory options */ int shm_force_alloc=0; /* force immediate (on startup) page allocation (by writting 0 in the pages), useful if mlock_pages is also 1 */ int mlock_pages=0; /* default off, try to disable swapping */ /* real time options */ int real_time=0; /* default off, flags: 1 on only timer, 2 slow timer, 4 all procs (7=all) */ int rt_prio=0; int rt_policy=0; /* SCHED_OTHER */ int rt_timer1_prio=0; /* "fast" timer */ int rt_timer2_prio=0; /* "slow" timer */ int rt_timer1_policy=0; /* "fast" timer, SCHED_OTHER */ int rt_timer2_policy=0; /* "slow" timer, SCHED_OTHER */ /* a hint to reply modules whether they should send reply to IP advertised in Via or IP from which a request came */ int reply_to_via=0; #ifdef USE_MCAST int mcast_loopback = 0; int mcast_ttl = -1; /* if -1, don't touch it, use the default (usually 1) */ #endif /* USE_MCAST */ int tos = IPTOS_LOWDELAY; int pmtu_discovery = 0; #ifdef USE_IPV6 int auto_bind_ipv6 = 0; #endif #if 0 char* names[MAX_LISTEN]; /* our names */ int names_len[MAX_LISTEN]; /* lengths of the names*/ struct ip_addr addresses[MAX_LISTEN]; /* our ips */ int addresses_no=0; /* number of names/ips */ #endif struct socket_info* udp_listen=0; #ifdef USE_TCP int tcp_main_pid=0; /* set after the tcp main process is started */ struct socket_info* tcp_listen=0; #endif #ifdef USE_TLS struct socket_info* tls_listen=0; #endif #ifdef USE_SCTP struct socket_info* sctp_listen=0; #endif struct socket_info* bind_address=0; /* pointer to the crt. proc. listening address*/ struct socket_info* sendipv4; /* ipv4 socket to use when msg. comes from ipv6*/ struct socket_info* sendipv6; /* same as above for ipv6 */ #ifdef USE_RAW_SOCKS int raw_udp4_send_sock = -1; /* raw socket used for sending udp4 packets */ #endif /* USE_RAW_SOCKS */ #ifdef USE_TCP struct socket_info* sendipv4_tcp; struct socket_info* sendipv6_tcp; #endif #ifdef USE_TLS struct socket_info* sendipv4_tls; struct socket_info* sendipv6_tls; #endif #ifdef USE_SCTP struct socket_info* sendipv4_sctp; struct socket_info* sendipv6_sctp; #endif unsigned short port_no=0; /* default port*/ #ifdef USE_TLS unsigned short tls_port_no=0; /* default port */ #endif #ifdef USE_STUN /* refresh interval in miliseconds */ unsigned int stun_refresh_interval=0; /* stun can be switch off even if it is compiled */ int stun_allow_stun=1; /* use or don't use fingerprint */ int stun_allow_fp=1; #endif struct host_alias* aliases=0; /* name aliases list */ /* Parameter to child_init */ int child_rank = 0; /* how much to wait for children to terminate, before taking extreme measures*/ int ser_kill_timeout=DEFAULT_SER_KILL_TIMEOUT; /* process_bm_t process_bit = 0; */ #ifdef ROUTE_SRV #endif /* cfg parsing */ int cfg_errors=0; int cfg_warnings=0; /* shared memory (in MB) */ unsigned long shm_mem_size=0; /* private (pkg) memory (in MB) */ unsigned long pkg_mem_size=0; /* export command-line to anywhere else */ int my_argc; char **my_argv; /* set to 1 when the cfg framework and core cfg is initialized/registered */ static int cfg_ok=0; #define MAX_FD 32 /* maximum number of inherited open file descriptors, (normally it shouldn't be bigger than 3) */ extern FILE* yyin; extern int yyparse(void); int is_main=1; /* flag = is this the "main" process? */ int fixup_complete=0; /* flag = is the fixup complete ? */ char* pid_file = 0; /* filename as asked by use */ char* pgid_file = 0; /* call it before exiting; if show_status==1, mem status is displayed */ void cleanup(show_status) { int memlog; /*clean-up*/ #ifndef SHM_SAFE_MALLOC if (mem_lock) shm_unlock(); /* hack: force-unlock the shared memory lock in case some process crashed and let it locked; this will allow an almost gracious shutdown */ #endif destroy_rpcs(); destroy_modules(); #ifdef USE_DNS_CACHE destroy_dns_cache(); #endif #ifdef USE_DST_BLACKLIST destroy_dst_blacklist(); #endif /* restore the original core configuration before the * config block is freed, otherwise even logging is unusable, * it can case segfault */ if (cfg_ok){ cfg_update(); /* copy current config into default_core_cfg */ if (core_cfg) default_core_cfg=*((struct cfg_group_core*)core_cfg); } core_cfg = &default_core_cfg; cfg_destroy(); #ifdef USE_TCP destroy_tcp(); #ifdef USE_TLS destroy_tls(); #endif /* USE_TLS */ #endif /* USE_TCP */ #ifdef USE_SCTP destroy_sctp(); #endif destroy_timer(); pv_destroy_api(); destroy_script_cb(); destroy_nonsip_hooks(); destroy_routes(); destroy_atomic_ops(); destroy_counters(); memlog=cfg_get(core, core_cfg, memlog); #ifdef PKG_MALLOC if (show_status && memlog <= cfg_get(core, core_cfg, debug)){ if (cfg_get(core, core_cfg, mem_summary) & 1) { LOG(memlog, "Memory status (pkg):\n"); pkg_status(); } if (cfg_get(core, core_cfg, mem_summary) & 4) { LOG(memlog, "Memory still-in-use summary (pkg):\n"); pkg_sums(); } } #endif #ifdef SHM_MEM if (pt) shm_free(pt); pt=0; if (show_status && memlog <= cfg_get(core, core_cfg, debug)){ if (cfg_get(core, core_cfg, mem_summary) & 2) { LOG(memlog, "Memory status (shm):\n"); shm_status(); } if (cfg_get(core, core_cfg, mem_summary) & 8) { LOG(memlog, "Memory still-in-use summary (shm):\n"); shm_sums(); } } /* zero all shmem alloc vars that we still use */ shm_mem_destroy(); #endif destroy_lock_ops(); if (pid_file) unlink(pid_file); if (pgid_file) unlink(pgid_file); destroy_pkg_mallocs(); } /* tries to send a signal to all our processes * if daemonized is ok to send the signal to all the process group, * however if not daemonized we might end up sending the signal also * to the shell which launched us => most signals will kill it if * it's not in interactive mode and we don't want this. The non-daemonized * case can occur when an error is encountered before daemonize is called * (e.g. when parsing the config file) or when ser is started in "dont-fork" * mode. Sending the signal to all the processes in pt[] will not work * for processes forked from modules (which have no correspondent entry in * pt), but this can happen only in dont_fork mode (which is only for * debugging). So in the worst case + "dont-fork" we might leave some * zombies. -- andrei */ static void kill_all_children(int signum) { int r; if (own_pgid) kill(0, signum); else if (pt){ /* lock processes table only if this is a child process * (only main can add processes, so from main is safe not to lock * and moreover it avoids the lock-holding suicidal children problem) */ if (!is_main) lock_get(process_lock); for (r=1; r<*process_count; r++){ if (r==process_no) continue; /* try not to be suicidal */ if (pt[r].pid) { kill(pt[r].pid, signum); } else LOG(L_CRIT, "BUG: killing: %s > %d no pid!!!\n", pt[r].desc, pt[r].pid); } if (!is_main) lock_release(process_lock); } } /* if this handler is called, a critical timeout has occurred while * waiting for the children to finish => we should kill everything and exit */ static void sig_alarm_kill(int signo) { kill_all_children(SIGKILL); /* this will kill the whole group including "this" process; for debugging replace with SIGABRT (but warning: it might generate lots of cores) */ } /* like sig_alarm_kill, but the timeout has occurred when cleaning up * => try to leave a core for future diagnostics */ static void sig_alarm_abort(int signo) { /* LOG is not signal safe, but who cares, we are abort-ing anyway :-) */ LOG(L_CRIT, "BUG: shutdown timeout triggered, dying..."); abort(); } static void shutdown_children(int sig, int show_status) { kill_all_children(sig); if (set_sig_h(SIGALRM, sig_alarm_kill) == SIG_ERR ) { LOG(L_ERR, "ERROR: shutdown: could not install SIGALARM handler\n"); /* continue, the process will die anyway if no * alarm is installed which is exactly what we want */ } alarm(ser_kill_timeout); while((wait(0) > 0) || (errno==EINTR)); /* wait for all the children to terminate*/ set_sig_h(SIGALRM, sig_alarm_abort); cleanup(show_status); /* cleanup & show status*/ alarm(0); set_sig_h(SIGALRM, SIG_IGN); } void handle_sigs(void) { pid_t chld; int chld_status; int memlog; switch(sig_flag){ case 0: break; /* do nothing*/ case SIGPIPE: /* SIGPIPE might be rarely received on use of exec module; simply ignore it */ LOG(L_WARN, "WARNING: SIGPIPE received and ignored\n"); break; case SIGINT: case SIGTERM: /* we end the program in all these cases */ if (sig_flag==SIGINT) DBG("INT received, program terminates\n"); else DBG("SIGTERM received, program terminates\n"); LOG(L_NOTICE, "Thank you for flying " NAME "!!!\n"); /* shutdown/kill all the children */ shutdown_children(SIGTERM, 1); exit(0); break; case SIGUSR1: #ifdef STATS dump_all_statistic(); #endif memlog=cfg_get(core, core_cfg, memlog); #ifdef PKG_MALLOC if (memlog <= cfg_get(core, core_cfg, debug)){ if (cfg_get(core, core_cfg, mem_summary) & 1) { LOG(memlog, "Memory status (pkg):\n"); pkg_status(); } if (cfg_get(core, core_cfg, mem_summary) & 2) { LOG(memlog, "Memory still-in-use summary (pkg):\n"); pkg_sums(); } } #endif #ifdef SHM_MEM if (memlog <= cfg_get(core, core_cfg, debug)){ if (cfg_get(core, core_cfg, mem_summary) & 1) { LOG(memlog, "Memory status (shm):\n"); shm_status(); } if (cfg_get(core, core_cfg, mem_summary) & 2) { LOG(memlog, "Memory still-in-use summary (shm):\n"); shm_sums(); } } #endif break; case SIGCHLD: while ((chld=waitpid( -1, &chld_status, WNOHANG ))>0) { if (WIFEXITED(chld_status)) LOG(L_ALERT, "child process %ld exited normally," " status=%d\n", (long)chld, WEXITSTATUS(chld_status)); else if (WIFSIGNALED(chld_status)) { LOG(L_ALERT, "child process %ld exited by a signal" " %d\n", (long)chld, WTERMSIG(chld_status)); #ifdef WCOREDUMP LOG(L_ALERT, "core was %sgenerated\n", WCOREDUMP(chld_status) ? "" : "not " ); #endif }else if (WIFSTOPPED(chld_status)) LOG(L_ALERT, "child process %ld stopped by a" " signal %d\n", (long)chld, WSTOPSIG(chld_status)); } #ifndef STOP_JIRIS_CHANGES if (dont_fork) { LOG(L_INFO, "INFO: dont_fork turned on, living on\n"); break; } LOG(L_INFO, "INFO: terminating due to SIGCHLD\n"); #endif /* exit */ shutdown_children(SIGTERM, 1); DBG("terminating due to SIGCHLD\n"); exit(0); break; case SIGHUP: /* ignoring it*/ DBG("SIGHUP received, ignoring it\n"); break; default: LOG(L_CRIT, "WARNING: unhandled signal %d\n", sig_flag); } sig_flag=0; } /* added by jku; allows for regular exit on a specific signal; good for profiling which only works if exited regularly and not by default signal handlers - modified by andrei: moved most of the stuff to handle_sigs, made it safer for the "fork" case */ void sig_usr(int signo) { #ifdef PKG_MALLOC int memlog; #endif if (is_main){ if (sig_flag==0) sig_flag=signo; else /* previous sig. not processed yet, ignoring? */ return; ; if (dont_fork) /* only one proc, doing everything from the sig handler, unsafe, but this is only for debugging mode*/ handle_sigs(); }else{ /* process the important signals */ switch(signo){ case SIGPIPE: #ifdef SIG_DEBUG /* signal unsafe stuff follows */ LOG(L_INFO, "INFO: signal %d received\n", signo); #endif break; case SIGINT: case SIGTERM: #ifdef SIG_DEBUG /* signal unsafe stuff follows */ LOG(L_INFO, "INFO: signal %d received\n", signo); /* print memory stats for non-main too */ #ifdef PKG_MALLOC /* make sure we have current cfg values, but update only the safe part (values not requiring callbacks), to account for processes that might not have registered config support */ cfg_update_no_cbs(); memlog=cfg_get(core, core_cfg, memlog); if (memlog <= cfg_get(core, core_cfg, debug)){ if (cfg_get(core, core_cfg, mem_summary) & 1) { LOG(memlog, "Memory status (pkg):\n"); pkg_status(); } if (cfg_get(core, core_cfg, mem_summary) & 2) { LOG(memlog, "Memory still-in-use summary (pkg):" "\n"); pkg_sums(); } } #endif #endif _exit(0); break; case SIGUSR1: /* statistics, do nothing, printed only from the main proc */ break; /* ignored*/ case SIGUSR2: case SIGHUP: break; case SIGCHLD: #ifndef STOP_JIRIS_CHANGES #ifdef SIG_DEBUG /* signal unsafe stuff follows */ DBG("SIGCHLD received: " "we do not worry about grand-children\n"); #endif #else _exit(0); /* terminate if one child died */ #endif break; } } } /* install the signal handlers, returns 0 on success, -1 on error */ int install_sigs(void) { /* added by jku: add exit handler */ if (set_sig_h(SIGINT, sig_usr) == SIG_ERR ) { ERR("no SIGINT signal handler can be installed\n"); goto error; } /* if we debug and write to a pipe, we want to exit nicely too */ if (set_sig_h(SIGPIPE, sig_usr) == SIG_ERR ) { ERR("no SIGINT signal handler can be installed\n"); goto error; } if (set_sig_h(SIGUSR1, sig_usr) == SIG_ERR ) { ERR("no SIGUSR1 signal handler can be installed\n"); goto error; } if (set_sig_h(SIGCHLD , sig_usr) == SIG_ERR ) { ERR("no SIGCHLD signal handler can be installed\n"); goto error; } if (set_sig_h(SIGTERM , sig_usr) == SIG_ERR ) { ERR("no SIGTERM signal handler can be installed\n"); goto error; } if (set_sig_h(SIGHUP , sig_usr) == SIG_ERR ) { ERR("no SIGHUP signal handler can be installed\n"); goto error; } if (set_sig_h(SIGUSR2 , sig_usr) == SIG_ERR ) { ERR("no SIGUSR2 signal handler can be installed\n"); goto error; } return 0; error: return -1; } /* returns -1 on error, 0 on success * sets proto */ static int parse_proto(unsigned char* s, long len, int* proto) { #define PROTO2UINT3(a, b, c) (( (((unsigned int)(a))<<16)+ \ (((unsigned int)(b))<<8)+ \ ((unsigned int)(c)) ) | 0x20202020) #define PROTO2UINT4(a, b ,c ,d) (( (((unsigned int)(a))<<24)+ \ (((unsigned int)(b))<<16)+ \ (((unsigned int)(c))<< 8)+ \ (((unsigned int)(d))) \ )| 0x20202020 ) unsigned int i; if (likely(len==3)){ i=PROTO2UINT3(s[0], s[1], s[2]); switch(i){ case PROTO2UINT3('u', 'd', 'p'): *proto=PROTO_UDP; break; #ifdef USE_TCP case PROTO2UINT3('t', 'c', 'p'): *proto=PROTO_TCP; break; #ifdef USE_TLS case PROTO2UINT3('t', 'l', 's'): *proto=PROTO_TLS; break; #endif #endif default: return -1; } } #ifdef USE_SCTP else if (likely(len==4)){ i=PROTO2UINT4(s[0], s[1], s[2], s[3]); if (i==PROTO2UINT4('s', 'c', 't', 'p')) *proto=PROTO_SCTP; else return -1; } #endif /* USE_SCTP */ else /* Deliberately leaving out PROTO_WS and PROTO_WSS as these are just upgraded TCP/TLS connections. */ return -1; return 0; } static struct name_lst* mk_name_lst_elem(char* name, int name_len, int flags) { struct name_lst* l; l=pkg_malloc(sizeof(struct name_lst)+name_len+1/* 0 */); if (l){ l->name=((char*)l)+sizeof(struct name_lst); memcpy(l->name, name, name_len); l->name[name_len]=0; l->flags=flags; l->next=0; } return l; } /* free a name_lst list with elements allocated with mk_name_lst_elem * (single block both for the structure and for the name) */ static void free_name_lst(struct name_lst* lst) { struct name_lst* l; while(lst){ l=lst; lst=lst->next; pkg_free(l); } } /* parse h and returns a name lst (flags are set to SI_IS_MHOMED if * h contains more then one name or contains a name surrounded by '(' ')' ) * valid formats: "hostname" * "(hostname, hostname1, hostname2)" * "(hostname hostname1 hostname2)" * "(hostname)" */ static struct name_lst* parse_name_lst(char* h, int h_len) { char* last; char* p; struct name_lst* n_lst; struct name_lst* l; struct name_lst** tail; int flags; n_lst=0; tail=&n_lst; last=h+h_len-1; flags=0; /* eat whitespace */ for(; h<=last && ((*h==' ') || (*h=='\t')); h++); for(; last>h && ((*last==' ') || (*last=='\t')); last--); /* catch empty strings and invalid lens */ if (h>last) goto error; if (*h=='('){ /* list mode */ if (*last!=')' || ((h+1)>(last-1))) goto error; h++; last--; flags=SI_IS_MHOMED; for(p=h; p<=last; p++) switch (*p){ case ',': case ';': case ' ': case '\t': if ((int)(p-h)>0){ l=mk_name_lst_elem(h, (int)(p-h), flags); if (l==0) goto error; *tail=l; tail=&l->next; } h=p+1; break; } }else{ /* single addr. mode */ flags=0; p=last+1; } if ((int)(p-h)>0){ l=mk_name_lst_elem(h, (int)(p-h), flags); if (l==0) goto error; *tail=l; tail=&l->next; } return n_lst; error: if (n_lst) free_name_lst(n_lst); return 0; } /* * parses [proto:]host[:port] or * [proto:](host_1, host_2, ... host_n)[:port] * where proto= udp|tcp|tls * returns fills proto, port, host and returns list of addresses on success * (pkg malloc'ed) and 0 on failure */ /** get protocol host and port from a string representation. * parses [proto:]host[:port] or * [proto:](host_1, host_2, ... host_n)[:port] * where proto= udp|tcp|tls|sctp * @param s - string (like above) * @param host - will be filled with the host part * Note: for multi-homing it wil contain all the addresses * (e.g.: "sctp:(1.2.3.4, 5.6.7.8)" => host="(1.2.3.4, 5.6.7.8)") * @param hlen - will be filled with the length of the host part. * @param port - will be filled with the port if present or 0 if it's not. * @param proto - will be filled with the protocol if present or PROTO_NONE * if it's not. * @return fills proto, port, host and returns 0 on success and -1 on failure. */ int parse_phostport(char* s, char** host, int* hlen, int* port, int* proto) { char* first; /* first ':' occurrence */ char* second; /* second ':' occurrence */ char* p; int bracket; char* tmp; first=second=0; bracket=0; /* find the first 2 ':', ignoring possible ipv6 addresses * (substrings between []) */ for(p=s; *p; p++){ switch(*p){ case '[': bracket++; if (bracket>1) goto error_brackets; break; case ']': bracket--; if (bracket<0) goto error_brackets; break; case ':': if (bracket==0){ if (first==0) first=p; else if( second==0) second=p; else goto error_colons; } break; } } if (p==s) return -1; if (*(p-1)==':') goto error_colons; if (first==0){ /* no ':' => only host */ *host=s; *hlen=(int)(p-s); *port=0; *proto=0; goto end; } if (second){ /* 2 ':' found => check if valid */ if (parse_proto((unsigned char*)s, first-s, proto)<0) goto error_proto; *port=strtol(second+1, &tmp, 10); if ((tmp==0)||(*tmp)||(tmp==second+1)) goto error_port; *host=first+1; *hlen=(int)(second-*host); goto end; } /* only 1 ':' found => it's either proto:host or host:port */ *port=strtol(first+1, &tmp, 10); if ((tmp==0)||(*tmp)||(tmp==first+1)){ /* invalid port => it's proto:host */ if (parse_proto((unsigned char*)s, first-s, proto)<0) goto error_proto; *port=0; *host=first+1; *hlen=(int)(p-*host); }else{ /* valid port => its host:port */ *proto=0; *host=s; *hlen=(int)(first-*host); } end: return 0; error_brackets: LOG(L_ERR, "ERROR: parse_phostport: too many brackets in %s\n", s); return -1; error_colons: LOG(L_ERR, "ERROR: parse_phostport: too many colons in %s\n", s); return -1; error_proto: LOG(L_ERR, "ERROR: parse_phostport: bad protocol in %s\n", s); return -1; error_port: LOG(L_ERR, "ERROR: parse_phostport: bad port number in %s\n", s); return -1; } /** get protocol host, port and MH addresses list from a string representation. * parses [proto:]host[:port] or * [proto:](host_1, host_2, ... host_n)[:port] * where proto= udp|tcp|tls|sctp * @param s - string (like above) * @param host - will be filled with the host part * Note: for multi-homing it wil contain all the addresses * (e.g.: "sctp:(1.2.3.4, 5.6.7.8)" => host="(1.2.3.4, 5.6.7.8)") * @param hlen - will be filled with the length of the host part. * @param port - will be filled with the port if present or 0 if it's not. * @param proto - will be filled with the protocol if present or PROTO_NONE * if it's not. * @return fills proto, port, host and returns list of addresses on success * (pkg malloc'ed) and 0 on failure */ static struct name_lst* parse_phostport_mh(char* s, char** host, int* hlen, int* port, int* proto) { if (parse_phostport(s, host, hlen, port, proto)==0) return parse_name_lst(*host, *hlen); return 0; } /** Update \c cfg_file variable to contain full pathname. The function updates * the value of \c cfg_file global variable to contain full absolute pathname * to the main configuration file of SER. The function uses CFG_FILE macro to * determine the default path to the configuration file if the user did not * specify one using the command line option. If \c cfg_file contains an * absolute pathname then it is used unmodified, if it contains a relative * pathanme than the value returned by \c getcwd function will be added at the * beginning. This function must be run before SER changes its current working * directory to / (in daemon mode). * @return Zero on success, negative number * on error. */ int fix_cfg_file(void) { char* res = NULL; size_t max_len, cwd_len, cfg_len; if (cfg_file == NULL) cfg_file = CFG_FILE; if (cfg_file[0] == '/') return 0; /* cfg_file contains a relative pathname, get the current * working directory and add it at the beginning */ cfg_len = strlen(cfg_file); max_len = pathmax(); if ((res = malloc(max_len)) == NULL) goto error; if (getcwd(res, max_len) == NULL) goto error; cwd_len = strlen(res); /* Make sure that the buffer is big enough */ if (cwd_len + 1 + cfg_len >= max_len) goto error; res[cwd_len] = '/'; memcpy(res + cwd_len + 1, cfg_file, cfg_len); res[cwd_len + 1 + cfg_len] = '\0'; /* Add terminating zero */ cfg_file = res; return 0; error: fprintf(stderr, "ERROR: Unable to fix cfg_file to contain full pathname\n"); if (res) free(res); return -1; } /* main loop */ int main_loop(void) { int i; pid_t pid; struct socket_info* si; char si_desc[MAX_PT_DESC]; #ifdef EXTRA_DEBUG int r; #endif int nrprocs; /* one "main" process and n children handling i/o */ if (dont_fork){ #ifdef STATS setstats( 0 ); #endif if (udp_listen==0){ LOG(L_ERR, "ERROR: no fork mode requires at least one" " udp listen address, exiting...\n"); goto error; } /* only one address, we ignore all the others */ if (udp_init(udp_listen)==-1) goto error; bind_address=udp_listen; if (bind_address->address.af==AF_INET) { sendipv4=bind_address; #ifdef USE_RAW_SOCKS /* always try to have a raw socket opened if we are using ipv4 */ raw_udp4_send_sock = raw_socket(IPPROTO_RAW, 0, 0, 1); if (raw_udp4_send_sock < 0) { if ( default_core_cfg.udp4_raw > 0) { /* force use raw socket failed */ ERR("could not initialize raw udp send socket (ipv4):" " %s (%d)\n", strerror(errno), errno); if (errno == EPERM) ERR("could not initialize raw socket on startup" " due to inadequate permissions, please" " restart as root or with CAP_NET_RAW\n"); goto error; } default_core_cfg.udp4_raw = 0; /* disabled */ } else { register_fds(1); if (default_core_cfg.udp4_raw < 0) { /* auto-detect => use it */ default_core_cfg.udp4_raw = 1; /* enabled */ DBG("raw socket possible => turning it on\n"); } if (default_core_cfg.udp4_raw_ttl < 0) { /* auto-detect */ default_core_cfg.udp4_raw_ttl = sock_get_ttl(sendipv4->socket); if (default_core_cfg.udp4_raw_ttl < 0) /* error, use some default value */ default_core_cfg.udp4_raw_ttl = 63; } } #else default_core_cfg.udp4_raw = 0; #endif /* USE_RAW_SOCKS */ } else sendipv6=bind_address; if (udp_listen->next){ LOG(L_WARN, "WARNING: using only the first listen address" " (no fork)\n"); } /* delay cfg_shmize to the last moment (it must be called _before_ forking). Changes to default cfgs after this point will be ignored. */ if (cfg_shmize() < 0) { LOG(L_CRIT, "could not initialize shared configuration\n"); goto error; } /* Register the children that will keep updating their * local configuration */ cfg_register_child( 1 /* main = udp listener */ + 1 /* timer */ #ifdef USE_SLOW_TIMER + 1 /* slow timer */ #endif ); if (do_suid()==-1) goto error; /* try to drop privileges */ /* process_no now initialized to zero -- increase from now on as new processes are forked (while skipping 0 reserved for main */ /* Temporary set the local configuration of the main process * to make the group instances available in PROC_INIT. */ cfg_main_set_local(); /* init childs with rank==PROC_INIT before forking any process, * this is a place for delayed (after mod_init) initializations * (e.g. shared vars that depend on the total number of processes * that is known only after all mod_inits have been executed ) * WARNING: the same init_child will be called latter, a second time * for the "main" process with rank PROC_MAIN (make sure things are * not initialized twice)*/ if (init_child(PROC_INIT) < 0) { LOG(L_ERR, "ERROR: main_dontfork: init_child(PROC_INT) --" " exiting\n"); cfg_main_reset_local(); goto error; } cfg_main_reset_local(); if (counters_prefork_init(get_max_procs()) == -1) goto error; #ifdef USE_SLOW_TIMER /* we need another process to act as the "slow" timer*/ pid = fork_process(PROC_TIMER, "slow timer", 0); if (pid<0){ LOG(L_CRIT, "ERROR: main_loop: Cannot fork\n"); goto error; } if (pid==0){ /* child */ /* timer!*/ /* process_bit = 0; */ if (real_time&2) set_rt_prio(rt_timer2_prio, rt_timer2_policy); if (arm_slow_timer()<0) goto error; slow_timer_main(); }else{ slow_timer_pid=pid; } #endif /* we need another process to act as the "main" timer*/ pid = fork_process(PROC_TIMER, "timer", 0); if (pid<0){ LOG(L_CRIT, "ERROR: main_loop: Cannot fork\n"); goto error; } if (pid==0){ /* child */ /* timer!*/ /* process_bit = 0; */ if (real_time&1) set_rt_prio(rt_timer1_prio, rt_timer1_policy); if (arm_timer()<0) goto error; timer_main(); }else{ } /* main process, receive loop */ process_no=0; /*main process number*/ pt[process_no].pid=getpid(); snprintf(pt[process_no].desc, MAX_PT_DESC, "stand-alone receiver @ %s:%s", bind_address->name.s, bind_address->port_no_str.s ); /* call it also w/ PROC_MAIN to make sure modules that init things * only in PROC_MAIN get a chance to run */ if (init_child(PROC_MAIN) < 0) { LOG(L_ERR, "ERROR: main_dontfork: init_child(PROC_MAIN) " "-- exiting\n"); goto error; } /* We will call child_init even if we * do not fork - and it will be called with rank 1 because * in fact we behave like a child, not like main process */ if (init_child(PROC_SIPINIT) < 0) { LOG(L_ERR, "main_dontfork: init_child failed\n"); goto error; } return udp_rcv_loop(); }else{ /* fork: */ /* Register the children that will keep updating their * local configuration. (udp/tcp/sctp listeneres * will be added later.) */ cfg_register_child( 1 /* timer */ #ifdef USE_SLOW_TIMER + 1 /* slow timer */ #endif ); for(si=udp_listen;si;si=si->next){ /* create the listening socket (for each address)*/ /* udp */ if (udp_init(si)==-1) goto error; /* get first ipv4/ipv6 socket*/ if ((si->address.af==AF_INET)&& ((sendipv4==0)||(sendipv4->flags&(SI_IS_LO|SI_IS_MCAST)))) sendipv4=si; #ifdef USE_IPV6 if ( ((sendipv6==0)||(sendipv6->flags&(SI_IS_LO|SI_IS_MCAST))) && (si->address.af==AF_INET6)) sendipv6=si; #endif /* children_no per each socket */ cfg_register_child((si->workers>0)?si->workers:children_no); } #ifdef USE_RAW_SOCKS /* always try to have a raw socket opened if we are using ipv4 */ if (sendipv4) { raw_udp4_send_sock = raw_socket(IPPROTO_RAW, 0, 0, 1); if (raw_udp4_send_sock < 0) { if ( default_core_cfg.udp4_raw > 0) { /* force use raw socket failed */ ERR("could not initialize raw udp send socket (ipv4):" " %s (%d)\n", strerror(errno), errno); if (errno == EPERM) ERR("could not initialize raw socket on startup" " due to inadequate permissions, please" " restart as root or with CAP_NET_RAW\n"); goto error; } default_core_cfg.udp4_raw = 0; /* disabled */ } else { register_fds(1); if (default_core_cfg.udp4_raw < 0) { /* auto-detect => use it */ default_core_cfg.udp4_raw = 1; /* enabled */ DBG("raw socket possible => turning it on\n"); } if (default_core_cfg.udp4_raw_ttl < 0) { /* auto-detect */ default_core_cfg.udp4_raw_ttl = sock_get_ttl(sendipv4->socket); if (default_core_cfg.udp4_raw_ttl < 0) /* error, use some default value */ default_core_cfg.udp4_raw_ttl = 63; } } } #else default_core_cfg.udp4_raw = 0; #endif /* USE_RAW_SOCKS */ #ifdef USE_SCTP if (!sctp_disable){ for(si=sctp_listen; si; si=si->next){ if (sctp_init_sock(si)==-1) goto error; /* get first ipv4/ipv6 socket*/ if ((si->address.af==AF_INET) && ((sendipv4_sctp==0) || (sendipv4_sctp->flags&(SI_IS_LO|SI_IS_MCAST)))) sendipv4_sctp=si; #ifdef USE_IPV6 if( ((sendipv6_sctp==0) || (sendipv6_sctp->flags&(SI_IS_LO|SI_IS_MCAST))) && (si->address.af==AF_INET6)) sendipv6_sctp=si; #endif /* sctp_children_no per each socket */ cfg_register_child((si->workers>0)?si->workers:sctp_children_no); } } #endif /* USE_SCTP */ #ifdef USE_TCP if (!tcp_disable){ for(si=tcp_listen; si; si=si->next){ /* same thing for tcp */ if (tcp_init(si)==-1) goto error; /* get first ipv4/ipv6 socket*/ if ((si->address.af==AF_INET)&& ((sendipv4_tcp==0) || (sendipv4_tcp->flags&(SI_IS_LO|SI_IS_MCAST)))) sendipv4_tcp=si; #ifdef USE_IPV6 if( ((sendipv6_tcp==0) || (sendipv6_tcp->flags&(SI_IS_LO|SI_IS_MCAST))) && (si->address.af==AF_INET6)) sendipv6_tcp=si; #endif } /* the number of sockets does not matter */ cfg_register_child(tcp_children_no + 1 /* tcp main */); } #ifdef USE_TLS if (!tls_disable && tls_has_init_si()){ for(si=tls_listen; si; si=si->next){ /* same as for tcp*/ if (tls_init(si)==-1) goto error; /* get first ipv4/ipv6 socket*/ if ((si->address.af==AF_INET)&& ((sendipv4_tls==0) || (sendipv4_tls->flags&(SI_IS_LO|SI_IS_MCAST)))) sendipv4_tls=si; #ifdef USE_IPV6 if( ((sendipv6_tls==0) || (sendipv6_tls->flags&(SI_IS_LO|SI_IS_MCAST))) && (si->address.af==AF_INET6)) sendipv6_tls=si; #endif } } #endif /* USE_TLS */ #endif /* USE_TCP */ /* all processes should have access to all the sockets (for * sending) so we open all first*/ if (do_suid()==-1) goto error; /* try to drop privileges */ /* delay cfg_shmize to the last moment (it must be called _before_ forking). Changes to default cfgs after this point will be ignored (cfg_shmize() will copy the default cfgs into shmem). */ if (cfg_shmize() < 0) { LOG(L_CRIT, "could not initialize shared configuration\n"); goto error; } /* Temporary set the local configuration of the main process * to make the group instances available in PROC_INIT. */ cfg_main_set_local(); /* init childs with rank==PROC_INIT before forking any process, * this is a place for delayed (after mod_init) initializations * (e.g. shared vars that depend on the total number of processes * that is known only after all mod_inits have been executed ) * WARNING: the same init_child will be called latter, a second time * for the "main" process with rank PROC_MAIN (make sure things are * not initialized twice)*/ if (init_child(PROC_INIT) < 0) { LOG(L_ERR, "ERROR: main: error in init_child(PROC_INT) --" " exiting\n"); cfg_main_reset_local(); goto error; } cfg_main_reset_local(); if (counters_prefork_init(get_max_procs()) == -1) goto error; /* udp processes */ for(si=udp_listen; si; si=si->next){ nrprocs = (si->workers>0)?si->workers:children_no; for(i=0;iaddress.af==AF_INET6) { if(si->useinfo.name.s) snprintf(si_desc, MAX_PT_DESC, "udp receiver child=%d " "sock=[%s]:%s (%s:%s)", i, si->name.s, si->port_no_str.s, si->useinfo.name.s, si->useinfo.port_no_str.s); else snprintf(si_desc, MAX_PT_DESC, "udp receiver child=%d " "sock=[%s]:%s", i, si->name.s, si->port_no_str.s); } else { if(si->useinfo.name.s) snprintf(si_desc, MAX_PT_DESC, "udp receiver child=%d " "sock=%s:%s (%s:%s)", i, si->name.s, si->port_no_str.s, si->useinfo.name.s, si->useinfo.port_no_str.s); else snprintf(si_desc, MAX_PT_DESC, "udp receiver child=%d " "sock=%s:%s", i, si->name.s, si->port_no_str.s); } child_rank++; pid = fork_process(child_rank, si_desc, 1); if (pid<0){ LOG(L_CRIT, "main_loop: Cannot fork\n"); goto error; }else if (pid==0){ /* child */ bind_address=si; /* shortcut */ #ifdef STATS setstats( i+r*children_no ); #endif return udp_rcv_loop(); } } /*parent*/ /*close(udp_sock)*/; /*if it's closed=>sendto invalid fd errors?*/ } #ifdef USE_SCTP /* sctp processes */ if (!sctp_disable){ for(si=sctp_listen; si; si=si->next){ nrprocs = (si->workers>0)?si->workers:sctp_children_no; for(i=0;iaddress.af==AF_INET6) { snprintf(si_desc, MAX_PT_DESC, "sctp receiver child=%d " "sock=[%s]:%s", i, si->name.s, si->port_no_str.s); } else { snprintf(si_desc, MAX_PT_DESC, "sctp receiver child=%d " "sock=%s:%s", i, si->name.s, si->port_no_str.s); } child_rank++; pid = fork_process(child_rank, si_desc, 1); if (pid<0){ LOG(L_CRIT, "main_loop: Cannot fork\n"); goto error; }else if (pid==0){ /* child */ bind_address=si; /* shortcut */ #ifdef STATS setstats( i+r*children_no ); #endif return sctp_rcv_loop(); } } /*parent*/ /*close(sctp_sock)*/; /*if closed=>sendto invalid fd errors?*/ } } #endif /* USE_SCTP */ /*this is the main process*/ bind_address=0; /* main proc -> it shouldn't send anything, */ #ifdef USE_SLOW_TIMER /* fork again for the "slow" timer process*/ pid = fork_process(PROC_TIMER, "slow timer", 1); if (pid<0){ LOG(L_CRIT, "main_loop: cannot fork \"slow\" timer process\n"); goto error; }else if (pid==0){ /* child */ if (real_time&2) set_rt_prio(rt_timer2_prio, rt_timer2_policy); if (arm_slow_timer()<0) goto error; slow_timer_main(); }else{ slow_timer_pid=pid; } #endif /* USE_SLOW_TIMER */ /* fork again for the "main" timer process*/ pid = fork_process(PROC_TIMER, "timer", 1); if (pid<0){ LOG(L_CRIT, "main_loop: cannot fork timer process\n"); goto error; }else if (pid==0){ /* child */ if (real_time&1) set_rt_prio(rt_timer1_prio, rt_timer1_policy); if (arm_timer()<0) goto error; timer_main(); } /* init childs with rank==MAIN before starting tcp main (in case they want * to fork a tcp capable process, the corresponding tcp. comm. fds in * pt[] must be set before calling tcp_main_loop()) */ if (init_child(PROC_MAIN) < 0) { LOG(L_ERR, "ERROR: main: error in init_child\n"); goto error; } #ifdef USE_TCP if (!tcp_disable){ /* start tcp & tls receivers */ if (tcp_init_children()<0) goto error; /* start tcp+tls master proc */ pid = fork_process(PROC_TCP_MAIN, "tcp main process", 0); if (pid<0){ LOG(L_CRIT, "main_loop: cannot fork tcp main process: %s\n", strerror(errno)); goto error; }else if (pid==0){ /* child */ tcp_main_loop(); }else{ tcp_main_pid=pid; unix_tcp_sock=-1; } } #endif /* main */ strncpy(pt[0].desc, "attendant", MAX_PT_DESC ); #ifdef USE_TCP close_extra_socks(PROC_ATTENDANT, get_proc_no()); if(!tcp_disable){ /* main's tcp sockets are disabled by default from init_pt() */ unix_tcp_sock=-1; } #endif /* init cfg, but without per child callbacks support */ cfg_child_no_cb_init(); cfg_ok=1; #ifdef EXTRA_DEBUG for (r=0; r<*process_count; r++){ fprintf(stderr, "% 3d % 5d - %s\n", r, pt[r].pid, pt[r].desc); } #endif DBG("Expect maximum %d open fds\n", get_max_open_fds()); /* in daemonize mode send the exit code back to the parent process */ if (!dont_daemonize) { if (daemon_status_send(0) < 0) { ERR("error sending daemon status: %s [%d]\n", strerror(errno), errno); goto error; } } for(;;){ handle_sigs(); pause(); cfg_update(); } } /*return 0; */ error: /* if we are here, we are the "main process", any forked children should exit with exit(-1) and not ever use return */ return -1; } /* * Calculate number of processes, this does not * include processes created by modules */ static int calc_proc_no(void) { int udp_listeners; struct socket_info* si; #ifdef USE_TCP int tcp_listeners; int tcp_e_listeners; #endif #ifdef USE_SCTP int sctp_listeners; #endif for (si=udp_listen, udp_listeners=0; si; si=si->next) udp_listeners += (si->workers>0)?si->workers:children_no; #ifdef USE_TCP for (si=tcp_listen, tcp_listeners=0, tcp_e_listeners=0; si; si=si->next) { if(si->workers>0) tcp_listeners += si->workers; else tcp_e_listeners = tcp_cfg_children_no; } tcp_listeners += tcp_e_listeners; tcp_children_no = tcp_listeners; #endif #ifdef USE_SCTP for (si=sctp_listen, sctp_listeners=0; si; si=si->next) sctp_listeners += (si->workers>0)?si->workers:sctp_children_no; #endif return /* receivers and attendant */ (dont_fork ? 1 : udp_listeners + 1) /* timer process */ + 1 /* always, we need it in most cases, and we can't tell here & now if we don't need it */ #ifdef USE_SLOW_TIMER + 1 /* slow timer process */ #endif #ifdef USE_TCP +((!tcp_disable)?( 1/* tcp main */ + tcp_listeners ):0) #endif #ifdef USE_SCTP +((!sctp_disable)?sctp_listeners:0) #endif ; } int main(int argc, char** argv) { FILE* cfg_stream; int c,r; char *tmp; int tmp_len; int port; int proto; char *options; int ret; unsigned int seed; int rfd; int debug_save, debug_flag; int dont_fork_cnt; struct name_lst* n_lst; char *p; /*init*/ time(&up_since); creator_pid = getpid(); ret=-1; my_argc=argc; my_argv=argv; debug_flag=0; dont_fork_cnt=0; daemon_status_init(); dprint_init_colors(); /* command line options */ options= ":f:cm:M:dVIhEeb:l:L:n:vKrRDTN:W:w:t:u:g:P:G:SQ:O:a:A:" #ifdef STATS "s:" #endif ; /* Handle special command line arguments, that must be treated before * intializing the various subsystem or before parsing other arguments: * - get the startup debug and log_stderr values * - look if pkg mem size is overriden on the command line (-M) and get * the new value here (before intializing pkg_mem). * - look if there is a -h, e.g. -f -h construction won't be caught * later */ opterr = 0; while((c=getopt(argc,argv,options))!=-1) { switch(c) { case 'd': debug_flag = 1; default_core_cfg.debug++; break; case 'E': log_stderr=1; break; case 'e': log_color=1; break; case 'M': pkg_mem_size=strtol(optarg, &tmp, 10) * 1024 * 1024; if (tmp &&(*tmp)){ fprintf(stderr, "bad private mem size number: -M %s\n", optarg); goto error; }; break; default: if (c == 'h' || (optarg && strcmp(optarg, "-h") == 0)) { printf("version: %s\n", full_version); printf("%s",help_msg); exit(0); } break; } } /*init pkg mallocs (before parsing cfg or the rest of the cmd line !)*/ if (pkg_mem_size) LOG(L_INFO, " private (per process) memory: %ld bytes\n", pkg_mem_size ); if (init_pkg_mallocs()==-1) goto error; #ifdef DBG_MSG_QA fprintf(stderr, "WARNING: ser startup: " "DBG_MSG_QA enabled, ser may exit abruptly\n"); #endif /* init counters / stats */ if (init_counters() == -1) goto error; #ifdef USE_TCP init_tcp_options(); /* set the defaults before the config */ #endif #ifdef USE_SCTP init_sctp_options(); /* set defaults before the config */ #endif /* process command line (cfg. file path etc) */ optind = 1; /* reset getopt */ /* switches required before script processing */ while((c=getopt(argc,argv,options))!=-1) { switch(c) { case 'f': cfg_file=optarg; break; case 'c': config_check=1; log_stderr=1; /* force stderr logging */ break; case 'L': mods_dir = optarg; break; case 'm': shm_mem_size=strtol(optarg, &tmp, 10) * 1024 * 1024; if (tmp &&(*tmp)){ fprintf(stderr, "bad shmem size number: -m %s\n", optarg); goto error; }; LOG(L_INFO, "ser: shared memory: %ld bytes\n", shm_mem_size ); break; case 'M': /* ignore it, it was parsed immediately after startup, the pkg mem. is already initialized at this point */ break; case 'd': /* ignore it, was parsed immediately after startup */ break; case 'v': case 'V': printf("version: %s\n", full_version); printf("flags: %s\n", ver_flags ); print_ct_constants(); #ifdef USE_SCTP tmp=malloc(256); if (tmp && (sctp_check_compiled_sockopts(tmp, 256)!=0)) printf("sctp unsupported socket options: %s\n", tmp); if (tmp) free(tmp); #endif printf("id: %s\n", ver_id); printf("compiled on %s with %s\n", ver_compiled_time, ver_compiler ); exit(0); break; case 'I': print_internals(); exit(0); break; case 'E': /* ignore it, was parsed immediately after startup */ break; case 'e': /* ignore it, was parsed immediately after startup */ break; case 'O': scr_opt_lev=strtol(optarg, &tmp, 10); if (tmp &&(*tmp)){ fprintf(stderr, "bad optimization level: -O %s\n", optarg); goto error; }; break; case 'u': /* user needed for possible shm. pre-init */ user=optarg; break; case 'A': p = strchr(optarg, '='); if(p) { *p = '\0'; } pp_define_set_type(0); if(pp_define(strlen(optarg), optarg)<0) { fprintf(stderr, "error at define param: -A %s\n", optarg); goto error; } if(p) { *p = '='; p++; if(pp_define_set(strlen(p), p)<0) { fprintf(stderr, "error at define value: -A %s\n", optarg); goto error; } } break; case 'b': case 'l': case 'n': case 'K': case 'r': case 'R': case 'D': case 'T': case 'N': case 'W': case 'w': case 't': case 'g': case 'P': case 'G': case 'S': case 'Q': case 'a': case 's': break; case '?': if (isprint(optopt)) { fprintf(stderr, "Unknown option `-%c'." " Use -h for help.\n", optopt); } else { fprintf(stderr, "Unknown option character `\\x%x'." " Use -h for help.\n", optopt); } goto error; case ':': fprintf(stderr, "Option `-%c' requires an argument." " Use -h for help.\n", optopt); goto error; default: abort(); } } if (endianness_sanity_check() != 0){ fprintf(stderr, "BUG: endianness sanity tests failed\n"); goto error; } if (init_routes()<0) goto error; if (init_nonsip_hooks()<0) goto error; if (init_script_cb()<0) goto error; if (pv_init_api()<0) goto error; if (pv_register_core_vars()!=0) goto error; if (init_rpcs()<0) goto error; if (register_core_rpcs()!=0) goto error; /* Fix the value of cfg_file variable.*/ if (fix_cfg_file() < 0) goto error; /* load config file or die */ cfg_stream=fopen (cfg_file, "r"); if (cfg_stream==0){ fprintf(stderr, "ERROR: loading config file(%s): %s\n", cfg_file, strerror(errno)); goto error; } /* seed the prng */ /* try to use /dev/urandom if possible */ seed=0; if ((rfd=open("/dev/urandom", O_RDONLY))!=-1){ try_again: if (read(rfd, (void*)&seed, sizeof(seed))==-1){ if (errno==EINTR) goto try_again; /* interrupted by signal */ LOG(L_WARN, "WARNING: could not read from /dev/urandom (%d)\n", errno); } DBG("read %u from /dev/urandom\n", seed); close(rfd); }else{ LOG(L_WARN, "WARNING: could not open /dev/urandom (%d)\n", errno); } seed+=getpid()+time(0); DBG("seeding PRNG with %u\n", seed); srand(seed); fastrand_seed(rand()); srandom(rand()+time(0)); DBG("test random numbers %u %lu %u\n", rand(), random(), fastrand()); /*register builtin modules*/ register_builtin_modules(); /* init named flags */ init_named_flags(); yyin=cfg_stream; debug_save = default_core_cfg.debug; if ((yyparse()!=0)||(cfg_errors)){ fprintf(stderr, "ERROR: bad config file (%d errors)\n", cfg_errors); goto error; } if (cfg_warnings){ fprintf(stderr, "%d config warnings\n", cfg_warnings); } if (debug_flag) default_core_cfg.debug = debug_save; print_rls(); /* options with higher priority than cfg file */ optind = 1; /* reset getopt */ while((c=getopt(argc,argv,options))!=-1) { switch(c) { case 'f': case 'c': case 'm': case 'M': case 'd': case 'v': case 'V': case 'I': case 'h': case 'O': case 'A': break; case 'E': log_stderr=1; /* use in both getopt switches, takes priority over config */ break; case 'e': log_color=1; /* use in both getopt switches, takes priority over config */ break; case 'b': maxbuffer=strtol(optarg, &tmp, 10); if (tmp &&(*tmp)){ fprintf(stderr, "bad max buffer size number: -b %s\n", optarg); goto error; } break; case 'l': if ((n_lst=parse_phostport_mh(optarg, &tmp, &tmp_len, &port, &proto))==0){ fprintf(stderr, "bad -l address specifier: %s\n", optarg); goto error; } /* add a new addr. to our address list */ if (add_listen_iface(n_lst->name, n_lst->next, port, proto, n_lst->flags)!=0){ fprintf(stderr, "failed to add new listen address\n"); free_name_lst(n_lst); goto error; } free_name_lst(n_lst); break; case 'n': children_no=strtol(optarg, &tmp, 10); if ((tmp==0) ||(*tmp)){ fprintf(stderr, "bad process number: -n %s\n", optarg); goto error; } break; case 'K': check_via=1; break; case 'r': received_dns|=DO_DNS; break; case 'R': received_dns|=DO_REV_DNS; break; case 'D': dont_fork_cnt++; break; case 'T': #ifdef USE_TCP tcp_disable=1; #else fprintf(stderr,"WARNING: tcp support not compiled in\n"); #endif break; case 'N': #ifdef USE_TCP tcp_cfg_children_no=strtol(optarg, &tmp, 10); if ((tmp==0) ||(*tmp)){ fprintf(stderr, "bad process number: -N %s\n", optarg); goto error; } #else fprintf(stderr,"WARNING: tcp support not compiled in\n"); #endif break; case 'W': #ifdef USE_TCP tcp_poll_method=get_poll_type(optarg); if (tcp_poll_method==POLL_NONE){ fprintf(stderr, "bad poll method name: -W %s\ntry " "one of %s.\n", optarg, poll_support); goto error; } #else fprintf(stderr,"WARNING: tcp support not compiled in\n"); #endif break; case 'S': #ifdef USE_SCTP sctp_disable=1; #else fprintf(stderr,"WARNING: sctp support not compiled in\n"); #endif break; case 'Q': #ifdef USE_SCTP sctp_children_no=strtol(optarg, &tmp, 10); if ((tmp==0) ||(*tmp)){ fprintf(stderr, "bad process number: -O %s\n", optarg); goto error; } #else fprintf(stderr,"WARNING: sctp support not compiled in\n"); #endif break; case 'w': working_dir=optarg; break; case 't': chroot_dir=optarg; break; case 'u': user=optarg; break; case 'g': group=optarg; break; case 'P': pid_file=optarg; break; case 'G': pgid_file=optarg; break; case 'a': if(strcmp(optarg, "on")==0 || strcmp(optarg, "yes")==0) sr_auto_aliases = 1; else if(strcmp(optarg, "off")==0 || strcmp(optarg, "no")==0) sr_auto_aliases = 0; else { fprintf(stderr, "bad auto aliases parameter: %s (valid on, off, yes, no)\n", optarg); goto error; } break; case 's': #ifdef STATS stat_file=optarg; #endif break; default: break; } } /* reinit if pv buffer size has been set in config */ if (pv_reinit_buffer()<0) goto error; if (dont_fork_cnt) dont_fork = dont_fork_cnt; /* override by command line */ if (dont_fork > 0) { dont_daemonize = dont_fork == 2; dont_fork = dont_fork == 1; } /* init locks first */ if (init_lock_ops()!=0) goto error; #ifdef USE_TCP #ifdef USE_TLS if (tcp_disable) tls_disable=1; /* if no tcp => no tls */ #endif /* USE_TLS */ #endif /* USE_TCP */ #ifdef USE_SCTP if (sctp_disable!=1){ /* fix it */ if (sctp_check_support()==-1){ /* check if sctp support is auto, if not warn about disabling it */ if (sctp_disable!=2){ fprintf(stderr, "ERROR: " "sctp enabled, but not supported by" " the OS\n"); goto error; } sctp_disable=1; }else{ /* sctp_disable!=1 and sctp supported => enable sctp */ sctp_disable=0; } } #endif /* USE_SCTP */ /* initialize the configured proto list */ init_proto_order(); /* init the resolver, before fixing the config */ resolv_init(); /* fix parameters */ if (port_no<=0) port_no=SIP_PORT; #ifdef USE_TLS if (tls_port_no<=0) tls_port_no=SIPS_PORT; #endif if (children_no<=0) children_no=CHILD_NO; #ifdef USE_TCP if (!tcp_disable){ if (tcp_cfg_children_no<=0) tcp_cfg_children_no=children_no; tcp_children_no = tcp_cfg_children_no; } #endif #ifdef USE_SCTP if (!sctp_disable){ if (sctp_children_no<=0) sctp_children_no=children_no; } #endif if (working_dir==0) working_dir="/"; /* get uid/gid */ if (user){ if (user2uid(&uid, &gid, user)<0){ fprintf(stderr, "bad user name/uid number: -u %s\n", user); goto error; } } if (group){ if (group2gid(&gid, group)<0){ fprintf(stderr, "bad group name/gid number: -u %s\n", group); goto error; } } if (fix_all_socket_lists()!=0){ fprintf(stderr, "failed to initialize list addresses\n"); goto error; } if (default_core_cfg.dns_try_ipv6 && !(socket_types & SOCKET_T_IPV6)){ /* if we are not listening on any ipv6 address => no point * to try to resovle ipv6 addresses */ default_core_cfg.dns_try_ipv6=0; } /* print all the listen addresses */ printf("Listening on \n"); print_all_socket_lists(); printf("Aliases: \n"); /*print_aliases();*/ print_aliases(); printf("\n"); if (dont_fork){ fprintf(stderr, "WARNING: no fork mode %s\n", (udp_listen)?( (udp_listen->next)?"and more than one listen address found " "(will use only the first one)":"" ):"and no udp listen address found" ); } if (config_check){ fprintf(stderr, "config file ok, exiting...\n"); return 0; } /*init shm mallocs * this must be here * -to allow setting shm mem size from the command line * => if shm_mem should be settable from the cfg file move * everything after * -it must be also before init_timer and init_tcp * -it must be after we know uid (so that in the SYSV sems case, * the sems will have the correct euid) * Note: shm can now be initialized when parsing the config script, that's * why checking for a prior initialization is needed. * --andrei */ #ifdef SHM_MEM if (!shm_initialized() && init_shm()<0) goto error; #endif /* SHM_MEM */ if (init_atomic_ops()==-1) goto error; if (init_basex() != 0){ LOG(L_CRIT, "could not initialize base* framework\n"); goto error; } if (sr_cfg_init() < 0) { LOG(L_CRIT, "could not initialize configuration framework\n"); goto error; } /* declare the core cfg before the module configs */ if (cfg_declare("core", core_cfg_def, &default_core_cfg, cfg_sizeof(core), &core_cfg) ) { LOG(L_CRIT, "could not declare the core configuration\n"); goto error; } #ifdef USE_TCP if (tcp_register_cfg()){ LOG(L_CRIT, "could not register the tcp configuration\n"); goto error; } #endif /* USE_TCP */ #ifdef USE_SCTP if (sctp_register_cfg()){ LOG(L_CRIT, "could not register the sctp configuration\n"); goto error; } #endif /* USE_SCTP */ /*init timer, before parsing the cfg!*/ if (init_timer()<0){ LOG(L_CRIT, "could not initialize timer, exiting...\n"); goto error; } #ifdef USE_DNS_CACHE if (init_dns_cache()<0){ LOG(L_CRIT, "could not initialize the dns cache, exiting...\n"); goto error; } #ifdef USE_DNS_CACHE_STATS /* preinitializing before the nubmer of processes is determined */ if (init_dns_cache_stats(1)<0){ LOG(L_CRIT, "could not initialize the dns cache measurement\n"); goto error; } #endif /* USE_DNS_CACHE_STATS */ #endif #ifdef USE_DST_BLACKLIST if (init_dst_blacklist()<0){ LOG(L_CRIT, "could not initialize the dst blacklist, exiting...\n"); goto error; } #ifdef USE_DST_BLACKLIST_STATS /* preinitializing before the number of processes is determined */ if (init_dst_blacklist_stats(1)<0){ LOG(L_CRIT, "could not initialize the dst blacklist measurement\n"); goto error; } #endif /* USE_DST_BLACKLIST_STATS */ #endif if (init_avps()<0) goto error; if (rpc_init_time() < 0) goto error; #ifdef USE_TCP if (!tcp_disable){ /*init tcp*/ if (init_tcp()<0){ LOG(L_CRIT, "could not initialize tcp, exiting...\n"); goto error; } } #endif /* USE_TCP */ #ifdef USE_SCTP if (!sctp_disable){ if (init_sctp()<0){ LOG(L_CRIT, "Could not initialize sctp, exiting...\n"); goto error; } } #endif /* USE_SCTP */ /* init_daemon? */ if( !dont_fork && daemonize((log_name==0)?argv[0]:log_name, 1) < 0) goto error; if (install_sigs() != 0){ fprintf(stderr, "ERROR: could not install the signal handlers\n"); goto error; } if (disable_core_dump) set_core_dump(0, 0); else set_core_dump(1, shm_mem_size+pkg_mem_size+4*1024*1024); if (open_files_limit>0){ if(increase_open_fds(open_files_limit)<0){ fprintf(stderr, "ERROR: error could not increase file limits\n"); goto error; } } if (mlock_pages) mem_lock_pages(); if (real_time&4) set_rt_prio(rt_prio, rt_policy); if (init_modules() != 0) { fprintf(stderr, "ERROR: error while initializing modules\n"); goto error; } /* initialize process_table, add core process no. (calc_proc_no()) to the * processes registered from the modules*/ if (init_pt(calc_proc_no())==-1) goto error; #ifdef USE_TCP #ifdef USE_TLS if (!tls_disable){ if (!tls_loaded()){ LOG(L_WARN, "WARNING: tls support enabled, but no tls engine " " available (forgot to load the tls module?)\n"); LOG(L_WARN, "WARNING: disabling tls...\n"); tls_disable=1; } /* init tls*/ if (init_tls()<0){ LOG(L_CRIT, "could not initialize tls, exiting...\n"); goto error; } } #endif /* USE_TLS */ #endif /* USE_TCP */ /* The total number of processes is now known, note that no * function being called before this point may rely on the * number of processes ! */ DBG("Expect (at least) %d SER processes in your process list\n", get_max_procs()); #if defined USE_DNS_CACHE && defined USE_DNS_CACHE_STATS if (init_dns_cache_stats(get_max_procs())<0){ LOG(L_CRIT, "could not initialize the dns cache measurement\n"); goto error; } #endif #if defined USE_DST_BLACKLIST && defined USE_DST_BLACKLIST_STATS if (init_dst_blacklist_stats(get_max_procs())<0){ LOG(L_CRIT, "could not initialize the dst blacklist measurement\n"); goto error; } #endif /* fix routing lists */ if ( (r=fix_rls())!=0){ fprintf(stderr, "ERROR: error %d while trying to fix configuration\n", r); goto error; }; fixup_complete=1; #ifdef STATS if (init_stats( dont_fork ? 1 : children_no )==-1) goto error; #endif ret=main_loop(); if (ret < 0) goto error; /*kill everything*/ if (is_main) shutdown_children(SIGTERM, 0); if (!dont_daemonize) { if (daemon_status_send(0) < 0) ERR("error sending exit status: %s [%d]\n", strerror(errno), errno); } /* else terminate process */ return ret; error: /*kill everything*/ if (is_main) shutdown_children(SIGTERM, 0); if (!dont_daemonize) { if (daemon_status_send((char)-1) < 0) ERR("error sending exit status: %s [%d]\n", strerror(errno), errno); } return -1; } kamailio-4.0.4/nonsip_hooks.h0000644000000000000000000000417612223032460014667 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * non-sip callbacks, called whenever a message with protocol != SIP/2.0 * is received (the message must have at least a sip like first line or * else they will be dropped before this callbacks are called */ /* * History: * -------- * 2006-11-29 created by andrei */ #ifndef _nonsip_hooks_h #define _nonsip_hooks_h #include "parser/msg_parser.h" /* sip_msg */ #define MAX_NONSIP_HOOKS 1 enum nonsip_msg_returns{ NONSIP_MSG_ERROR=-1, NONSIP_MSG_DROP=0, NONSIP_MSG_PASS, NONSIP_MSG_ACCEPT }; struct nonsip_hook{ char* name; /* must be !=0, it has only "debugging" value */ /* called each time a sip like request (from the first line point of view) * with protocol/version != SIP/2.0 is received * return: 0 - drop message immediately, >0 - continue with other hooks, * <0 - error (drop message) */ int (*on_nonsip_req)(struct sip_msg* msg); /* called before ser shutdown (last minute cleanups) */ void (*destroy)(void); }; int init_nonsip_hooks(void); void destroy_nonsip_hooks(void); int register_nonsip_msg_hook(struct nonsip_hook *h); int nonsip_msg_run_hooks(struct sip_msg* msg); #endif kamailio-4.0.4/raw_sock.c0000644000000000000000000005555312223032460013766 0ustar rootroot/* * $Id$ * * Copyright (C) 2010 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** raw socket functions. * @file raw_sock.c * @ingroup core * Module: @ref core */ /* * History: * -------- * 2010-06-07 initial version (from older code) andrei * 2010-06-15 IP_HDRINCL raw socket support, including on-send * fragmentation (andrei) */ #ifdef USE_RAW_SOCKS #include "compiler_opt.h" #include "ip_addr.h" #include "dprint.h" #include "str.h" #include "rand/fastrand.h" #include "globals.h" #include #include #include #include #include #include #include #include #include #ifndef __USE_BSD #define __USE_BSD /* on linux use bsd version of iphdr (more portable) */ #endif /* __USE_BSD */ #include #define __FAVOR_BSD /* on linux use bsd version of udphdr (more portable) */ #include #include "raw_sock.h" #include "cfg/cfg.h" #include "cfg_core.h" #if defined (__OS_freebsd) || defined (__OS_netbsd) || defined(__OS_openbsd) \ || defined (__OS_darwin) /** fragmentation is done by the kernel (no need to do it in userspace) */ #define RAW_IPHDR_INC_AUTO_FRAG #endif /* __OS_* */ /* macros for converting values in the expected format */ #if defined (__OS_freebsd) || defined (__OS_netbsd) || defined (__OS_darwin) /* on freebsd and netbsd the ip offset (along with flags) and the ip header length must be filled in _host_ bytes order format. The same is true for openbsd < 2.1. */ /** convert the ip offset in the format expected by the kernel. */ #define RAW_IPHDR_IP_OFF(off) (unsigned short)(off) /** convert the ip total length in the format expected by the kernel. */ #define RAW_IPHDR_IP_LEN(tlen) (unsigned short)(tlen) #else /* __OS_* */ /* linux, openbsd >= 2.1 a.s.o. */ /** convert the ip offset in the format expected by the kernel. */ #define RAW_IPHDR_IP_OFF(off) htons((unsigned short)(off)) /** convert the ip total length in the format expected by the kernel. */ #define RAW_IPHDR_IP_LEN(tlen) htons((unsigned short)(tlen)) #endif /* __OS_* */ int raw_ipip = 0; /* set if raw socket is in capture mode for IPIP */ /** create and return a raw socket. * @param proto - protocol used (e.g. IPPROTO_UDP, IPPROTO_RAW) * @param ip - if not null the socket will be bound on this ip. * @param iface - if not null the socket will be bound to this interface * (SO_BINDTODEVICE). This is supported only on linux. * @param iphdr_incl - set to 1 if packets send on this socket include * a pre-built ip header (some fields, like the checksum * will still be filled by the kernel, OTOH packet * fragmentation has to be done in user space). * @return socket on success, -1 on error */ int raw_socket(int proto, struct ip_addr* ip, str* iface, int iphdr_incl) { int sock; int t; union sockaddr_union su; #if defined (SO_BINDTODEVICE) char short_ifname[sizeof(int)]; int ifname_len; char* ifname; #endif /* SO_BINDTODEVICE */ sock = socket(PF_INET, SOCK_RAW, proto); if (sock==-1) goto error; /* set socket options */ if (iphdr_incl) { t=1; if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &t, sizeof(t))<0){ ERR("raw_socket: setsockopt(IP_HDRINCL) failed: %s [%d]\n", strerror(errno), errno); goto error; } } else { /* IP_PKTINFO makes no sense if the ip header is included */ /* using IP_PKTINFO */ t=1; #ifdef IP_PKTINFO if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &t, sizeof(t))<0){ ERR("raw_socket: setsockopt(IP_PKTINFO) failed: %s [%d]\n", strerror(errno), errno); goto error; } #elif defined(IP_RECVDSTADDR) if (setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &t, sizeof(t))<0){ ERR("raw_socket: setsockop(IP_RECVDSTADDR) failed: %s [%d]\n", strerror(errno), errno); goto error; } #else #error "no method of getting the destination ip address supported" #endif /* IP_RECVDSTADDR / IP_PKTINFO */ } #if defined (IP_MTU_DISCOVER) && defined (IP_PMTUDISC_DONT) t=IP_PMTUDISC_DONT; if(setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &t, sizeof(t)) ==-1){ ERR("raw_socket: setsockopt(IP_MTU_DISCOVER): %s\n", strerror(errno)); goto error; } #endif /* IP_MTU_DISCOVER && IP_PMTUDISC_DONT */ if (iface && iface->s){ #if defined (SO_BINDTODEVICE) /* workaround for linux bug: arg to setsockopt must have at least * sizeof(int) size or EINVAL would be returned */ if (iface->lens, iface->len); short_ifname[iface->len]=0; /* make sure it's zero term */ ifname_len=sizeof(short_ifname); ifname=short_ifname; }else{ ifname_len=iface->len; ifname=iface->s; } if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, ifname_len) <0){ ERR("raw_socket: could not bind to %.*s: %s [%d]\n", iface->len, ZSW(iface->s), strerror(errno), errno); goto error; } #else /* !SO_BINDTODEVICE */ /* SO_BINDTODEVICE is linux specific => cannot bind to a device */ ERR("raw_socket: bind to device supported only on linux\n"); goto error; #endif /* SO_BINDTODEVICE */ } /* FIXME: probe_max_receive_buffer(sock) missing */ if (ip){ init_su(&su, ip, 0); if (bind(sock, &su.s, sockaddru_len(su))==-1){ ERR("raw_socket: bind(%s) failed: %s [%d]\n", ip_addr2a(ip), strerror(errno), errno); goto error; } } return sock; error: if (sock!=-1) close(sock); return -1; } /** create and return an udp over ipv4 raw socket. * @param ip - if not null the socket will be bound on this ip. * @param iface - if not null the socket will be bound to this interface * (SO_BINDTODEVICE). * @param iphdr_incl - set to 1 if packets send on this socket include * a pre-built ip header (some fields, like the checksum * will still be filled by the kernel, OTOH packet * fragmentation has to be done in user space). * @return socket on success, -1 on error */ int raw_udp4_socket(struct ip_addr* ip, str* iface, int iphdr_incl) { return raw_socket(IPPROTO_UDP, ip, iface, iphdr_incl); } /** receives an ipv4 packet using a raw socket. * An ipv4 packet is received in buf, using IP_PKTINFO or IP_RECVDSTADDR. * from and to are filled (only the ip part the ports are 0 since this * function doesn't try to look beyond the IP level). * @param sock - raw socket * @param buf - detination buffer. * @param len - buffer len (should be enough for receiving a packet + * IP header). * @param from - result parameter, the IP address part of it will be filled * with the source address and the port with 0. * @param to - result parameter, the IP address part of it will be filled * with the destination (local) address and the port with 0. * @return packet len or <0 on error: -1 (check errno), * -2 no IP_PKTINFO/IP_RECVDSTADDR found or AF mismatch */ int recvpkt4(int sock, char* buf, int len, union sockaddr_union* from, union sockaddr_union* to) { struct iovec iov[1]; struct msghdr rcv_msg; struct cmsghdr* cmsg; #ifdef IP_PKTINFO struct in_pktinfo* rcv_pktinfo; #endif /* IP_PKTINFO */ int n, ret; char msg_ctrl_buf[1024]; iov[0].iov_base=buf; iov[0].iov_len=len; rcv_msg.msg_name=from; rcv_msg.msg_namelen=sockaddru_len(*from); rcv_msg.msg_control=msg_ctrl_buf; rcv_msg.msg_controllen=sizeof(msg_ctrl_buf); rcv_msg.msg_iov=&iov[0]; rcv_msg.msg_iovlen=1; ret=-2; /* no PKT_INFO or AF mismatch */ retry: n=recvmsg(sock, &rcv_msg, MSG_WAITALL); if (unlikely(n==-1)){ if (errno==EINTR) goto retry; ret=n; goto end; } /* find the pkt info */ for (cmsg=CMSG_FIRSTHDR(&rcv_msg); cmsg; cmsg=CMSG_NXTHDR(&rcv_msg, cmsg)){ #ifdef IP_PKTINFO if (likely((cmsg->cmsg_level==IPPROTO_IP) && (cmsg->cmsg_type==IP_PKTINFO))) { rcv_pktinfo=(struct in_pktinfo*)CMSG_DATA(cmsg); to->sin.sin_family=AF_INET; memcpy(&to->sin.sin_addr, &rcv_pktinfo->ipi_spec_dst.s_addr, sizeof(to->sin.sin_addr)); to->sin.sin_port=0; /* not known */ /* interface no. in ipi_ifindex */ ret=n; /* success */ break; } #elif defined (IP_RECVDSTADDR) if (likely((cmsg->cmsg_level==IPPROTO_IP) && (cmsg->cmsg_type==IP_RECVDSTADDR))) { to->sin.sin_family=AF_INET; memcpy(&to->sin.sin_addr, CMSG_DATA(cmsg), sizeof(to->sin.sin_addr)); to->sin.sin_port=0; /* not known */ ret=n; /* success */ break; } #else #error "no method of getting the destination ip address supported" #endif /* IP_PKTINFO / IP_RECVDSTADDR */ } end: return ret; } /* receive an ipv4 udp packet over a raw socket. * The packet is copied in *buf and *buf is advanced to point to the * payload. Fills from and to. * @param rsock - raw socket * @param buf - the packet will be written to where *buf points intially and * then *buf will be advanced to point to the udp payload. * @param len - buffer length (should be enough to hold at least the * ip and udp headers + 1 byte). * @param from - result parameter, filled with source address and port of the * packet. * @param from - result parameter, filled with destination (local) address and * port of the packet. * @param rf - filter used to decide whether or not the packet is * accepted/processed. If null, all the packets are accepted. * @return packet len or <0 on error (-1 and -2 on recv error @see recvpkt4, * -3 if the headers are invalid and -4 if the packet doesn't * match the filter). */ int raw_udp4_recv(int rsock, char** buf, int len, union sockaddr_union* from, union sockaddr_union* to, struct raw_filter* rf) { int n; unsigned short dst_port; unsigned short src_port; struct ip_addr dst_ip; char* end; char* udph_start; char* udp_payload; struct ip iph; struct udphdr udph; unsigned short udp_len; n=recvpkt4(rsock, *buf, len, from, to); if (unlikely(n<0)) goto error; end=*buf+n; if (unlikely(n<((sizeof(struct ip) * raw_ipip ? 2 : 1)+sizeof(struct udphdr)))) { n=-3; goto error; } if(raw_ipip) *buf = *buf + sizeof(struct ip); /* FIXME: if initial buffer is aligned, one could skip the memcpy and directly cast ip and udphdr pointer to the memory */ memcpy(&iph, *buf, sizeof(struct ip)); udph_start=*buf+iph.ip_hl*4; udp_payload=udph_start+sizeof(struct udphdr); if (unlikely(udp_payload>end)){ n=-3; goto error; } memcpy(&udph, udph_start, sizeof(struct udphdr)); udp_len=ntohs(udph.uh_ulen); if (unlikely((udph_start+udp_len)!=end)){ if ((udph_start+udp_len)>end){ n=-3; goto error; }else{ ERR("udp length too small: %d/%d\n", (int)udp_len, (int)(end-udph_start)); n=-3; goto error; } } /* advance buf */ *buf=udp_payload; n=(int)(end-*buf); /* fill ip from the packet (needed if no PKT_INFO is used) */ dst_ip.af=AF_INET; dst_ip.len=4; dst_ip.u.addr32[0]=iph.ip_dst.s_addr; /* fill dst_port */ dst_port=ntohs(udph.uh_dport); ip_addr2su(to, &dst_ip, dst_port); /* fill src_port */ src_port=ntohs(udph.uh_sport); su_setport(from, src_port); if (likely(rf)) { su2ip_addr(&dst_ip, to); if ( (dst_port && rf->port1 && ((dst_portport1) || (dst_port>rf->port2)) ) || (matchnet(&dst_ip, &rf->dst)!=1) ){ /* no match */ n=-4; goto error; } } error: return n; } /** udp checksum helper: compute the pseudo-header 16-bit "sum". * Computes the partial checksum (no complement) of the pseudo-header. * It is meant to be used by udpv4_chksum(). * @param uh - filled udp header * @param src - source ip address in network byte order. * @param dst - destination ip address in network byte order. * @param length - payload length (not including the udp header), * in _host_ order. * @return the partial checksum in host order */ static inline unsigned short udpv4_vhdr_sum( struct udphdr* uh, struct in_addr* src, struct in_addr* dst, unsigned short length) { unsigned sum; /* pseudo header */ sum=(src->s_addr>>16)+(src->s_addr&0xffff)+ (dst->s_addr>>16)+(dst->s_addr&0xffff)+ htons(IPPROTO_UDP)+(uh->uh_ulen); /* udp header */ sum+=(uh->uh_dport)+(uh->uh_sport)+(uh->uh_ulen) + 0 /*chksum*/; /* fold it */ sum=(sum>>16)+(sum&0xffff); sum+=(sum>>16); /* no complement */ return ntohs((unsigned short) sum); } /** compute the udp over ipv4 checksum. * @param u - filled udp header (except checksum). * @param src - source ip v4 address, in _network_ byte order. * @param dst - destination ip v4 address, int _network_ byte order. * @param data - pointer to the udp payload. * @param length - payload length, not including the udp header and in * _host_ order. The length mist be <= 0xffff - 8 * (to allow space for the udp header). * @return the checksum in _host_ order */ inline static unsigned short udpv4_chksum(struct udphdr* u, struct in_addr* src, struct in_addr* dst, unsigned char* data, unsigned short length) { unsigned sum; unsigned char* end; sum=udpv4_vhdr_sum(u, src, dst, length); end=data+(length&(~0x1)); /* make sure it's even */ /* TODO: 16 & 32 bit aligned version */ /* not aligned */ for(;data>16)+(sum&0xffff); sum+=(sum>>16); return (unsigned short)~sum; } /** fill in an udp header. * @param u - udp header that will be filled. * @param from - source ip v4 address and port. * @param to - destination ip v4 address and port. * @param buf - pointer to the payload. * @param len - payload length (not including the udp header). * @param do_chk - if set the udp checksum will be computed, else it will * be set to 0. * @return 0 on success, < 0 on error. */ inline static int mk_udp_hdr(struct udphdr* u, struct sockaddr_in* from, struct sockaddr_in* to, unsigned char* buf, int len, int do_chk) { u->uh_ulen=htons((unsigned short)len+sizeof(struct udphdr)); u->uh_sport=from->sin_port; u->uh_dport=to->sin_port; if (do_chk) u->uh_sum=htons( udpv4_chksum(u, &from->sin_addr, &to->sin_addr, buf, len)); else u->uh_sum=0; /* no checksum */ return 0; } /** fill in an ip header. * Note: the checksum is _not_ computed. * WARNING: The ip header length and offset might be filled in * _host_ byte order or network byte order (depending on the OS, for example * freebsd needs host byte order for raw sockets with IPHDR_INC, while * linux needs network byte order). * @param iph - ip header that will be filled. * @param from - source ip v4 address (network byte order). * @param to - destination ip v4 address (network byte order). * @param payload len - payload length (not including the ip header). * @param proto - protocol. * @return 0 on success, < 0 on error. */ inline static int mk_ip_hdr(struct ip* iph, struct in_addr* from, struct in_addr* to, int payload_len, unsigned char proto) { iph->ip_hl = sizeof(struct ip)/4; iph->ip_v = 4; iph->ip_tos = tos; /* on freebsd ip_len _must_ be in _host_ byte order instead of network byte order. On linux the length is ignored (it's filled automatically every time). */ iph->ip_len = RAW_IPHDR_IP_LEN(payload_len + sizeof(struct ip)); iph->ip_id = 0; /* 0 => will be filled automatically by the kernel */ iph->ip_off = 0; /* frag.: first 3 bits=flags=0, last 13 bits=offset */ iph->ip_ttl = cfg_get(core, core_cfg, udp4_raw_ttl); iph->ip_p = proto; iph->ip_src = *from; iph->ip_dst = *to; iph->ip_sum = 0; return 0; } /** send an udp packet over a non-ip_hdrincl raw socket. * @param rsock - raw socket * @param buf - data * @param len - data len * @param from - source address:port (_must_ be non-null, but the ip address * can be 0, in which case it will be filled by the kernel). * @param to - destination address:port * @return <0 on error (errno set too), number of bytes sent on success * (including the udp header => on success len + udpheader size). */ int raw_udp4_send(int rsock, char* buf, unsigned int len, union sockaddr_union* from, union sockaddr_union* to) { struct msghdr snd_msg; struct cmsghdr* cmsg; #ifdef IP_PKTINFO struct in_pktinfo* snd_pktinfo; #endif /* IP_PKTINFO */ struct iovec iov[2]; struct udphdr udp_hdr; char msg_ctrl_snd_buf[1024]; int ret; memset(&snd_msg, 0, sizeof(snd_msg)); snd_msg.msg_name=&to->sin; snd_msg.msg_namelen=sockaddru_len(*to); snd_msg.msg_iov=&iov[0]; /* prepare udp header */ mk_udp_hdr(&udp_hdr, &from->sin, &to->sin, (unsigned char*)buf, len, 1); iov[0].iov_base=(char*)&udp_hdr; iov[0].iov_len=sizeof(udp_hdr); iov[1].iov_base=buf; iov[1].iov_len=len; snd_msg.msg_iovlen=2; snd_msg.msg_control=msg_ctrl_snd_buf; snd_msg.msg_controllen=sizeof(msg_ctrl_snd_buf); /* init pktinfo cmsg */ cmsg=CMSG_FIRSTHDR(&snd_msg); cmsg->cmsg_level=IPPROTO_IP; #ifdef IP_PKTINFO cmsg->cmsg_type=IP_PKTINFO; cmsg->cmsg_len=CMSG_LEN(sizeof(struct in_pktinfo)); snd_pktinfo=(struct in_pktinfo*)CMSG_DATA(cmsg); snd_pktinfo->ipi_ifindex=0; snd_pktinfo->ipi_spec_dst.s_addr=from->sin.sin_addr.s_addr; #elif defined (IP_SENDSRCADDR) cmsg->cmsg_type=IP_SENDSRCADDR; cmsg->cmsg_len=CMSG_LEN(sizeof(struct in_addr)); memcpy(CMSG_DATA(cmsg), &from->sin.sin_addr.s_addr, sizeof(struct in_addr)); #else #error "no method of setting the source ip supported" #endif /* IP_PKTINFO / IP_SENDSRCADDR */ snd_msg.msg_controllen=cmsg->cmsg_len; snd_msg.msg_flags=0; ret=sendmsg(rsock, &snd_msg, 0); return ret; } /** send an udp packet over an IP_HDRINCL raw socket. * If needed, send several fragments. * @param rsock - raw socket * @param buf - data * @param len - data len * @param from - source address:port (_must_ be non-null, but the ip address * can be 0, in which case it will be filled by the kernel). * @param to - destination address:port * @param mtu - maximum datagram size (including the ip header, excluding * link layer headers). Minimum allowed size is 28 * (sizeof(ip_header + udp_header)). If mtu is lower, it will * be ignored (the packet will be sent un-fragmented). * 0 can be used to disable fragmentation. * @return <0 on error (-2: datagram too big, -1: check errno), * number of bytes sent on success * (including the ip & udp headers => * on success len + udpheader + ipheader size). */ int raw_iphdr_udp4_send(int rsock, char* buf, unsigned int len, union sockaddr_union* from, union sockaddr_union* to, unsigned short mtu) { struct msghdr snd_msg; struct iovec iov[2]; struct ip_udp_hdr { struct ip ip; struct udphdr udp; } hdr; unsigned int totlen; #ifndef RAW_IPHDR_INC_AUTO_FRAG unsigned int ip_frag_size; /* fragment size */ unsigned int last_frag_extra; /* extra bytes possible in the last frag */ unsigned int ip_payload; unsigned int last_frag_offs; void* last_frag_start; int frg_no; #endif /* RAW_IPHDR_INC_AUTO_FRAG */ int ret; totlen = len + sizeof(hdr); if (unlikely(totlen) > 65535) return -2; memset(&snd_msg, 0, sizeof(snd_msg)); snd_msg.msg_name=&to->sin; snd_msg.msg_namelen=sockaddru_len(*to); snd_msg.msg_iov=&iov[0]; /* prepare the udp & ip headers */ mk_udp_hdr(&hdr.udp, &from->sin, &to->sin, (unsigned char*)buf, len, 1); mk_ip_hdr(&hdr.ip, &from->sin.sin_addr, &to->sin.sin_addr, len + sizeof(hdr.udp), IPPROTO_UDP); iov[0].iov_base=(char*)&hdr; iov[0].iov_len=sizeof(hdr); snd_msg.msg_iovlen=2; snd_msg.msg_control=0; snd_msg.msg_controllen=0; snd_msg.msg_flags=0; /* this part changes for different fragments */ /* packets are fragmented if mtu has a valid value (at least an IP header + UDP header fit in it) and if the total length is greater then the mtu */ #ifndef RAW_IPHDR_INC_AUTO_FRAG if (likely(totlen <= mtu || mtu <= sizeof(hdr))) { #endif /* RAW_IPHDR_INC_AUTO_FRAG */ iov[1].iov_base=buf; iov[1].iov_len=len; ret=sendmsg(rsock, &snd_msg, 0); #ifndef RAW_IPHDR_INC_AUTO_FRAG } else { ip_payload = len + sizeof(hdr.udp); /* a fragment offset must be a multiple of 8 => its size must also be a multiple of 8, except for the last fragment */ ip_frag_size = (mtu -sizeof(hdr.ip)) & (~7); last_frag_extra = (mtu - sizeof(hdr.ip)) & 7; /* rest */ frg_no = ip_payload / ip_frag_size + ((ip_payload % ip_frag_size) > last_frag_extra); /*ip_last_frag_size = ip_payload % frag_size + ((ip_payload % frag_size) <= last_frag_extra) * ip_frag_size; */ last_frag_offs = (frg_no - 1) * ip_frag_size; /* if we are here mtu => sizeof(ip_h+udp_h) && payload > mtu => last_frag_offs >= sizeof(hdr.udp) */ last_frag_start = buf + last_frag_offs - sizeof(hdr.udp); hdr.ip.ip_id = fastrand_max(65534) + 1; /* random id, should be != 0 (if 0 the kernel will fill it) */ /* send the first fragment */ iov[1].iov_base=buf; /* ip_frag_size >= sizeof(hdr.udp) because we are here only if mtu >= sizeof(hdr.ip) + sizeof(hdr.udp) */ iov[1].iov_len=ip_frag_size - sizeof(hdr.udp); hdr.ip.ip_len = RAW_IPHDR_IP_LEN(ip_frag_size + sizeof(hdr.ip)); hdr.ip.ip_off = RAW_IPHDR_IP_OFF(0x2000); /* set MF */ ret=sendmsg(rsock, &snd_msg, 0); if (unlikely(ret < 0)) goto end; /* all the other fragments, include only the ip header */ iov[0].iov_len = sizeof(hdr.ip); iov[1].iov_base = (char*)iov[1].iov_base + iov[1].iov_len; /* fragments between the first and the last */ while(unlikely(iov[1].iov_base < last_frag_start)) { iov[1].iov_len = ip_frag_size; hdr.ip.ip_len = RAW_IPHDR_IP_LEN(iov[1].iov_len + sizeof(hdr.ip)); /* set MF */ hdr.ip.ip_off = RAW_IPHDR_IP_OFF( (unsigned short) (((char*)iov[1].iov_base - (char*)buf + sizeof(hdr.udp)) / 8) | 0x2000 ); ret=sendmsg(rsock, &snd_msg, 0); if (unlikely(ret < 0)) goto end; iov[1].iov_base = (char*)iov[1].iov_base + iov[1].iov_len; } /* last fragment */ iov[1].iov_len = buf + len - (char*)iov[1].iov_base; hdr.ip.ip_len = RAW_IPHDR_IP_LEN(iov[1].iov_len + sizeof(hdr.ip)); /* don't set MF (last fragment) */ hdr.ip.ip_off = RAW_IPHDR_IP_OFF((unsigned short) (((char*)iov[1].iov_base - (char*)buf + sizeof(hdr.udp)) / 8) ); ret=sendmsg(rsock, &snd_msg, 0); if (unlikely(ret < 0)) goto end; } end: #endif /* RAW_IPHDR_INC_AUTO_FRAG */ return ret; } #endif /* USE_RAW_SOCKS */ kamailio-4.0.4/utils/0000755000000000000000000000000012223032460013135 5ustar rootrootkamailio-4.0.4/utils/protoshoot/0000755000000000000000000000000012223032460015355 5ustar rootrootkamailio-4.0.4/utils/protoshoot/README0000644000000000000000000000020012223032460016225 0ustar rootrootPROTOSHOOT ========== Command line tool to send data on network. Data is loaded from a file. See 'protoshoot -h' for options. kamailio-4.0.4/utils/protoshoot/protoshoot.c0000644000000000000000000002222612223032460017745 0ustar rootroot/* $Id$ */ /* * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * 2005-09-09 basic tcp support added (andrei) */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_SCTP #include #endif /* USE_SCTP */ #include #include static char *id="$Id$"; static char *version="protoflood 0.3"; static char* help_msg="\ Usage: udp_flood -f file -d address -p port -c count [-v]\n\ Options:\n\ -f file file with the content of the udp packet (max 65k)\n\ -d address destination address\n\ -p port destination port\n\ -c count number of packets to be sent\n\ -s usec microseconds to sleep before sending \"throttle\" packets\n\ -t throttle number of packets to send before sleeping\n\ -r sleep randomly up to -s usec packets (see -s) \n\ -T use tcp instead of udp \n\ -S use sctp instead of udp \n\ -1 use sctp in one to one mode \n\ -n no tcp connection number \n\ -R close the tcp connections with RST (SO_LINGER) \n\ -v increase verbosity level\n\ -V version number\n\ -h this help message\n\ "; #define BUF_SIZE 65535 enum protos { PROTO_NONE, PROTO_UDP, PROTO_TCP, PROTO_SCTP }; int main (int argc, char** argv) { int fd; int sock; char c; int n,r; char* tmp; char buf[BUF_SIZE]; struct hostent* he; struct sockaddr_in addr; int count; int verbose; char *fname; char *dst; int port; unsigned long usec; int throttle; int random_sleep; enum protos proto; int sctp_o2o; int tcp_rst; int con_no; int t; struct linger t_linger; int k; int err; /* init */ count=1; verbose=0; fname=0; dst="127.0.0.1"; port=5060; usec=0; throttle=0; random_sleep=0; proto=PROTO_UDP; tcp_rst=0; con_no=1; sctp_o2o=0; err=0; opterr=0; while ((c=getopt(argc,argv, "f:c:d:p:s:t:n:rTS1RvhV"))!=-1){ switch(c){ case 'f': fname=optarg; break; case 'v': verbose++; break; case 'd': dst=optarg; break; case 'p': port=strtol(optarg, &tmp, 10); if ((tmp==0)||(*tmp)){ fprintf(stderr, "bad port number: -p %s\n", optarg); goto error; } break; case 'c': count=strtol(optarg, &tmp, 10); if ((tmp==0)||(*tmp)){ fprintf(stderr, "bad count: -c %s\n", optarg); goto error; } break; case 's': usec=strtol(optarg, &tmp, 10); if ((tmp==0)||(*tmp)){ fprintf(stderr, "bad count: -c %s\n", optarg); goto error; } break; case 't': throttle=strtol(optarg, &tmp, 10); if ((tmp==0)||(*tmp)){ fprintf(stderr, "bad count: -c %s\n", optarg); goto error; } break; case 'n': con_no=strtol(optarg, &tmp, 10); if ((tmp==0)||(*tmp)||(con_no<1)){ fprintf(stderr, "bad count: -c %s\n", optarg); goto error; } break; case 'r': random_sleep=1; break; case 'T': proto=PROTO_TCP; break; case 'S': #ifdef USE_SCTP proto=PROTO_SCTP; #else fprintf(stderr, "sctp not supported (recompile with " "-DUSE_SCTP)\n"); goto error; #endif /* USE_SCTP */ break; case '1': sctp_o2o=1; break; case 'R': tcp_rst=1; break; case 'V': printf("version: %s\n", version); printf("%s\n",id); exit(0); break; case 'h': printf("version: %s\n", version); printf("%s", help_msg); exit(0); break; case '?': if (isprint(optopt)) fprintf(stderr, "Unknown option `-%c´\n", optopt); else fprintf(stderr, "Unknown character `\\x%x´\n", optopt); goto error; case ':': fprintf(stderr, "Option `-%c´ requires an argument.\n", optopt); goto error; break; default: abort(); } } /* check if all the required params are present */ if (fname==0){ fprintf(stderr, "Missing -f file\n"); exit(-1); } if (dst==0){ fprintf(stderr, "Missing destination (-d ...)\n"); exit(-1); } if(port==0){ fprintf(stderr, "Missing port number (-p port)\n"); exit(-1); }else if(port<0){ fprintf(stderr, "Invalid port number (-p %d)\n", port); exit(-1); } if(count==0){ fprintf(stderr, "Missing packet count (-c number)\n"); exit(-1); }else if(count<0){ fprintf(stderr, "Invalid packet count (-c %d)\n", count); exit(-1); } if (proto==PROTO_UDP || (proto==PROTO_SCTP && !sctp_o2o)) con_no=1; /* ignore sigpipe */ if (signal(SIGPIPE, SIG_IGN)==SIG_ERR){ fprintf(stderr, "failed to ignore SIGPIPE: %s\n", strerror(errno)); exit(-1); } /* open packet file */ fd=open(fname, O_RDONLY); if (fd<0){ fprintf(stderr, "ERROR: loading packet-file(%s): %s\n", fname, strerror(errno)); goto error; } n=read(fd, buf, BUF_SIZE); if (n<0){ fprintf(stderr, "ERROR: reading file(%s): %s\n", fname, strerror(errno)); goto error; } if (verbose) printf("read %d bytes from file %s\n", n, fname); close(fd); /* resolve destination */ he=gethostbyname(dst); if (he==0){ fprintf(stderr, "ERROR: could not resolve %s\n", dst); goto error; } /* open socket*/ addr.sin_family=he->h_addrtype; addr.sin_port=htons(port); #ifdef HAVE_SOCKADDR_SA_LEN addr.sin_len=sizeof(struct sockaddr_in); #endif memcpy(&addr.sin_addr.s_addr, he->h_addr_list[0], he->h_length); for (k=0; kh_addrtype, SOCK_DGRAM, 0); break; case PROTO_TCP: sock = socket(he->h_addrtype, SOCK_STREAM, 0); break; #ifdef USE_SCTP case PROTO_SCTP: sock = socket(he->h_addrtype, sctp_o2o?SOCK_STREAM:SOCK_SEQPACKET, IPPROTO_SCTP); break; #endif /* USE_SCTP */ default: fprintf(stderr, "BUG: unkown proto %d\n", proto); goto error; } if (sock==-1){ fprintf(stderr, "ERROR: socket: %s\n", strerror(errno)); goto error; } if (proto==PROTO_TCP){ t=1; if (setsockopt(sock, IPPROTO_TCP , TCP_NODELAY, &t, sizeof(t))<0){ fprintf(stderr, "ERROR: could not disable Nagle: %s\n", strerror(errno)); } if (tcp_rst){ t_linger.l_onoff=1; t_linger.l_linger=0; if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &t_linger, sizeof(t_linger))<0){ fprintf(stderr, "ERROR: could not set SO_LINGER: %s\n", strerror(errno)); } } } #ifdef USE_SCTP else if (proto==PROTO_SCTP){ t=1; if (setsockopt(sock, IPPROTO_SCTP, SCTP_NODELAY, &t, sizeof(t))<0){ fprintf(stderr, "ERROR: could not disable Nagle: %s\n", strerror(errno)); } } #endif /* USE_SCTP */ if ( #ifdef USE_SCTP (proto!=PROTO_SCTP || sctp_o2o) && #endif /* USE_SCTP */ (connect(sock, (struct sockaddr*) &addr, sizeof(struct sockaddr))!=0)){ fprintf(stderr, "ERROR: connect: %s\n", strerror(errno)); goto error; } /* flood loop */ t=throttle; for (r=0; r1)&&((r%1000)==999)){ putchar('.'); fflush(stdout); } #ifdef USE_SCTP if (proto==PROTO_SCTP && !sctp_o2o){ if (sctp_sendmsg(sock, buf, n, (struct sockaddr*) &addr, sizeof(struct sockaddr), 0, SCTP_UNORDERED, 0, 0, 0)==-1){ fprintf(stderr, "Error(%d): send: %s\n", err, strerror(errno)); err++;; } }else #endif /* USE_SCTP */ { if (send(sock, buf, n, 0)==-1) { fprintf(stderr, "Error(%d): send: %s\n", err, strerror(errno)); err++;; } } if (usec){ t--; if (t==0){ usleep(random_sleep? (unsigned long)((double)usec*rand()/RAND_MAX):usec); t=throttle; } } } close(sock); if ((verbose) && (k%1000==999)) { putchar('#'); fflush(stdout); } } if (proto==PROTO_TCP || proto==PROTO_SCTP){ printf("\n%d packets sent on %d %s connections (%d on each of them)," " %d bytes each => total %d bytes\n", count*con_no-err, con_no, (proto==PROTO_TCP)?"tcp":"sctp", count, n, (con_no*count-err)*n); }else{ printf("\n%d packets sent, %d bytes each => total %d bytes\n", count-err, n, n*(count-err)); } if (err) printf("%d errors\n", err); exit(0); error: fprintf(stderr, "exiting due to error (%s)\n", strerror(errno)); exit(-1); } kamailio-4.0.4/utils/protoshoot/Makefile0000644000000000000000000000046612223032460017023 0ustar rootroot# COREPATH=../.. include $(COREPATH)/Makefile.defs include $(COREPATH)/Makefile.targets auto_gen= NAME=protoshoot RELEASE=0.2 ifneq (,$(findstring -DUSE_SCTP,$(C_DEFS))) SCTP=1 endif ifeq ($(SCTP),1) ifeq ($(OS), linux) LIBS+= -lsctp endif endif include $(COREPATH)/Makefile.utils $(NAME).o: modules: kamailio-4.0.4/utils/fifo_relay/0000755000000000000000000000000012223032460015254 5ustar rootrootkamailio-4.0.4/utils/fifo_relay/fifo_server.php0000644000000000000000000001206312223032460020300 0ustar rootroot#!/usr/bin/php4 -q $mtime) { $fd = fopen($fifo_clients, "r"); if ($fd == FALSE) { echo "Cannot open fifo.clients file!\n"; return FALSE; } $clients = array(); while (!feof ($fd)) { $client = ip2long(fgets($fd, 4096)); if ($client != -1) { $clients[] = $client; } } fclose ($fd); $mtime = $cur_mtime; } return in_array($long_addr, $clients, TRUE); } if (($sock = socket_create (AF_INET, SOCK_STREAM, 0)) < 0) { echo "socket_create() failed: " . socket_strerror ($sock) . "\n"; return; } if (($ret = socket_bind($sock, $fifo_server_address, $fifo_server_port)) < 0) { echo "socket_bind() failed: " . socket_strerror ($ret) . "\n"; socket_close($sock); return; } if (($ret = socket_listen ($sock, 5)) < 0) { echo "socket_listen() failed: " . socket_strerror ($ret) . "\n"; socket_close($sock); return; } do { if (($msgsock = socket_accept($sock)) < 0) { echo "socket_accept() failed: ".socket_strerror($msgsock)."\n"; socket_close($msgsock); continue; } socket_getpeername($msgsock, $addr); if (!fifo_allow($fifo_clients, $addr)) { $msg = "403 Forbidden\n"; socket_write($msgsock, $msg, strlen($msg)); socket_shutdown($msgsock); socket_close($msgsock); continue; } if (FALSE === ($fifo_cmd = socket_read ($msgsock, 8192, PHP_BINARY_READ))) { echo "socket_read() failed: ".socket_strerror(socket_last_error($msgsock))."\n"; socket_shutdown($msgsock); socket_close($msgsock); continue; } $fifo_reply_file_name = "ser_fifo_reply_".rand(); $fifo_reply_file = "/tmp/".$fifo_reply_file_name; $fifo_cmd = str_replace("REPLY_FILE_NAME", $fifo_reply_file_name, $fifo_cmd); /* add command separator */ $fifo_cmd=$fifo_cmd."\n"; $fifo_handle=fopen( "/tmp/ser_fifo", "w" ); if (!$fifo_handle) { $msg = "sorry -- cannot open write fifo"; socket_write($msgsock, $msg, strlen($msg)); socket_shutdown($msgsock); socket_close($msgsock); continue; } /* create fifo for replies */ @system("mkfifo -m 666 ".$fifo_reply_file); /* write fifo command */ if (fwrite( $fifo_handle, $fifo_cmd) == -1) { @unlink($fifo_reply_file); @fclose($fifo_handle); $msg = "sorry -- fifo writing error"; socket_write($msgsock, $msg, strlen($msg)); socket_shutdown($msgsock); socket_close($msgsock); continue; } @fclose($fifo_handle); /* read output now */ @$fp = fopen($fifo_reply_file, "r"); if (!$fp) { @unlink($fifo_reply_file); $msg = "sorry -- reply fifo opening error"; socket_write($msgsock, $msg, strlen($msg)); socket_shutdown($msgsock); socket_close($msgsock); continue; } $status = fgetS($fp, 256); if (!$status) { @unlink($fifo_reply_file); $msg = "sorry -- reply fifo reading error"; socket_write($msgsock, $msg, strlen($msg)); socket_shutdown($msgsock); socket_close($msgsock); continue; } socket_write($msgsock, $status, strlen($status)); $rest = fread($fp, 8192); @unlink($fifo_reply_file); socket_write($msgsock, $rest, strlen($rest)); socket_close ($msgsock); } while (true); socket_close ($sock); ?> kamailio-4.0.4/utils/db_oracle/0000755000000000000000000000000012223032460015047 5ustar rootrootkamailio-4.0.4/utils/db_oracle/selcon.c0000644000000000000000000000461012223032460016477 0ustar rootroot#include "orasel.h" //----------------------------------------------------------------------------- void open_sess(con_t* con) { sword status; if ( OCIEnvCreate(&con->envhp, OCI_DEFAULT | OCI_NEW_LENGTH_SEMANTICS, NULL, NULL, NULL, NULL, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->errhp, OCI_HTYPE_ERROR, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->srvhp, OCI_HTYPE_SERVER, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->svchp, OCI_HTYPE_SVCCTX, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->authp, OCI_HTYPE_SESSION, 0, NULL) != OCI_SUCCESS || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->stmthp, OCI_HTYPE_STMT, 0, NULL) != OCI_SUCCESS) { errxit("no oracle memory left"); } status = OCIAttrSet(con->svchp, OCI_HTYPE_SVCCTX, con->srvhp, 0, OCI_ATTR_SERVER, con->errhp); if (status != OCI_SUCCESS) goto connect_err; status = OCIAttrSet(con->authp, OCI_HTYPE_SESSION, (text*)con->username->s, con->username->len, OCI_ATTR_USERNAME, con->errhp); if (status != OCI_SUCCESS) goto connect_err; status = OCIAttrSet(con->authp, OCI_HTYPE_SESSION, (text*)con->password->s, con->password->len, OCI_ATTR_PASSWORD, con->errhp); if (status != OCI_SUCCESS) goto connect_err; status = OCIAttrSet(con->svchp, OCI_HTYPE_SVCCTX, con->authp, 0, OCI_ATTR_SESSION, con->errhp); if (status != OCI_SUCCESS) goto connect_err; status = OCIServerAttach(con->srvhp, con->errhp, (OraText*)con->uri->s, con->uri->len, 0); if (status != OCI_SUCCESS) goto connect_err; status = OCISessionBegin(con->svchp, con->errhp, con->authp, OCI_CRED_RDBMS, OCI_DEFAULT); if (status != OCI_SUCCESS) { connect_err: oraxit(status, con); } } //----------------------------------------------------------------------------- void send_req(con_t* con, const Str* req) { sword status; status = OCIStmtPrepare(con->stmthp, con->errhp, (text*)req->s, req->len, OCI_NTV_SYNTAX, OCI_DEFAULT); if (status != OCI_SUCCESS) goto request_err; status = OCIStmtExecute(con->svchp, con->stmthp, con->errhp, 0, 0, NULL, NULL, OCI_STMT_SCROLLABLE_READONLY); if (status != OCI_SUCCESS) { request_err: fprintf(stderr, "%.*s\n", req->len, req->s); oraxit(status, con); } } //----------------------------------------------------------------------------- kamailio-4.0.4/utils/db_oracle/orasel.c0000644000000000000000000000542412223032460016505 0ustar rootroot#include "orasel.h" #include #include outmode_t outmode; //----------------------------------------------------------------------------- static void prepare_uri(con_t* con, const char *uri) { const char *p = strchr(uri, '/'); if(!p || p == uri) goto bad; con->username = str_alloc(uri, p - uri); uri = p+1; p = strchr(uri, '@'); if(!p || p == uri) goto bad; con->password = str_alloc(uri, p - uri); if(strchr(con->password->s, '/')) goto bad; if(!*++p) goto bad; con->uri = str_alloc(p, strlen(p)); return; bad: errxit("invalid db (must be as name/password@dbname)"); } //----------------------------------------------------------------------------- static const Str* prepare_req(const char* req) { Str* ps; char *p; while(*req && isspace((unsigned char)*req)) ++req; if(strncasecmp(req, "select", sizeof("select")-1)) goto bad; p = (char*)req + sizeof("select")-1; if(!*p || !isspace((unsigned char)*p)) goto bad; ps = str_alloc(req, strlen(req)); p = (char*) ps->s + sizeof("select")-1; do if(isspace((unsigned char)*p)) *p = ' '; while(*++p); do --p; while(isspace((unsigned char)*p)); if(*p != ';') goto bad; do { do --p; while(isspace((unsigned char)*p)); }while(*p == ';'); *++p = '\0'; ps->len = p - ps->s; if(ps->len <= sizeof("select")-1) { bad: errxit("support only 'select ...;' request"); } return ps; } //----------------------------------------------------------------------------- static void get_opt(int argc, char* argv[]) { int opt = 0; if(argc <= 1 || (argc == 2 && !strcmp(argv[1], "--help"))) { help: fprintf(stderr, "Kamailio for oracle 'select' request utility\n"); opt = -2; /* flag for help print */ } else { while((opt = getopt(argc-1, argv+1, "BLNe:")) != -1) { switch(opt) { case 'B': outmode.raw = 1;; break; case 'L': outmode.hdr = 1; break; case 'N': outmode.emp = 1; break; case 'e': if(optind == argc-1) return; // pass thru default: goto help; } } } fprintf(stderr, "use: %s user/password@db [-BLN] -e \"select ...;\"\n", argv[0]); if(opt == -2) { fprintf(stderr, " where -B - print using tab separator\n"); fprintf(stderr, " -L - skip column headers\n"); fprintf(stderr, " -N - skip notify of empty result\n"); } exit(1); } //----------------------------------------------------------------------------- int main(int argc, char* argv[]) { con_t con; res_t res; const Str* req; get_opt(argc, argv); memset(&con, 0, sizeof(con)); memset(&res, 0, sizeof(res)); prepare_uri(&con, argv[1]); req = prepare_req(optarg); open_sess(&con); send_req(&con, req); get_res(&con, &res); OCITerminate(OCI_DEFAULT); out_res(&res); return 0; } //----------------------------------------------------------------------------- kamailio-4.0.4/utils/db_oracle/orasel.h0000644000000000000000000000213112223032460016502 0ustar rootroot#ifndef __orasel_h__ #define __orasel_h__ #include #include #include #include #include typedef struct { unsigned len; char s[]; }Str; typedef struct { const Str* username; const Str* password; const Str* uri; OCIError* errhp; OCISvcCtx* svchp; OCIEnv* envhp; OCISession* authp; OCIServer* srvhp; OCIStmt* stmthp; }con_t; typedef struct { Str** names; Str*** rows; unsigned char* types; unsigned col_n; unsigned row_n; }res_t; void __attribute__((noreturn)) donegood(const char *msg); void __attribute__((noreturn)) errxit(const char *msg); void __attribute__((noreturn)) oraxit(sword status, const con_t* con); void* safe_malloc(size_t sz); Str* str_alloc(const char *s, size_t len); void open_sess(con_t* con); void send_req(con_t* con, const Str* req); void get_res(const con_t* con, res_t* _r); void out_res(const res_t* _r); typedef struct { unsigned raw : 1, hdr : 1, emp : 1; }outmode_t; extern outmode_t outmode; #endif kamailio-4.0.4/utils/db_oracle/outres.c0000644000000000000000000000301312223032460016531 0ustar rootroot#include "orasel.h" //----------------------------------------------------------------------------- static void out_delim(const unsigned* pl, unsigned nc) { unsigned i; for(i = 0; i < nc; i++) { unsigned j = pl[i] + 2; putchar('+'); do putchar('-'); while(--j); } printf("+\n"); } //----------------------------------------------------------------------------- void out_res(const res_t* _r) { unsigned* pl = NULL; unsigned nc = _r->col_n, nr = _r->row_n, i, j; Str** ps = _r->names; if(!outmode.raw) { pl = safe_malloc(nc * sizeof(unsigned)); for(i = 0; i < nc; i++) pl[i] = ps[i]->len; for(j = 0; j < nr; j++) { ps = _r->rows[j]; for(i = 0; i < nc; i++) if(pl[i] < ps[i]->len) pl[i] = ps[i]->len; } out_delim(pl, nc); } if(!outmode.hdr) { ps = _r->names; for(i = 0; i < nc; i++) { if(!outmode.raw) { printf("| %-*.*s ", pl[i], ps[i]->len, ps[i]->s); } else { if(i) putchar('\t'); printf("%.*s", ps[i]->len, ps[i]->s); } } if(outmode.raw) putchar('\n'); else { printf("|\n"); out_delim(pl, nc); } } for(j = 0; j < nr; j++) { ps = _r->rows[j]; if(!outmode.raw) { for(i = 0; i < nc; i++) printf(_r->types[i] ? "| %-*.*s " : "| %*.*s ", pl[i], ps[i]->len, ps[i]->s); printf("|\n"); } else { for(i = 0; i < nc; i++) { if(i) putchar('\t'); printf("%.*s", ps[i]->len, ps[i]->s); } putchar('\n'); } } if(!outmode.raw) out_delim(pl, nc); } //----------------------------------------------------------------------------- kamailio-4.0.4/utils/db_oracle/util.c0000644000000000000000000000415512223032460016175 0ustar rootroot#include "orasel.h" //----------------------------------------------------------------------------- void __attribute__((noreturn)) donegood(const char *msg) { OCITerminate(OCI_DEFAULT); if (msg && !outmode.emp) printf("%s\n", msg); exit(0); } //----------------------------------------------------------------------------- void __attribute__((noreturn)) errxit(const char *msg) { OCITerminate(OCI_DEFAULT); fprintf(stderr, "ERROR: %s\n", msg); exit(1); } //----------------------------------------------------------------------------- void __attribute__((noreturn)) oraxit(sword status, const con_t* con) { const char *p = NULL; char buf[512]; sword ecd; switch (status) { case OCI_SUCCESS_WITH_INFO: case OCI_ERROR: ecd = 0; if(OCIErrorGet(con->errhp, 1, NULL, &ecd, (OraText*)buf, sizeof(buf), OCI_HTYPE_ERROR) != OCI_SUCCESS) { snprintf(buf, sizeof(buf), "unknown ORAERR %u", ecd); } break; default: snprintf(buf, sizeof(buf), "unknown status %u", status); break; case OCI_SUCCESS: p = "success"; break; case OCI_NEED_DATA: p = "need data"; break; case OCI_NO_DATA: p = "no data"; break; case OCI_INVALID_HANDLE: p = "invalid handle"; break; case OCI_STILL_EXECUTING: /* ORA-3123 */ p = "executing"; break; case OCI_CONTINUE: p = "continue"; break; } if (p) { snprintf(buf, sizeof(buf), "logic error (%s)", p); } errxit(buf); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- static void __attribute__((noreturn)) nomem(void) { errxit("no enough memory"); } //----------------------------------------------------------------------------- void* safe_malloc(size_t sz) { void *p = malloc(sz); if (!p) nomem(); return p; } //----------------------------------------------------------------------------- Str* str_alloc(const char *s, size_t len) { Str* ps = (Str*)safe_malloc(sizeof(Str) + len + 1); ps->len = len; memcpy(ps->s, s, len); ps->s[len] = '\0'; return ps; } //----------------------------------------------------------------------------- kamailio-4.0.4/utils/db_oracle/getres.c0000644000000000000000000001742512223032460016515 0ustar rootroot#include "orasel.h" #include #include /* * Uncomment next string if you will sell 'NULL' on unitialized NON text field */ //#define NULL_ID "NULL" static char st_buf[65536]; enum type_t { DB_STR = 0, DB_DATETIME, /* end of left alignment */ DB_INT, DB_BITMAP, DB_DOUBLE /* MUST belast */ }; //--------------------------------------------------------- struct dmap { OCIDefine** defh; union { dvoid* v; double* f; int* i; char* c; OCIDate* o; }* pv; dvoid** pval; ub2* ilen; sb2* ind; ub2* len; }; typedef struct dmap dmap_t; //----------------------------------------------------------------------------- static void dmap_init(dmap_t* _d, unsigned n) { size_t sz = sizeof(*_d->defh) + sizeof(*_d->pv) + sizeof(*_d->pval) + sizeof(*_d->ilen) + sizeof(*_d->ind) + sizeof(*_d->len); unsigned char *p = safe_malloc(sz * n); _d->defh = (void*)p; p += n*sizeof(*_d->defh); _d->pv = (void*)p; p += n*sizeof(*_d->pv); _d->pval = (void*)p; p += n*sizeof(*_d->pval); _d->ilen = (void*)p; p += n*sizeof(*_d->ilen); _d->ind = (void*)p; p += n*sizeof(*_d->ind); _d->len = (void*)p; // p += n*sizeof(*_d->len); } //----------------------------------------------------------------------------- /* * Get and convert columns from a result. Define handlers and buffers */ static void get_columns(const con_t* con, res_t* _r, dmap_t* _d) { OCIParam *param; size_t tsz; ub4 i, n; sword status; status = OCIAttrGet(con->stmthp, OCI_HTYPE_STMT, &n, NULL, OCI_ATTR_PARAM_COUNT, con->errhp); if (status != OCI_SUCCESS) oraxit(status, con); if (!n) donegood("Empty table"); dmap_init(_d, n); _r->names = (Str**)safe_malloc(n * sizeof(Str*)); _r->types = (unsigned char*)safe_malloc(n * sizeof(unsigned char)); _r->col_n = n; tsz = 0; memset(_d->defh, 0, sizeof(_d->defh[0]) * n); for (i = 0; i < n; i++) { ub4 len; ub2 dtype; unsigned char ctype = DB_DOUBLE; status = OCIParamGet(con->stmthp, OCI_HTYPE_STMT, con->errhp, (dvoid**)(dvoid*)¶m, i+1); if (status != OCI_SUCCESS) goto ora_err; { text *name; status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&name, &len, OCI_ATTR_NAME, con->errhp); if (status != OCI_SUCCESS) goto ora_err; _r->names[i] = str_alloc((char*)name, len); } status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&dtype, NULL, OCI_ATTR_DATA_TYPE, con->errhp); if (status != OCI_SUCCESS) goto ora_err; switch (dtype) { case SQLT_UIN: set_bitmap: ctype = DB_BITMAP; len = sizeof(unsigned); break; case SQLT_INT: set_int: ctype = DB_INT; len = sizeof(int); break; case SQLT_VNU: case SQLT_NUM: len = 0; /* PRECISION is ub1 (byte) */ status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&len, NULL, OCI_ATTR_PRECISION, con->errhp); if (status != OCI_SUCCESS) goto ora_err; if (len <= 11) { sb1 sc; status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&sc, NULL, OCI_ATTR_SCALE, con->errhp); if (status != OCI_SUCCESS) goto ora_err; if (!sc) { dtype = SQLT_INT; if (len != 11) goto set_int; dtype = SQLT_UIN; goto set_bitmap; } if(sc < 0) sc = 0; ctype += sc; } case SQLT_FLT: case SQLT_BFLOAT: case SQLT_BDOUBLE: case SQLT_IBFLOAT: case SQLT_IBDOUBLE: case SQLT_PDN: len = sizeof(double); dtype = SQLT_FLT; break; case SQLT_DATE: case SQLT_DAT: case SQLT_ODT: case SQLT_TIMESTAMP: case SQLT_TIMESTAMP_TZ: case SQLT_TIMESTAMP_LTZ: ctype = DB_DATETIME; len = sizeof(OCIDate); dtype = SQLT_ODT; break; case SQLT_CLOB: case SQLT_BLOB: case SQLT_CHR: case SQLT_STR: case SQLT_VST: case SQLT_VCS: case SQLT_AFC: case SQLT_AVC: ctype = DB_STR; dtype = SQLT_CHR; len = 0; /* DATA_SIZE is ub2 (word) */ status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid**)(dvoid*)&len, NULL, OCI_ATTR_DATA_SIZE, con->errhp); if (status != OCI_SUCCESS) goto ora_err; ++len; break; default: errxit("unsupported datatype"); } _r->types[i] = ctype; _d->ilen[i] = (ub2)len; _d->pv[i].v = st_buf + tsz; tsz += len; status = OCIDefineByPos(con->stmthp, &_d->defh[i], con->errhp, i+1, _d->pv[i].v, len, dtype, &_d->ind[i], &_d->len[i], NULL, OCI_DEFAULT); if (status != OCI_SUCCESS) goto ora_err; } if (tsz > sizeof(st_buf)) errxit("too large row"); return; ora_err: oraxit(status, con); } //----------------------------------------------------------------------------- /* * Convert data fron db format to internal format */ static void convert_row(const res_t* _res, Str*** _r, const dmap_t* _d) { unsigned i, n = _res->col_n; Str** v; *_r = v = (Str**)safe_malloc(n * sizeof(Str**)); for (i = 0; i < n; i++, v++) { char buf[64]; unsigned char t = _res->types[i]; if (_d->ind[i] == -1) { static const struct { unsigned len; char s[1]; }_empty = { 0, "" }; #ifdef NULL_ID static const struct { unsigned len; char s[sizeof(NULL_ID)]; }_null = { sizeof(NULL_ID)-1, NULL_ID }; *v = (Str*)&_null; if (t != DB_STR) continue; #endif *v = (Str*)&_empty; continue; } // if (_d->ind[i]) errxit("truncated value in DB"); switch (t) { case DB_STR: *v = str_alloc(_d->pv[i].c, _d->len[i]); break; case DB_INT: *v = str_alloc(buf, snprintf(buf, sizeof(buf), "%i", *_d->pv[i].i)); break; case DB_BITMAP: *v = str_alloc(buf, snprintf(buf, sizeof(buf), "0x%X", *_d->pv[i].i)); break; case DB_DATETIME: { struct tm tm; memset(&tm, 0, sizeof(tm)); OCIDateGetTime(_d->pv[i].o, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); OCIDateGetDate(_d->pv[i].o, &tm.tm_year, &tm.tm_mon, &tm.tm_mday); if (tm.tm_mon) --tm.tm_mon; if (tm.tm_year >= 1900) tm.tm_year -= 1900; *v = str_alloc(buf, strftime(buf, sizeof(buf), "%d-%b-%Y %T", &tm)); } break; case DB_DOUBLE: *v = str_alloc(buf, snprintf(buf, sizeof(buf), "%g", *_d->pv[i].f)); break; default: { double x = fabs(*_d->pv[i].f); const char *fmt = "%.*f"; if (x && (x >= 1.0e6 || x < 1.0e-5)) fmt = "%.*e"; *v = str_alloc(buf, snprintf(buf, sizeof(buf), fmt, (t - DB_DOUBLE), *_d->pv[i].f)); } break; } } } //----------------------------------------------------------------------------- /* * Get rows and convert it from oracle to db API representation */ static void get_rows(const con_t* con, res_t* _r, dmap_t* _d) { ub4 rcnt; sword status; unsigned n = _r->col_n; memcpy(_d->len, _d->ilen, sizeof(_d->len[0]) * n); status = OCIStmtFetch2(con->stmthp, con->errhp, 1, OCI_FETCH_LAST, 0, OCI_DEFAULT); if (status != OCI_SUCCESS) { if (status == OCI_NO_DATA) donegood("Empty set"); goto ora_err; } status = OCIAttrGet(con->stmthp, OCI_HTYPE_STMT, &rcnt, NULL, OCI_ATTR_CURRENT_POSITION, con->errhp); if (status != OCI_SUCCESS) goto ora_err; if (!rcnt) errxit("lastpos==0"); _r->row_n = rcnt; _r->rows = (Str***)safe_malloc(rcnt * sizeof(Str**)); while ( 1 ) { convert_row(_r, &_r->rows[--rcnt], _d); if (!rcnt) return; memcpy(_d->len, _d->ilen, sizeof(_d->len[0]) * n); status = OCIStmtFetch2(con->stmthp, con->errhp, 1, OCI_FETCH_PRIOR, 0, OCI_DEFAULT); if (status != OCI_SUCCESS) break; } ora_err: oraxit(status, con); } //----------------------------------------------------------------------------- /* * Read database answer and fill the structure */ void get_res(const con_t* con, res_t* _r) { dmap_t dmap; unsigned n; unsigned char *pt; get_columns(con, _r, &dmap); get_rows(con, _r, &dmap); n = _r->col_n; pt = _r->types; do { --n; assert(DB_STR == 0 && DB_DATETIME == 1); pt[n] = (pt[n] <= DB_DATETIME); }while(n); } //----------------------------------------------------------------------------- kamailio-4.0.4/utils/db_oracle/Makefile0000644000000000000000000000274712223032460016521 0ustar rootroot# $Id$ # # db_orasel Makefile # include ../../Makefile.defs auto_gen= NAME=kamailio_orasel include ../../Makefile.sources ORAPATH= # use for multiple client sdk version install ifneq ($(ORAVERSION),) ORAVERDIR=/$(ORAVERSION) endif # use include/library path's for full client installation ifneq ($(ORAHOME),) DEFS +=-I$(ORAHOME)/include LIBS +=-L$(ORAHOME)/lib ifeq ($(ORAPATH),) ORAPATH=$(ORAHOME)/lib endif else # use standard know paths oci.h locations (linux) DEFS +=-I$(LOCALBASE)/include/oracle$(ORAVERDIR) \ -I$(SYSBASE)/include/oracle$(ORAVERDIR) endif # search 'so' path if it non standard (possible liboclntsh locations on linux) ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(LOCALBASE)/lib64/oracle$(ORAVERDIR)/libocci.so ] && \ echo $(LOCALBASE)/lib64/oracle$(ORAVERDIR) ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(SYSBASE)/lib64/oracle$(ORAVERDIR)/libocci.so ] && \ echo $(SYSBASE)/lib64/oracle$(ORAVERDIR) ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(SYSBASE)/lib/oracle$(ORAVERDIR)/libocci.so ] && \ echo $(SYSBASE)/lib/oracle$(ORAVERDIR) ) endif ifeq ($(ORAPATH),) ORAPATH=$(shell [ -f $(SYSBASE)/lib/oracle$(ORAVERDIR)/libocci.so ] && \ echo $(SYSBASE)/lib/oracle$(ORAVERDIR) ) endif ifneq ($(ORAPATH),) LIBS +=-L$(ORAPATH) endif LIBS +=-locci -lclntsh #DEFS +=-DLINUX -D_GNU_SOURCE -D_REENTRANT #LIBS +=-lpthread ifneq ($(ORAPATH),) LIBS +=-Wl,-rpath $(ORAPATH) endif include ../../Makefile.rules modules: kamailio-4.0.4/utils/kamunix/0000755000000000000000000000000012223032460014611 5ustar rootrootkamailio-4.0.4/utils/kamunix/kamunix.c0000644000000000000000000000655312223032460016442 0ustar rootroot/* * $Id$ * * Copyright (C) 2004 FhG FOKUS * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include /* AF_LOCAL is not defined on solaris */ #if !defined(AF_LOCAL) #define AF_LOCAL AF_UNIX #endif #if !defined(PF_LOCAL) #define PF_LOCAL PF_UNIX #endif /* solaris doesn't have SUN_LEN */ #ifndef SUN_LEN #define SUN_LEN(sa) ( strlen((sa)->sun_path) + \ (size_t)(((struct sockaddr_un*)0)->sun_path) ) #endif #define BUF_SIZE 65536 #define DEFAULT_TIMEOUT 5 int main(int argc, char** argv) { int sock, len; socklen_t from_len; struct sockaddr_un from, to; char name[256]; static char buffer[BUF_SIZE]; char *chroot_dir; if (argc != 2) { printf("Usage: %s \n", argv[0]); return 1; } sock = socket(PF_LOCAL, SOCK_DGRAM, 0); if (sock == -1) { fprintf(stderr, "Error while opening socket: %s\n", strerror(errno)); return -1; } memset(&from, 0, sizeof(from)); from.sun_family = PF_LOCAL; chroot_dir = getenv("CHROOT_DIR"); if (chroot_dir == NULL) chroot_dir = ""; sprintf(name, "%s/tmp/Kamailio.%d.XXXXXX", chroot_dir, getpid()); umask(0); /* set mode to 0666 for when Kamailio is running as non-root user and kamctl is running as root */ if (mkstemp(name) == -1) { fprintf(stderr, "Error in mkstemp with name=%s: %s\n", name, strerror(errno)); return -2; } if (unlink(name) == -1) { fprintf(stderr, "Error in unlink of %s: %s\n", name, strerror(errno)); return -2; } strncpy(from.sun_path, name, strlen(name)); if (bind(sock, (struct sockaddr*)&from, SUN_LEN(&from)) == -1) { fprintf(stderr, "Error in bind: %s\n", strerror(errno)); goto err; } memset(&to, 0, sizeof(to)); to.sun_family = PF_LOCAL; strncpy(to.sun_path, argv[1], sizeof(to.sun_path) - 1); len = fread(buffer, 1, BUF_SIZE, stdin); if (len) { if (sendto(sock, buffer, len, 0, (struct sockaddr*)&to, SUN_LEN(&to)) == -1) { fprintf(stderr, "Error in sendto: %s\n", strerror(errno)); goto err; } from_len = sizeof(from); len = recvfrom(sock, buffer, BUF_SIZE, 0, (struct sockaddr*)&from, &from_len); if (len == -1) { fprintf(stderr, "Error in recvfrom: %s\n", strerror(errno)); goto err; } fprintf(stdout, "%.*s", len, buffer); } else { fprintf(stderr, "Nothing to send\n"); goto err; } close(sock); if (unlink(name) == -1) fprintf(stderr, "Error in unlink of %s: %s\n", name, strerror(errno)); return 0; err: close(sock); if (unlink(name) == -1) fprintf(stderr, "Error in unlink of %s: %s\n", name, strerror(errno)); return -1; } kamailio-4.0.4/utils/kamunix/kamunix.80000644000000000000000000000235412223032460016362 0ustar rootroot.\" $Id$ .TH kamunix 8 21.06.2006 kamailio "Kamailio" .\" Process with .\" groff -man -Tascii kamunix.8 .\" .SH NAME kamunix \- Kamailio UNIX socket wrapper .SH SYNOPSIS .B kamunix .BI path_to_socket .SH DESCRIPTION .B kamunix is a wrapper for sending external commands .B to Kamailio SIP server via UNIX sockets interface. .br This is a binary alternative to the textual FIFO interface. .PP .B kamunix reads from standard input one .B Kamailio command along with its paramters (if any) and prints the response to standard output. .SH PARAMETERS .TP 3 .B path_to_socket full path of the UNIX socket file used by Kamailio to receive the external commands .SH EXAMPLES .PP An Kamailio commands consists in one ore more lines: first contains the command name enclosed between ":", the following lines containing the command parameters, one per line. .PP echo ":uptime:" | kamunix /tmp/kamailio.sock .SH AUTHORS see .B /usr/share/doc/kamailio/AUTHORS .SH SEE ALSO .BR kamailio(8), kamailio.cfg(5), kamctl(8) .PP Full documentation on Kamailio is available at .I http://www.kamailio.org/. .PP Mailing lists: .nf users@lists.kamailio.org - Kamailio user community .nf devel@lists.kamailio.org - Kamailio development, new features and unstable version kamailio-4.0.4/utils/kamunix/Makefile0000644000000000000000000000023512223032460016251 0ustar rootroot# $Id$ # # kamunix Makefile # include ../../Makefile.defs auto_gen= NAME=kamunix include ../../Makefile.sources include ../../Makefile.rules modules: kamailio-4.0.4/utils/db_berkeley/0000755000000000000000000000000012223032460015404 5ustar rootrootkamailio-4.0.4/utils/db_berkeley/kambdb_recover.c0000644000000000000000000004473312223032460020530 0ustar rootroot/* * $Id$ * * recovery for berkeley_db module * Copyright (C) 2007 Cisco Systems * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #include #include "kambdb_recover.h" tbl_cache_p tables; char* schema_dir = NULL; char* db_home = NULL; const char *progname; /** * main -- */ int main(int argc, char* argv[]) { int ret, ch, i; ret = 0; progname = argv[0]; while ((ch = getopt(argc, argv, "s:h:c:C:r:R:")) != EOF) switch (ch) { case 's': schema_dir = optarg; load_schema(optarg); break; case 'c': /*create */ ret = create(optarg); break; case 'C': /*Create all*/ ret = create_all(); break; case 'r': /*recover */ ret = recover(optarg); break; case 'R': /*recover_all */ ret = sscanf(optarg,"%i", &i); if(ret != 1) return -1; ret = recover_all(i); break; case 'h': db_home = optarg; break; case '?': default: return(usage()); } argc -= optind; argv += optind; /*free mem; close open files.*/ cleanup(); return ret; } /** * usage -- * */ int usage(void) { fprintf(stderr, "usage: %s %s\n", progname, "-s schemadir [-h home] [-c tablename]"); fprintf(stderr, "usage: %s %s\n", progname, "-s schemadir [-h home] [-C all]"); fprintf(stderr, "usage: %s %s\n", progname, "-s schemadir [-h home] [-r journal-file]"); fprintf(stderr, "usage: %s %s\n", progname, "-s schemadir [-h home] [-R lastN]"); return (EXIT_FAILURE); } /** * create -- creates a Berkeley DB file with tablename (tn), along with * the needed metadata. * requires the schema data to be already parsed '-L' option. */ int create(char* tn) { DB* db; int rc; tbl_cache_p tbc = NULL; table_p tp = NULL; rc = 0; tbc = get_table(tn); if(!tbc) { fprintf(stderr, "[create] Table %s is not supported.\n",tn); return 1; } tp = tbc->dtp; db = get_db(tp); if(db) { printf("Created table %s\n",tn); rc = 0; } else { fprintf(stderr, "[create] Failed to create table %s\n",tn); rc = 1; } return rc; } /** * create_all -- creates a new Berkeley DB table for only the core tables */ int create_all(void) { tbl_cache_p _tbc = tables; int rc; rc = 0; #ifdef EXTRA_DEBUG time_t tim1 = time(NULL); time_t tim2; #endif while(_tbc) { if(_tbc->dtp) if((rc = create(_tbc->dtp->name)) != 0 ) break; _tbc = _tbc->next; } #ifdef EXTRA_DEBUG tim2 = time(NULL); int i = tim2 - tim1; printf("took %i sec\n", i); #endif return rc; } /** * file_list -- * returns a sorted linkedlist of all files in d * * parmameter d is the directory name * parameter tn is optional, * if tablename (tn) is specified returns only jnl files for tablename (tn) * else returns a sorted linkedlist of all files in d * returns lnode_p * the head linknode points to the latests file. */ lnode_p file_list(char* d, char* tn) { DIR *dirp; int i, j, len; char *fn; char *list[MAXFILES]; char dir[MAX_FILENAME_SIZE]; struct dirent *dp; lnode_p h,n; h = n = NULL; i = j = 0; if(!d) { fprintf(stderr, "[file_list]: null path to schema files.\n"); return NULL; } memset(dir, 0, MAX_FILENAME_SIZE); strcpy(dir, d); strcat(dir, "/"); //strcat(dir, "."); dirp = opendir(dir); while ((dp = readdir(dirp)) != NULL) { j=0; if (i> (MAXFILES-1) ) continue; fn = dp->d_name; if (fn[0] == '.') continue; if(tn) { /* only looking for jnl files */ len = strlen(tn); if (!strstr(fn, ".jnl")) continue; if (strncmp(fn, tn, len)) continue; } j = strlen(fn) +1; list[i] = malloc(sizeof(char) * j); memset(list[i], 0 , j); strcat(list[i], fn); i++; } closedir(dirp); qsort(list, i, sizeof(char*), compare); for(j=0;jprev=NULL; n->p = list[j]; if(h) h->prev = n; n->next = h; h = n; } return h; } /** qsort C-string comparison function */ int compare (const void *a, const void *b) { const char **ia = (const char **)a; const char **ib = (const char **)b; return strcmp(*ia, *ib); } /** * recover -- given a journal filename, creates a new db w. metadata, and replays * the events in journalized order. * Results in a new db containing the journaled data. * * fn (filename) must be in the form: * location-20070803175446.jnl */ int recover(char* jfn) { #ifdef EXTRA_DEBUG time_t tim1 = time(NULL); time_t tim2; #endif int len, i, cs, ci, cd, cu; char *v, *s; char line [MAX_ROW_SIZE]; char tn [MAX_TABLENAME_SIZE]; char fn [MAX_FILENAME_SIZE]; char op [7]; //INSERT, DELETE, UPDATE are all 7 char wide (w. null) FILE * fp = NULL; tbl_cache_p tbc = NULL; table_p tp = NULL; i = 0 ; cs = ci = cd = cu = 0; if(!strstr(jfn, ".jnl")) { fprintf(stderr, "[recover]: Does NOT look like a journal file: %s.\n", jfn); return 1; } if(!db_home) { fprintf(stderr, "[recover]: null path to db_home.\n"); return 1; } /*tablename tn*/ s = strchr(jfn, '-'); len = s - jfn; strncpy(tn, jfn, len); tn[len] = 0; /*create abs path to journal file relative to db_home*/ memset(fn, 0 , MAX_FILENAME_SIZE); strcat(fn, db_home); strcat(fn, "/"); strcat(fn, jfn); fp = fopen(fn, "r"); if(!fp) { fprintf(stderr, "[recover]: FAILED to load journal file: %s.\n", jfn); return 2; } tbc = get_table(tn); if(!tbc) { fprintf(stderr, "[recover]: Table %s is not supported.\n",tn); fprintf(stderr, "[recover]: FAILED to load journal file: %s.\n", jfn); return 2; } tp = tbc->dtp; if(!tbc || !tp) { fprintf(stderr, "[recover]: FAILED to get find metadata for : %s.\n", tn); return 3; } while ( fgets(line , MAX_ROW_SIZE, fp) != NULL ) { len = strlen(line); if(line[0] == '#' || line[0] == '\n') continue; if(len > 0) line[len-1] = 0; /*chomp trailing \n */ v = strchr(line, '|'); len = v - line; strncpy(op, line, len); op[len] = 0; switch( get_op(op, len) ) { case INSERT: v++; //now v points to data len = strlen(v); insert(tp, v, len); ci++; break; case UPDATE: v++; len = strlen(v); update(tp, v, len); cu++; break; case DELETE: //v is really the key delete(tp, v, len); cd++; break; case UNKNOWN_OP: fprintf(stderr,"[recover]: UnknownOP - Skipping ROW: %s\n",line); cs++; continue; } i++; } #ifdef EXTRA_DEBUG printf("Processed journal file: %s.\n", jfn); printf("INSERT %i records.\n",ci); printf("UPDATE %i records.\n",cu); printf("DELETE %i records.\n",cd); printf("SKIPed %i records.\n",cs); printf("------------------------\n"); printf("Total %i records.\n",i); tim2 = time(NULL); i = tim2 - tim1; printf("took %i sec\n", i); #endif fclose(fp); return 0; } /** * recover_all -- Iterates over all core tables in enumerated order for recovery from * journal files (.jnl). * The parm 'lastn' is the number of journal files needed to be recovered. * Hardcoded to only find MAXFILES. * * e.g. * 25 journal files are present for the 'acc' table, however you only * want to restore the latest 3; so lastn=3. */ int recover_all(int lastn) { lnode_p n, h; tbl_cache_p _tbc = tables; if(MAXFILES < lastn) return 1; if(!schema_dir) { fprintf(stderr, "[recover_all]: null path to schema files.\n"); return 1; } if(!db_home) { fprintf(stderr, "[recover_all]: null path to db_home.\n"); return 1; } while(_tbc) { int j; if(_tbc->dtp) h = file_list(db_home, _tbc->dtp->name); n = h; /*lastn; move to the oldest of the N*/ for(j=1;jnext != NULL) ) n = n->next; while(n) { printf("[recover_all] recovering file: %s\n",n->p); if(recover(n->p)) fprintf(stderr, "[recover_all]: Error while recovering: [%s]\n. Continuing..\n",n->p); n = n->prev; } while(h) /*free mem*/ { n = h->next; free(h->p); free(h); h = n; } _tbc = _tbc->next; } return 0; } /** * extract_key -- uses the internal schema to extract the key from the data * row that was found in the journal. * caller provides inititialize memory for destination key (k). * data is provided ; key is filled in */ int extract_key(table_p tp, char* k, char* d) { char *s, *p; char buf[MAX_ROW_SIZE]; int n, len; if(!tp || !k || !d) return -1; len=n=0; p = k; /*copy data so we can tokenize w.o trampling */ len = strlen(d); strncpy(buf, d, len); buf[len] = 0; s = strtok(buf, "|"); while(s!=NULL && nncols-1) > n) { if( tp->colp[n]->kflag ) { strncpy(p, s, len); p+=len; *p = '|'; p++; } } s=strtok(NULL, "|"); n++; } *p = 0; return 0; } /** * delete -- deletes a row from the db we are trying to rebuild */ int delete(table_p tp, char* k, int len) { DBT key; DB *db; if(!tp || !k) return 1; if((db = get_db(tp)) == NULL) return 2; memset(&key, 0, sizeof(DBT)); key.data = k; key.ulen = MAX_ROW_SIZE; key.size = len; if ( db->del(db, NULL, &key, 0)) { fprintf(stderr, "[delete] FAILED --> [%.*s] \n", len, k); return 3; } return 0; } /** * _insert -- inserts a new row in to the db we are trying to rebuild * I needed this to directly insert metadata when the db is created. */ int _insert(DB* db, char* k, char* v, int klen, int vlen) { DBT key, data; if(!db || !k || !v) return 1; memset(&key, 0, sizeof(DBT)); key.data = k; key.ulen = MAX_ROW_SIZE; key.size = klen; memset(&data, 0, sizeof(DBT)); data.data = v; data.ulen = MAX_ROW_SIZE; data.size = vlen; if (db->put(db, NULL, &key, &data, 0)) { fprintf(stderr, "[insert] FAILED --> [%.*s] \n", vlen, v); return 1; } return 0; } /** * insert -- given the data row (v) and its length (vlen), we build the corresponding * key, and insert the data in to the db. * This will over-right the value if already present. */ int insert(table_p tp, char* v, int vlen) { char k[MAX_ROW_SIZE]; int rc, klen; DB *db; if(!tp || !v) return 1; if((db = get_db(tp)) == NULL) return 2; memset(k,0,MAX_ROW_SIZE); if( extract_key(tp, k, v) ) { fprintf(stderr, "[insert] failed to extract key for row: %.*s",vlen, v); return 2; } klen = strlen(k); rc = _insert(db, k, v, klen, vlen); return rc; } /** * update -- given the data row (v) and its length (vlen), we build the corresponding * key, and update the data in the db. * This is implemented as DELETE + INSERT. */ int update(table_p tp, char* v, int len) { char k[MAX_ROW_SIZE]; if(!tp || !v) return 1; memset(k,0,MAX_ROW_SIZE); if( extract_key(tp, k, v) ) { fprintf(stderr, "[update] failed to extract key for row: %.*s",len, v); return 2; } /* if( delete(tp, k, strlen(k)) ) return 3; */ if( insert(tp, v, len) ) return 4; return 0; } /** * get_op -- used to convert the string operation name to an enumerated op */ int get_op(char* op, int len) { if((len==6) && strstr("INSERT",op) ) return INSERT; if((len==6) && strstr("UPDATE",op) ) return UPDATE; if((len==6) && strstr("DELETE",op) ) return DELETE; return UNKNOWN_OP; } /** * load_schema -- sets up the internal representation of the schema. */ int load_schema(char* d) { int rc; char *tn; char line1 [MAX_ROW_SIZE]; char line2 [MAX_ROW_SIZE]; char fn [MAX_FILENAME_SIZE]; tbl_cache_p tbc = NULL; table_p tp = NULL; FILE * fp = NULL; lnode_p h,n; rc=0; h = n = NULL; if(!d) { fprintf(stderr, "[load_schema]: null path to schema files.\n"); return 1; } tables = (tbl_cache_p)malloc(sizeof(tbl_cache_t)); if(!tables) return 1; h = file_list(d, NULL); while(h) { n = h->next; /*create abs path to journal file (relative to db_home) */ memset(fn, 0 , MAX_FILENAME_SIZE); strcat(fn, d); strcat(fn, "/"); strcat(fn, h->p); fp = fopen(fn, "r"); if(!fp) { fprintf(stderr, "[load_schema]: FAILED to load schema file: %s.\n", h->p); break; } tn = h->p; tbc = get_table(tn); if(!tbc) { fprintf(stderr, "[load_schema]: Table %s is not supported.\n",tn); fprintf(stderr, "[load_schema]: FAILED to load data for table: %s.\n", tn); goto done; } tp = tbc->dtp; while ( fgets(line1 , MAX_ROW_SIZE, fp) != NULL ) { if ( fgets(line2 , MAX_ROW_SIZE, fp) != NULL ) { if(strstr(line1, METADATA_COLUMNS)) { if(0!=load_metadata_columns(tp, line2)) { fprintf(stderr, "[load_schema]: FAILED to load METADATA COLS in table: %s.\n", tn); goto done; } } if(strstr(line1, METADATA_KEY)) { if(0!=load_metadata_key(tp, line2)) { fprintf(stderr, "[load_schema]: FAILED to load METADATA KEYS in table: %s.\n", tn); goto done; } } } else { fprintf(stderr, "[load_schema]: FAILED to read schema value in table: %s.\n", tn); goto done; } } done: fclose(fp); h = n; } while(h) /*free mem*/ { n = h->next; free(h->p); free(h); h = n; } return rc; } /** * get_table -- return pointer to lazy initialized table struct */ tbl_cache_p get_table(char *_s) { tbl_cache_p _tbc = tables; table_p _tp = NULL; while(_tbc) { if(_tbc->dtp) { if(_tbc->dtp->name && !strcmp(_tbc->dtp->name,_s)) { return _tbc; } } _tbc = _tbc->next; } _tbc = (tbl_cache_p)malloc(sizeof(tbl_cache_t)); if(!_tbc) return NULL; _tp = create_table(_s); if(!_tp) { fprintf(stderr, "[get_table]: failed to create table.\n"); free(_tbc); return NULL; } _tbc->dtp = _tp; if(tables) (tables)->prev = _tbc; _tbc->next = tables; tables = _tbc; return _tbc; } /** * create_table -- returns an initialed table struct */ table_p create_table(char *_s) { int i; table_p tp = NULL; tp = (table_p)malloc(sizeof(table_t)); if(!tp) return NULL; i=strlen(_s)+1; tp->name = (char*)malloc(i*sizeof(char)); strncpy(tp->name, _s, i); tp->ncols=0; tp->nkeys=0; tp->ro=0; tp->logflags=0; tp->db = NULL; for(i=0;icolp[i] = NULL; return tp; } /** * load_metadata_columns -- parses the METADATA_COLUMNS line into the internal * representation. */ int load_metadata_columns(table_p _tp, char* line) { int ret,n,len; char *s = NULL; char cn[64], ct[16]; column_p col; ret = n = len = 0; if(!_tp) return -1; if(_tp->ncols!=0) return 0; /* eg: line = "table_name(str) table_version(int)" */ s = strtok(line, " \t"); while(s!=NULL && nname = (char*)malloc(len * sizeof(char)); strcpy(col->name, cn ); /* set type*/ len = strlen( ct )+1; col->type = (char*)malloc(len * sizeof(char)); strcpy(col->type, ct ); _tp->colp[n] = col; n++; _tp->ncols++; s=strtok(NULL, " \t"); } return 0; } /** * load_metadata_key -- parses the METADATA_KEY line into the internal * representation. */ int load_metadata_key(table_p _tp, char* line) { int ret,n,ci; char *s = NULL; ret = n = ci = 0; if(!_tp)return -1; s = strtok(line, " \t"); while(s!=NULL && n< _tp->ncols) { ret = sscanf(s,"%i", &ci); if(ret != 1) return -1; if( _tp->colp[ci] ) { _tp->colp[ci]->kflag = 1; _tp->nkeys++; } n++; s=strtok(NULL, " "); } return 0; } /** * get_db -- lazy initialized DB access * Its like this so we get new db files only for the tables that have * journal files. * The db file on disk will be named: * .new */ DB* get_db(table_p tp) { int rc; DB* db; char dfn[MAX_FILENAME_SIZE]; if( !tp) return NULL; if( tp->db) return tp->db; memset(dfn, 0, MAX_FILENAME_SIZE); if(db_home) { strcpy(dfn, db_home); strcat(dfn, "/"); } /*creation of DB follows*/ strcat(dfn, tp->name); if ((rc = db_create(&db, NULL, 0)) != 0) { fprintf(stderr, "[create_table]: error db_create for table: %s.\n",dfn); return NULL; } if ((rc = db->open(db, NULL, dfn, NULL, DB_HASH, DB_CREATE, 0664)) != 0) { fprintf(stderr, "[create_table]: error opening %s.\n",dfn); fprintf(stderr, "[create_table]: error msg: %s.\n",db_strerror(rc)); return NULL; } tp->db = db; import_schema(tp); return db; } /** */ int import_schema(table_p tp) { int rc, len1, len2; char line1 [MAX_ROW_SIZE]; char line2 [MAX_ROW_SIZE]; char fn [MAX_FILENAME_SIZE]; FILE * fp = NULL; rc = 0; if(!schema_dir) { fprintf(stderr, "[import_schema]: null schema dir.\n"); return 1; } if(!tp) { fprintf(stderr, "[import_schema]: null table parameter.\n"); return 1; } /*create abs path to journal file (relative to db_home) */ memset(fn, 0 , MAX_FILENAME_SIZE); strcat(fn, schema_dir); strcat(fn, "/"); strcat(fn, tp->name); fp = fopen(fn, "r"); if(!fp) { fprintf(stderr, "[import_schema]: FAILED to open def schema file: %s.\n", fn); return 1; } while ( fgets(line1 , MAX_ROW_SIZE, fp) != NULL ) { if ( fgets(line2 , MAX_ROW_SIZE, fp) != NULL ) { len1 = strlen(line1)-1; len2 = strlen(line2)-1; line1[len1] = 0; line2[len2] = 0; if((rc = _insert(tp->db, line1, line2, len1, len2) )!=0) { fprintf(stderr, "[import_schema]: FAILED to write schema def into table: %s.\n", tp->name); goto done; } } else { fprintf(stderr, "[import_schema]: FAILED to read schema def value in table: %s.\n", tp->name); goto done; } } done: fclose(fp); return rc; } /** * cleanup -- frees memory; closes any files. */ void cleanup(void) { //cleanup while(tables) { int i; tbl_cache_p n = tables->next; table_p tp = tables->dtp; if(tp) { free(tp->name); for(i=0;i< tp->ncols;i++) { free(tp->colp[i]->name); free(tp->colp[i]->type); free(tp->colp[i]); } if(tp->db) tp->db->close(tp->db, 0); free(tp); } free(tables); tables = n; } } kamailio-4.0.4/utils/db_berkeley/kambdb_recover.h0000644000000000000000000000520012223032460020517 0ustar rootroot/* * $Id$ * * recovery for berkeley_db module * * Copyright (C) 2007 Cisco Systems * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2007-09-19 genesis (wiquan) */ #include #include #include #include #include #include /*max number of journal files that we are reading*/ #define MAXFILES 64 /*max number of columns in a table*/ #define MAX_NUM_COLS 32 /*max char width of a table row*/ #define MAX_ROW_SIZE 2048 /*max char width of a table name*/ #define MAX_TABLENAME_SIZE 64 #define MAX_FILENAME_SIZE 512 #define METADATA_KEY "METADATA_KEY" #define METADATA_COLUMNS "METADATA_COLUMNS" /*operations*/ enum { INSERT, UPDATE, DELETE, UNKNOWN_OP }; typedef struct _lnode { char* p; struct _lnode *prev; struct _lnode *next; } lnode_t, *lnode_p; typedef struct _column { char* name; char* type; int kflag; } column_t, *column_p; typedef struct _table { char* name; column_p colp [MAX_NUM_COLS]; int ncols; int nkeys; int ro; int logflags; DB* db; } table_t, *table_p; typedef struct _tbl_cache { table_p dtp; struct _tbl_cache *prev; struct _tbl_cache *next; } tbl_cache_t, *tbl_cache_p; int usage(void); DB* get_db(table_p tp); int get_op(char* op, int len); int delete(table_p tp, char* v, int len); int insert(table_p tp, char* v, int len); int _insert(DB* db, char* k, char* v, int klen, int vlen); int update(table_p tp, char* v, int len); int create(char* tn); int _version(DB* db); int create_all(void); int recover(char* tn); int recover_all(int lastn); lnode_p file_list(char* d, char* tn); int compare (const void *a, const void *b); int extract_key(table_p tp, char* key, char* data); int load_schema(char* dir); tbl_cache_p get_table(char *s); table_p create_table(char *_s); int load_metadata_columns(table_p _tp, char* line); int load_metadata_key(table_p _tp, char* line); int import_schema(table_p tp); void cleanup(void); kamailio-4.0.4/utils/db_berkeley/Makefile0000644000000000000000000000101512223032460017041 0ustar rootroot# $Id: Makefile 882 2006-05-30 15:31:09Z miconda $ # # db_berkeley Makefile # include ../../Makefile.defs auto_gen= NAME=kambdb_recover include ../../Makefile.sources # if you want to tune or reset flags #DEFS:=-DEXTRA_DEBUG -I$(LOCALBASE)/BerkeleyDB.4.6/include DEFS+=-I$(LOCALBASE)/include -I$(LOCALBASE)/BerkeleyDB.4.6/include \ -I$(LOCALBASE)/include/db4 -I$(SYSBASE)/include LIBS=-L$(LOCALBASE)/lib -L$(SYSBASE)/lib -L$(LOCALBASE)/BerkeleyDB.4.6/lib -ldb include ../../Makefile.rules modules: kamailio-4.0.4/utils/sipgrep/0000755000000000000000000000000012223032460014606 5ustar rootrootkamailio-4.0.4/utils/sipgrep/sipgrep0000755000000000000000000001273012223032460016210 0ustar rootroot#!/usr/bin/perl # sipgrep version 0.2. Skin for ngrep. (C) 2005-2006 Alexandr Dubovikov use Term::ANSIColor; use Getopt::Std; #colors: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, ON_BLACK, ON_RED, ON_GREEN, ON_YELLOW, ON_BLUE, ON_MAGENTA, ON_CYAN, ON_WHITE # #type: BOLD, DARK, UNDERLINE, UNDERSCORE, BLINK, REVERSE, CONCEALED, $COLORS{'method'}='bold red'; $COLORS{'response'} ='bold yellow'; $COLORS{'callid'} = 'bold magenta'; $COLORS{'fromtag'} = 'bold blue'; $COLORS{'totag'} = 'bold green'; $COLORS{'viabranch'} = 'bold cyan'; $limit=2000; $ngrep="/usr/local/bin/ngrep"; #path to NGREP $ngrep_flags="-l"; # Flag for Ngrep $colorsmethods="INVITE|REGISTER|BYE|ACK|CANCEL|OPTIONS|REFER|NOTIFY|MESSAGE|INFO|PRACK|UPDATE"; %options=(); getopts("f:t:l:ahVp:Tcn",\%options); $version=< END $usage=< <-f number> <-t number> <-a> <-l file> <-V> <-p> <-T> <-n|-c> -h Displays this help message. -f ARG Search ARG in From field. -t ARG Search ARG in To field. -a Search the ARG from '-f' and '-t' parameters in To and From fields. -l ARG Debug file name. -V Displays the current version. -p Port for ngrep. -T Parameter for ngrep. Indicating the delta between packet matches. -c Allow colors in debug file. -n Not allow colors in STDOUT. Example: sipgrep -f 0123456 -t 0654321 -l debug.sip or sipgrep -f 0123456 -a -l debug.sip END #version if(defined $options{V}) { print $version; exit; } #usage if((!defined $options{f} && !defined $options{t}) || defined $options{h}) { print $usage; exit; } #TimeStamp $ngrep_flags .= sprintf(" %s", (defined $options{T}) ? "-T" : "-t" ); #Port $ngrep_flags .= sprintf(" port %d", (defined $options{p}) ? $options{p} : "5060" ); #our system variables $anumber=$options{f}; $bnumber=$options{t}; $all=$options{a}; $filedebug=$options{l}; $nocolors=$options{n}; $debugfilecolors=$options{c}; #remove old debug file. unlink $filedebug if(defined $filedebug); #open PIPE open(PIPE,"$ngrep $ngrep_flags |") or die "Can't run '$ngrep' programm: $!\n"; select(PIPE); $| = 1; # make unbuffered select(STDOUT); $| = 1; # make unbuffered while() { chomp($_); s/ //ig; s/ // if(/^ /); if(/\.\. (.*)$/) { $tmp.=$_; if(create_newline($tmp)==1) { undef $firstvia; system_out("----------------begin of packet -----------------------------\n"); foreach $key (@tmparray) { system_out($key."\n"); } system_out("------------------end of packet -----------------------------\n"); } } elsif(/^#/) { undef $tmp;} elsif(/^U /) { $tmp=$_."....";} else { $tmp.=$_;} } close(PIPE); sub create_newline { my $tmpstring = shift; exit if($index > $limit); undef @tmparray; @tmparray=split(/\.\./,$tmpstring); $print_out=1; undef $searchcallid; foreach $key(@tmparray) { if(defined $anumber || defined $bnumber) { $print_out=0; getmatch($key); #if(!$callid) $tmpcallid=getcallid($key); if($searchcallid==1) { $GCALLID{$tmpcallid}=1; $print_out=1; } } } return $print_out; } sub getmatch { my $tmps = shift; #From: "Martin Mustermann" ;tag=2bdf62455c76484b9e1163154d2758cd;epid=46aa53832b if($tmps=~/^From:/i && ((defined $anumber && $tmps=~/$anumber/ig) || (defined $all && defined $bnumber && $tmps=~/$bnumber/ig))) { $searchcallid=1; } elsif($tmps=~/^To:/i && ((defined $bnumber && $tmps=~/$bnumber/ig) || (defined $all && defined $anumber && $tmps=~/$anumber/ig))) { $searchcallid=1; } if($tmps=~/^Call-ID:/ig) { (undef,$tmpcallid)=split(/: /,$tmps,2); $print_out=1 if($GCALLID{$tmpcallid}==1); } } sub getcallid { my $tmps = shift; (undef,$tmpcallid)=split(/: /,$tmps,2) if($tmps=~/^Call-ID:/ig); return $tmpcallid; } sub system_out { my $out = shift; my $tmpmain, $tmpstr; #Method: if($out =~/^($colorsmethods) /ig) { ($tmpmain,$tmpstr)=split(/ /,$out,2); print_out($tmpmain, $COLORS{'method'}); print_out(" ".$tmpstr); } #Response: elsif($out =~/^SIP\/2\.0 [1-6][0-9][0-9] /ig) { ($tmpstr, $tmpmain)=split(/ /,$out,2); print_out($tmpstr." "); print_out($tmpmain, $COLORS{'response'}); } #Callid elsif($out =~/^(Call-ID):/ig) { ($tmpstr, $tmpmain)=split(/: /,$out,2); print_out($tmpstr.": "); print_out($tmpmain, $COLORS{'callid'}); } #From/To: tag elsif($out =~/^(From|f|To|t): /ig && $out=~/;tag=/ig) { ($tmpstr, $tmpmain)=split(/;tag=/,$out,2); print_out($tmpstr.";tag="); ($tmpmain, $tmpstr)=split(/;/,$tmpmain,2); print_out($tmpmain, $out =~/^(From|f): / ? $COLORS{'fromtag'} : $COLORS{'totag'}); print_out(";".$tmpstr) if(defined $tmpstr); } #Via: branch elsif($out =~/^(Via|v): /ig && $out=~/;branch=/ig && !defined $firstvia) { ($tmpstr, $tmpmain)=split(/;branch=/,$out,2); print_out($tmpstr.";branch="); ($tmpmain, $tmpstr)=split(/;/,$tmpmain,2); print_out($tmpmain, $COLORS{'viabranch'}); print_out(";".$tmpstr) if(defined $tmpstr); $firstvia = 1; } else { print_out($out); } } sub print_out { my $ltext = shift; my $lcolor = shift; $lcolor='reset' if(!defined $lcolor || defined $nocolors); print color $lcolor; print $ltext; if(defined $filedebug) { open(DBG, ">>$filedebug"); $lcolor = 'reset' if(!(defined $debugfilecolors)); print DBG color $lcolor; print DBG $ltext; close(DBG); } }kamailio-4.0.4/utils/sipgrep/README0000644000000000000000000000117112223032460015466 0ustar rootrootsipgrep was created by Alexandr Dubovikov It is a wrapper on ngrep which * filters SIP messages according number in To or From header fields * displays From tag, To tag, Call-ID and branch in different colours, thus it is possible to trace dialogs or transactions by "one look into message" ... this is really great! ;-) * It can store received messages into a file and show them (together) Known "issues": * you might need to change "path to ngrep", for example I changed it to $ngrep="sudo ngrep -d any"; which runs ngrep on all interfaces under sudo for me [ README written by Vaclav ] kamailio-4.0.4/utils/profile/0000755000000000000000000000000012223032460014575 5ustar rootrootkamailio-4.0.4/utils/profile/launch.sh0000644000000000000000000000401112223032460016377 0ustar rootroot#!/bin/sh -x # profile filename PREF=$1 # number of cycles NROFCYCLES=4 # functions to go into report MYFN="eat_line|eat_space|eat_token" # set to non-zero if only a report is to be generated #REPONLY=tru CONFIG=profile.cfg SRD=${HOME}/sip_router PRO=${SRD}/profile REP=$PRO/$PREF.txt EXEC=ser function usage() { cat << EOF usage: $0 profile_name currently, this script starts a remote sender at alioth. The sender iterates through all messages available in its directory and send them many times (should be configurable). Sip-router is configured to relay requests to benetnash:sink and responses are built to be routed there too. This repeats NROFCYCLES-times. Results of all cycles are then dumped into -.txt and a report .txt is generated. EOF exit 1 } if [ "$#" -ne 1 ] ; then usage fi cd $SRD function run() { j=0 while [ $j -lt "$NROFCYCLES" ] ; do i=`printf "%.3d" $j` j=`expr $j + 1` echo "*** Entering cycle $j" /usr/bin/time --output="$PRO/${PREF}-${i}-time.txt" ${SRD}/$EXEC -l 192.168.99.100 -D -E -d -f ${PRO}/$CONFIG & #rsh -l jku benetnash.fokus.gmd.de 'nohup bin/sock -s -u 5060 ' rsh -l jku alioth.fokus.gmd.de 'nohup tmp/sipp/start.sh ' killall -INT $EXEC gprof $EXEC > $PRO/${PREF}-${i}.txt done } function report() { cat > $REP << EOF first line ... time spent in tested procedure second line (yyrestart) ... total time third line (receive_msg) ... numer of calls % cumulative self self total time seconds seconds calls ms/call ms/call name EOF j=0 while [ $j -lt "$NROFCYCLES" ] ; do i=`printf "%.3d" $j` j=`expr $j + 1` FN="${PRO}/${PREF}-${i}.txt" echo >> $REP echo >> $REP echo $FN >> $REP egrep "($MYFN|yyrestart)" $FN | grep -v '\[' >> $REP grep 'receive_msg \[.\]' $FN | grep -v '\/' >> $REP echo >> $REP cat $PRO/${PREF}-${i}-time.txt >> $REP done cat >> $REP << EOF Execution environment: EOF cat /proc/cpuinfo /proc/meminfo >> $REP } if [ -z "$REPONLY" ] ; then run fi report echo '*** finished ***' echo kamailio-4.0.4/utils/profile/profile.cfg0000644000000000000000000000032012223032460016711 0ustar rootroot# first sort out iptel.org requests from those destined somewhere else #################################################################################### port=5080 route[0] { forward( 127.0.0.1, 9 ); } kamailio-4.0.4/utils/pike_top/0000755000000000000000000000000012223032460014747 5ustar rootrootkamailio-4.0.4/utils/pike_top/pike_top.c0000644000000000000000000004143312223032460016732 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #define NAME "SER mod_pike top console" #define VERSION "1.0" // XML elements in result // they MUST NOT have a number on the tail // because pike uses it for "row" numbering static const char * const MAX_HITS = "max_hits"; static const char * const IP_ADDR = "ip_addr"; static const char * const LEAF_HITS_PREV = "leaf_hits_prev"; static const char * const LEAF_HITS_CURR = "leaf_hits_curr"; static const char * const EXPIRES = "expires"; static const char * const STATUS = "status"; static const char * const NUMBER_OF_ROWS = "number_of_rows"; #define IP_ADDR_MAX_LENGTH 40 #define STATUS_MAX_LENGTH 10 typedef struct TopItem { char ip_addr[IP_ADDR_MAX_LENGTH]; in_addr_t ipv4_addr; /* uint32_t */ struct in6_addr ipv6_addr; unsigned short leaf_hits[2]; unsigned int expires; char status[STATUS_MAX_LENGTH]; int num_of_ips; /* number of IP addresses in aggregated result */ } TopItem; int compare_TopItem_hits(const void* left, const void *right) { TopItem *li = (TopItem *)left; TopItem *ri = (TopItem *)right; return li->leaf_hits[0] + li->leaf_hits[1] - ri->leaf_hits[0] - ri->leaf_hits[1]; } /** Compare function to qsort array in reverse order (biger first) */ int compare_TopItem_hits_reverse(const void* left, const void *right) { return compare_TopItem_hits(right, left); } int compare_TopItem_ipv4_addr(const void *item1, const void *item2) { return ((TopItem*)item1)->ipv4_addr - ((TopItem*)item2)->ipv4_addr; } /** * @return concatenated string in newly allocated memory */ static char *concat( const char *name, int index ) { char *ptr; int rv; rv = asprintf(&ptr, "%s%d", name, index); if ( rv == -1 ) return 0; return ptr; } static void strfree( char *ptr ) { if (ptr) free(ptr); } static void die_if_fault_occurred (xmlrpc_env *env) { if (env->fault_occurred) { fprintf(stderr, "XML-RPC Fault: %s (%d)\n", env->fault_string, env->fault_code); exit(1); } } /** @return 0 if everything is OK, 1 otherwise */ static int fault_occurred (xmlrpc_env *env) { if (env->fault_occurred) { fprintf(stderr, "XML-RPC Fault: %s (%d)\n", env->fault_string, env->fault_code); return 1; } return 0; } static void die_if_fault_occurred_line (xmlrpc_env *env, int line) { if (env->fault_occurred) fprintf(stderr, "LINE: %d\n", line); die_if_fault_occurred(env); } void print_help() { printf("\n"); if ( isatty(1) ) printf("usage: \033[1mpike_top : [--hot|--warm|--all] [--mask]\033[0m\n"); else printf("usage: pike_top : [--hot|--warm|--all] [--mask] [--ipleaf|--inner]\n"); printf("\n"); printf("\toptions:\n" "\t\t--hot ... show hot IP leaves\n" "\t\t--all ... show all IP leaves\n" "\t\t--mask ... aggregate results regarding IP mask length\n" "\t\t (default 32, i.e. not aggregate)\n" "\t\t IPv4 only at this time\n\n" "\t\t\tdefault is to show HOT nodes\n\n"); if ( isatty(1) ) printf("You can use \033[1mwatch\033[0m(1) utility for periodical output.\n\n"); else printf("You can use watch(1) utility for periodical output.\n\n"); /* printf("Note:\n" "It is a question if reporting warm nodes is useful and if yes, how to report\n" "them. I feel that should be more welcome to report parents of warm nodes,\n" "or something like this to show which subnets could be problematic...\n\n"); */ } /* Following options are conforming to definition of node status defined in pike/ip_tree.h */ #define OPT_WARM 1 #define OPT_HOT 2 #define OPT_ALL 3 /** * @param options ORed cmdline options * @return position of first non option parameter */ int process_options(int argc, char *argv[], int *options, int *mask_length) { static struct option long_options[] = { {"hot", 0, 0, 'h'}, /* {"warm", 0, 0, 'w'}, */ {"all", 0, 0, 'a'}, {"mask", 0, 0, 'm'}, {"help", 0, 0, '?'}, {0,0,0,0} }; int c, index, counter; *options = 0; counter = 0; while ( (c=getopt_long(argc, argv, "hwam:", long_options, &index)) != -1 ) { switch (c) { case 'h': *options = OPT_HOT; ++counter; break; /* case 'w': *options = OPT_WARM; ++counter; break; */ case 'a': *options = OPT_ALL; ++counter; break; case 'm': *mask_length = atoi(optarg); ; break; case '?': default: print_help(); exit(0); break; } } if ( counter > 1 ) { fprintf(stderr, "ERROR: Node type selectors are exlusive, only one of them can be used\n"); print_help(); exit(1); } if ( *options == 0 ) *options = OPT_HOT; return optind; } /* Get an integer value from struct result of xmlrpc_client_call() * @param structP pointer to a result struct * @param element_name name of structure item * @param rv returned value * @return 1 if succeed and 0 otherwise */ int get_int_from_struct_by_name(xmlrpc_value *structP, const char *element_name, int *rv) { xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_value *valueP; xmlrpc_struct_find_value(&env, structP, element_name, &valueP); if ( env.fault_occurred ) goto error; xmlrpc_read_int(&env, valueP, rv); if ( env.fault_occurred ) goto error1; xmlrpc_DECREF(valueP); return 1; error1: xmlrpc_DECREF(valueP); error: return 0; } /* Get a new string value from struct result of xmlrpc_client_call() * @param structP pointer to a result struct * @param element_name name of structure item * @param rv contains newly allocated string or NULL if fails * @return 1 if succeed and 0 otherwise */ /* FIXME terminates the programm if it fails */ int get_string_from_struct_by_name(xmlrpc_value *structP, const char *element_name, char **rv) { xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_value *valueP; int length; xmlrpc_struct_find_value(&env, structP, element_name, &valueP); die_if_fault_occurred_line(&env, __LINE__); xmlrpc_read_string(&env, valueP, (const char **)rv); die_if_fault_occurred_line(&env, __LINE__); xmlrpc_DECREF(valueP); return 1; } /* Get an integer value from struct result of xmlrpc_client_call() * @param structP pointer to a result struct * @param index index of requested element * @param rv returned value * @return 1 if succeed and 0 otherwise */ /* FIXME terminates the program if it fails */ int get_int_from_struct_by_idx(xmlrpc_value *structP, int index, int *rv) { xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_value *keyP; xmlrpc_value *valueP; xmlrpc_struct_read_member(&env, structP, index, &keyP, &valueP); /* increment refcount of returned values */ die_if_fault_occurred_line(&env, __LINE__); xmlrpc_read_int(&env, valueP, rv); die_if_fault_occurred_line(&env, __LINE__); xmlrpc_DECREF(valueP); return 1; } enum _value_type { TYPE_NOT_DEF = 0, TYPE_INTEGER = 1, TYPE_STRING = 2 }; typedef enum _value_type value_type; struct _key_value_pair { char *key; value_type type; union value { int integer; char *string; void *value; } value; }; typedef struct _key_value_pair key_value_pair; void key_value_pair_cleanup(key_value_pair *kvp) { if (kvp && kvp->key) { free(kvp->key); } if (kvp && kvp->type == TYPE_STRING && kvp->value.string) { free(kvp->value.string); } kvp->key = 0; kvp->type = TYPE_NOT_DEF; kvp->value.integer = 0; } /** Get a string value from struct result of xmlrpc_client_call() * @param structP pointer to a result struct * @param index index of requested element * @param rv pointer to key_value_pair * @return 1 if succeed and 0 otherwise */ /* FIXME terminates the programm if it fails */ int get_struct_item_by_idx(xmlrpc_value *structP, int index, key_value_pair *rv) { xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_value *keyP; xmlrpc_value *valueP; int length; const char *string; xmlrpc_struct_read_member(&env, structP, index, &keyP, &valueP); /* increment refcount of returned values */ die_if_fault_occurred_line(&env, __LINE__); xmlrpc_read_string(&env, keyP, (const char **)&rv->key); /* handle value type */ switch ( xmlrpc_value_type(valueP) ) { case XMLRPC_TYPE_INT: xmlrpc_read_int(&env, valueP, &rv->value.integer); die_if_fault_occurred_line(&env, __LINE__); rv->type = TYPE_INTEGER; break; case XMLRPC_TYPE_STRING: xmlrpc_read_string(&env, valueP, &string); printf("get_struct_item_by_idx: ptr = %p, string value = '%s'\n", string, string); die_if_fault_occurred_line(&env, __LINE__); rv->value.string = (char *)string; rv->type = TYPE_STRING; break; default: fprintf(stderr, "Wrong type of return value in key: '%s', exiting...\n", rv->key); exit(1); } xmlrpc_DECREF(keyP); /* decrement refcount */ xmlrpc_DECREF(valueP); die_if_fault_occurred_line(&env, __LINE__); /* FIXME add error handling */ return 1; } /** * Reads one toprow from given structure * @param structP * @param rownum * @param top_item */ int read_row(xmlrpc_value *structP, int index, TopItem *top_item) { char *elem; char *string = 0; elem = concat(IP_ADDR, index); if ( ! get_string_from_struct_by_name(structP, elem, &string) ) goto error; strncpy(top_item->ip_addr, string, sizeof(top_item->ip_addr)); free(string); string = 0; free(elem); elem = concat(LEAF_HITS_PREV, index); if ( ! get_int_from_struct_by_name(structP, elem, (unsigned int *)&top_item->leaf_hits[0]) ) goto error; free(elem); elem = concat(LEAF_HITS_CURR, index); if ( ! get_int_from_struct_by_name(structP, elem, (unsigned int *)&top_item->leaf_hits[1]) ) goto error; free(elem); elem = concat(EXPIRES, index); if ( ! get_int_from_struct_by_name(structP, elem, (unsigned int *)&top_item->expires) ) goto error; free(elem); elem = concat(STATUS, index); if ( ! get_string_from_struct_by_name(structP, elem, &string) ) goto error; strncpy(top_item->status, string, sizeof(top_item->status)); free(string); free(elem); return 1; error: if ( string ) free(string); free(elem); return 0; } void print_row(TopItem *ti) { char *fmt; if ( ! ti ) { printf("%-15s %10s %10s %10s %-10s\n", "IP address", "HITS PREV", "HITS CURR", "EXPIRES", "STATUS"); return; } if ( strlen(ti->ip_addr) > 15 ) // IPv6 addr fmt = "%s\n %10d %10d %10d %-10s\n"; else fmt = "%-15s %10d %10d %10d %-10s\n"; printf(fmt, ti->ip_addr, ti->leaf_hits[0], ti->leaf_hits[1], ti->expires, ti->status); } void print_row_agg(TopItem *ti) /* IPv4 only */ { char *fmt; if ( ! ti ) { printf("%-15s %10s %10s %5s\n", "IP address", "HITS PREV", "HITS CURR", "COUNT"); return; } fmt = "%-15s %10d %10d %5d\n"; printf(fmt, ti->ip_addr, ti->leaf_hits[0], ti->leaf_hits[1], ti->num_of_ips); } uint32_t mask( int msklen ) { if ( msklen ) return 0xffffffff ^ ((1 << (32-msklen)) - 1); else return 0; } void print_rows(TopItem *root, int nmemb, int mask_length) { int i; void (*print_function)(TopItem *); if (mask_length == 32) print_function = print_row; else print_function = print_row_agg; print_function(0); for ( i = 0; i < nmemb; ++i, ++root ) { print_function(root); } } int main( int argc, char *argv[] ) { xmlrpc_env env; xmlrpc_value * resultP; xmlrpc_value * keyP; xmlrpc_value * valueP; int struct_size; int i, j; size_t length; const char * str_key_value; xmlrpc_int int_key_value; unsigned int max_hits = 0; unsigned int rows = 0; int rv; char *uri; int options; /* what kind of nodes should be processed */ int uri_pos; /* position of first non option argument */ char stropts[16]; int pos = 0; int mask_length = 32; /* 32 means NO aggregate */ if (argc-1 < 1) { print_help(); exit(0); } uri_pos = process_options(argc, argv, &options, &mask_length); switch (options) { case OPT_HOT: sprintf(stropts, "HOT"); break; case OPT_ALL: sprintf(stropts, "ALL"); break; case OPT_WARM: sprintf(stropts, "WARM"); break; } printf("Nodes = %s\n", stropts); printf("Mask = /%d\n", mask_length); /* Start up our XML-RPC client library. */ xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS, NAME, VERSION); /* Initialize our error-handling environment. */ xmlrpc_env_init(&env); /* prototype: xmlrpc_value * xmlrpc_client_call(xmlrpc_env * const envP, const char * const server_url, const char * const method_name, const char * const format, ...); */ asprintf(&uri, "http://%s/RPC2", argv[uri_pos]); resultP = xmlrpc_client_call(&env, uri, "pike.top", "(s)", stropts); free(uri); die_if_fault_occurred_line(&env, __LINE__); /* parse returned structure */ if ( xmlrpc_value_type(resultP) != XMLRPC_TYPE_STRUCT ) { printf("unexpected result - should be structure\n"); xmlrpc_env_clean(&env); xmlrpc_client_cleanup(); exit(1); } struct_size = xmlrpc_struct_size(&env, resultP); die_if_fault_occurred_line(&env, __LINE__); // printf("Struct size: %d\n", struct_size); if ( ! get_int_from_struct_by_name(resultP, MAX_HITS, &max_hits) ) { fprintf(stderr, "ERROR: %s not foung in result\n", MAX_HITS); exit (1); } printf("max_hits = %d\n", max_hits); if ( ! get_int_from_struct_by_name(resultP, NUMBER_OF_ROWS, &rows) ) { fprintf(stderr, "ERROR: %s not foung in result\n", NUMBER_OF_ROWS); exit (1); } printf("rows = %d\n", rows); TopItem top_items[rows]; TopItem *item; /* tmp item ptr */ TopItem *result_items = top_items; /* if no aggregation use this */ memset(top_items, 0, sizeof(top_items)); /* aggregated values */ if ( rows == 0 ) return 0; for ( i = 0, item = top_items; i < rows; ++i, ++item ) { if ( ! read_row(resultP, i, item) ) { fprintf(stderr, "ERROR: while reading row number %d\n", i); } /* fill in ipv4 addr */ // printf("item[%d].ip_addr = %s, len = %d\n", i, item->ip_addr, strlen(item->ip_addr)); rv = inet_pton(AF_INET, item->ip_addr, &item->ipv4_addr); if ( rv > 0 ) { // printf("IPv4 addr: %x\n", item->ipv4_addr); } else { fprintf(stderr, "IP conversion failed - not an IPv4 address: '%s'\n", item->ip_addr); /* conversion failed from any reason */ printf("item[%d].ipv4_addr = %x\n", i, item->ipv4_addr); } } assert( rows > 0 ); /* if IP mask length is shorter than 32 then aggregate list according to the mask */ if ( mask_length < 32 ) { uint32_t ip_mask = htonl(mask(mask_length)); qsort(top_items, rows, sizeof(TopItem), compare_TopItem_ipv4_addr); /* sort by IPv4 */ /* skip items without ipv4 address */ i = 0; /* index of non aggregated items */ while (!top_items[i].ipv4_addr && i < rows ) { printf("Skip item[%d] - do not has IPv4 address: %s\n", i, top_items[i].ip_addr); memset(&top_items[i], 0, sizeof(TopItem)); ++i; } j = 0; /* index of aggregated items */ if ( i == 0 ) ++i; top_items[0].ipv4_addr &= ip_mask; top_items[0].num_of_ips = 1; inet_ntop(AF_INET, &top_items[0].ipv4_addr, top_items[0].ip_addr, sizeof(top_items[0].ip_addr)); while ( i < rows ) { top_items[i].ipv4_addr &= ip_mask; if ( top_items[j].ipv4_addr == top_items[i].ipv4_addr ) { top_items[j].leaf_hits[0] += top_items[i].leaf_hits[0]; top_items[j].leaf_hits[1] += top_items[i].leaf_hits[1]; ++(top_items[j].num_of_ips); ++i; } else { ++j; top_items[j] = top_items[i]; top_items[j].num_of_ips = 1; inet_ntop(AF_INET, &top_items[j].ipv4_addr, top_items[j].ip_addr, sizeof(top_items[j].ip_addr)); ++i; } } rows = j + 1; } qsort(top_items, rows, sizeof(TopItem), compare_TopItem_hits_reverse); print_rows( top_items, rows, mask_length ); /* Dispose of our result value. */ xmlrpc_DECREF(resultP); /* Clean up our error-handling environment. */ xmlrpc_env_clean(&env); /* Shutdown our XML-RPC client library. */ xmlrpc_client_cleanup(); return 0; } kamailio-4.0.4/utils/pike_top/INSTALL0000644000000000000000000000041412223032460015777 0ustar rootrootYou must not have installed libxmlrpc-c from Debian repo. It is very old version with old API. Prerequisities: libxmlrpc-c ver. 1.03 (http://xmlrpc-c.sourceforge.net/) Makefile suppose that it is installed in /opt/xmlrpc directory, if not customize you should it. kamailio-4.0.4/utils/pike_top/Makefile0000644000000000000000000000040112223032460016402 0ustar rootrootLIBXMLRPC_BASE = /opt/xmlrpc CFLAGS = `$(LIBXMLRPC_BASE)/bin/xmlrpc-c-config client --cflags` LIBS = `$(LIBXMLRPC_BASE)/bin/xmlrpc-c-config client --libs` CC = gcc .PHONY = pike_top pike_top: pike_top.c $(CC) -g pike_top.c -o pike_top $(CFLAGS) $(LIBS) kamailio-4.0.4/utils/README-UTILS0000644000000000000000000000070512223032460014715 0ustar rootrootThis directory contains various utility programs for SIP-router db_berkeley recovery for berkeley_db module db_oracle ???? fifo_relay PHP script to provide network based fifo access kamctl Script to communicate with Kamailio over the MI interface kamunix Kamailio UNIX socket wrapper pdbt ???? pike_top SER mod_pike top console profile ???? route_graph ???? kamcmd Software to communicate with Kamailio using the binRPC interface sipgrep ???? kamailio-4.0.4/utils/route_graph/0000755000000000000000000000000012223032460015454 5ustar rootrootkamailio-4.0.4/utils/route_graph/route_graph.py0000755000000000000000000001230312223032460020347 0ustar rootroot#! /usr/bin/env python """ * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * """ import sys,re max_depth = 10 debug = 0 re_main_route = re.compile("^([a-z]+_)*route[\s\t]*(?![\(\)])[\s\t]*\{?", re.I) re_def_route = re.compile("^([a-z]+_)*route(\[\"?([A-Za-z0-9-_:]+)\"?\])+[\s\t]*\{?", re.I) re_call_route = re.compile("^(.*\([\s\t!]*)?route\(\"?([A-Za-z0-9-_]+)\"?\)", re.I) routes = {} f_routes = {} b_routes = {} r_routes = {} s_routes = {} e_routes = {} def log(_s): if debug: print _s def print_route_level(_l, _n): log("prl: %i, %s" % (_l, _n)) if _l > max_depth: return spacer = "" route = "" for i in range(_l): spacer += "| " if i < _l - 1: route += "| " else: route += "\- " + str(_n) if len(spacer) > 0: print spacer if len(route) > 0: print route def traverse_routes(_level, _name): log("tr: %i, %s" % (_level, _name)) if _level > max_depth: print "warning: max_depth reached" return print_route_level(_level, _name) if routes.has_key(_name): for r in routes[_name]: traverse_routes(_level + 1, r) if len(sys.argv) < 2: raise "usage: %s configuration-file [max_depth]" % sys.argv[0] if len(sys.argv) == 3: max_depth = int(sys.argv[2]) cfg = file(sys.argv[1], "r") if cfg == None: raise "Missing config file" line = cfg.readline() rt = routes while line: line = line.strip() if not line.startswith("#"): log(line) main_match = re_main_route.search(line) def_match = re_def_route.search(line) call_match = re_call_route.search(line) if not call_match == None: log("CALL: " + line) name = call_match.group(2) log(rname +":"+name) rt[rname].append(name) elif not def_match == None: log("DEF: " + line) rtype = def_match.group(1) rname = def_match.group(3) if rtype == "failure_": rt = f_routes if rname == None: rname = "failure" elif rtype == "onreply_": rt = r_routes if rname == None: rname = "onreply" elif rtype == "onsend_": rt = s_routes if rname == None: rname = "onsend" elif rtype == "branch_": rt = b_routes if rname == None: rname = "branch" elif rtype == "event_": rt = e_routes if rname == None: rname = "event" else: rt = routes log(rname) rt[rname] = [] elif not main_match == None: log("MAIN: " + line) rtype = main_match.group(1) if rtype == "failure_": rt = f_routes rname = "failure" elif rtype == "onreply_": rt = r_routes rname = "onreply" elif rtype == "onsend_": rt = s_routes rname = "onsend" elif rtype == "branch_": rt = b_routes rname = "branch" elif rtype == "event_": rt = e_routes rname = "event" else: rt = routes rname = "Main" log(rname) rt[rname] = [] line = cfg.readline() log("routes: %s" % (routes)) log("branch_routes: %s" % (b_routes)) log("failure_routes: %s" % (f_routes)) log("onreply_routes: %s" % (r_routes)) log("onsend_routes: %s" % (s_routes)) log("event_routes: %s" % (e_routes)) for name in routes.keys(): for val in routes[name]: if not routes.has_key(val): print "Missing Route %s!!!" % val # checking for unreferenced routes does not work yet because functions # can call routes as well?! #found = False #for n in routes.keys(): # for v in routes[n]: # if v == name: # found = True #if not found and (not (name == "Main" or name == "Failure" or name == "Onreply" or name == "Branch")): # print "Unreferenced Route %s!!!" % name print "\nMain" traverse_routes(0, "Main") if len(b_routes) > 0: print "\nBranch routes\n-------------" for br in b_routes.keys(): print "\n%s" % (br) for r in b_routes[br]: traverse_routes(1, r) if len(s_routes) > 0: print "\nSend routes\n-----------" for sr in s_routes.keys(): print "\n%s" % (sr) for r in s_routes[sr]: traverse_routes(1, r) if len(f_routes) > 0: print "\nFailure routes\n--------------" for fr in f_routes.keys(): print "\n%s" % (fr) for r in f_routes[fr]: traverse_routes(1, r) if len(r_routes) > 0: print "\nOnreply routes\n--------------" for onr in r_routes.keys(): print "\n%s" % (onr) for r in r_routes[onr]: traverse_routes(1, r) if len(e_routes) > 0: print "\nEvent routes\n--------------" for onr in e_routes.keys(): print "\n%s" % (onr) for r in e_routes[onr]: traverse_routes(1, r) print kamailio-4.0.4/utils/misc/0000755000000000000000000000000012223032460014070 5ustar rootrootkamailio-4.0.4/utils/misc/vim/0000755000000000000000000000000012223032460014663 5ustar rootrootkamailio-4.0.4/utils/misc/vim/ftdetect/0000755000000000000000000000000012223032460016465 5ustar rootrootkamailio-4.0.4/utils/misc/vim/ftdetect/ser.vim0000644000000000000000000000143212223032460017773 0ustar rootroot" Copy this file to $HOME/.vim/ftdetect/ser.vim func! s:cfgType() let max = line("$") > 400 ? 400 : line("$") for n in range(1, max) if getline(n) =~ '^\s*#!\(KAMAILIO\|OPENSER\|SER\|ALL\|MAXCOMPAT\)' set filetype=ser return elseif getline(n) =~ '^\s*#!\(define\|ifdef\|endif\|subst\|substdef\)' set filetype=ser return elseif getline(n) =~ '^\s*!!\(define\|ifdef\|endif\|subst\|substdef\)' set filetype=ser return elseif getline(n) =~ '^\s*modparam\s*(\s*"[^"]\+"' set filetype=ser return elseif getline(n) =~ '^\s*route\s*{\s*' set filetype=ser return endif endfor setf cfg endfunc au BufNewFile,BufRead *.cfg call s:cfgType() kamailio-4.0.4/utils/misc/vim/syntax/0000755000000000000000000000000012223032460016211 5ustar rootrootkamailio-4.0.4/utils/misc/vim/syntax/ser.vim0000644000000000000000000001527112223032460017525 0ustar rootroot" -*- vim -*- " FILE: ser.vim " LAST MODIFICATION: 2009-05-28 18:30 " (C) Copyright 2008 StanisÅ‚aw Pitucha " (C) Copyright 2009-2010 Daniel-Constantin Mierla " Version: 1.02 " USAGE: " " Save this file to $VIMFILES/syntax/ser.vim. Either add a detection " script to filetypes.vim, or set filetype manually to "ser" when " editing SIP Router configuration file with 'setf ser'. " " List of keyword and core functions taken from latest dev version of " SIP Router. Module functions not included. " " Tested only on vim 7.1 " " Example: "setf ser" " " REQUIREMENTS: " vim (>= 7) if exists("b:current_syntax") finish endif syn match serConfigParamLine '^[^=]\+=.*$' contains=serCoreParameter,serString,serConfigConstant,serSpecial,serNumber,serCppComment,serHashComment syn region serConfigModparam start='^\s*modparam\s*(' end=')' contains=serString,serNumber syn match serConfigModule '^\s*loadmodule\s*"[^"]\+"' contains=serString syn keyword serTodo TODO FIXME XXX contained syn match serOperator '!\|&&\|||\|=[~=]\?\|>\|<\|+\|-\|/\|\*\||\|&\|^\|\~\|defined\|eq\|ieq\|ne\|ine\|mod' display contained syn region serCppComment start='/\*' end='\*/' contains=serTodo syn match serHashDefine '#!define\s\|#!ifdef\s\|#!ifndef\s\|#!endif\|#!else\|#!substdef\|#!subst\|!!define\s\|!!ifdef\s\|!!ifndef\s\|!!endif\|!!else\|!!substdef\|!!subst\|#!KAMAILIO\|#!OPENSER\|#!SER\|#!MAXCOMPAT\|#!ALL' " syn match serHashDefine '^\s*#!.+$' syn match serHashComment '#[^!].*$\|#$' contains=serTodo syn match serStringEscape '\\.' contained syn match serNumber '[0-9]\+' contained syn region serString matchgroup=Normal start='"' skip='\\"' end='"' contained contains=serVariable,serStringEscape syn match serVariable "$[a-zA-Z_][a-zA-Z0-9_]*\(([^)]\+)\)\?" contained syn match serIdentifier '[a-zA-Z_][a-zA-Z0-9_]*' contained syn keyword serStatement route if else switch case default break exit return drop while include_file import_file contained syn keyword serSpecial yes no on off true false enabled disabled contained syn keyword serCoreKeyword af dst_ip dst_port from_uri method msg:len proto status snd_af snd_ip snd_port snd_proto src_ip src_port to_af to_ip to_port to_proto to_uri uri uri:host uri:port contained syn keyword serCoreValue udp UDP tcp TCP tls TLS sctp SCTP inet INET inet6 INET6 sslv23 SSLv23 SSLV23 sslv2 SSLv2 SSLV2 sslv3 SSLv3 SSLV3 tlsv1 TLSv1 TLSV1 max_len myself contained syn keyword serCoreFunction forward forward_tcp forward_udp forward_tls forward_sctp send send_tcp log error exec force_rport add_rport force_tcp_alias add_tcp_alias udp_mtu udp_mtu_try_proto setflag resetflag isflagset flags bool setavpflag resetavpflag isavpflagset avpflags rewritehost sethost seth rewritehostport sethostport sethp rewritehostporttrans sethostporttrans sethpt rewriteuser setuser setu rewriteuserpass setuserpass setup rewriteport setport setp rewriteuri seturi revert_uri prefix strip strip_tail userphone append_branch set_advertised_address set_advertised_port force_send_socket contained syn keyword serCoreParameter debug fork log_stderror log_facility log_name log_color listen alias auto_aliases dns rev_dns dns_try_ipv6 dns_try_naptr dns_srv_lb dns_srv_loadbalancing dns_udp_pref dns_udp_preference dns_tcp_pref dns_tcp_preference dns_tls_pref dns_tls_preference dns_sctp_pref dns_sctp_preference dns_retr_time dns_retr_no dns_servers_no dns_use_search_list dns_search_full_match dns_cache_init use_dns_cache use_dns_failover dns_cache_flags dns_cache_negative_ttl dns_cache_min_ttl dns_cache_max_ttl dns_cache_mem dns_cache_gc_interval dns_cache_del_nonexp dns_cache_delete_nonexpired dst_blacklist_init use_dst_blacklist dst_blacklist_mem dst_blacklist_expire dst_blacklist_ttl dst_blacklist_gc_interval port statistics maxbuffer children check_via phone2tel syn_branch memlog mem_log memdbg mem_dbg sip_warning server_signature reply_to_via user uid group gid chroot workdir wdir mhomed disable_tcp tcp_children tcp_accept_aliases tcp_send_timeout tcp_connect_timeout tcp_connection_lifetime tcp_poll_method tcp_max_connections tcp_no_connect tcp_source_ipv4 tcp_source_ipv6 tcp_fd_cache tcp_buf_write tcp_async tcp_conn_wq_max tcp_wq_max tcp_rd_buf_size tcp_wq_blk_size tcp_defer_accept tcp_delayed_ack tcp_syncnt tcp_linger2 tcp_keepalive tcp_keepidle tcp_keepintvl tcp_keepcnt tcp_crlf_ping disable_tls tls_disable enable_tls tls_enable tlslog tls_log tls_port_no tls_method tls_verify tls_require_certificate tls_certificate tls_private_key tls_ca_list tls_handshake_timeout tls_send_timeout disable_sctp enable_sctp sctp_children sctp_socket_rcvbuf sctp_socket_receive_buffer sctp_socket_sndbuf sctp_socket_send_buffer sctp_autoclose sctp_send_ttl sctp_send_retries socket_workers advertised_address advertised_port disable_core_dump open_files_limit shm_force_alloc mlock_pages real_time rt_prio rt_policy rt_timer1_prio rt_fast_timer_prio rt_ftimer_prio rt_timer1_policy rt_ftimer_policy rt_timer2_prio rt_stimer_prio rt_timer2_policy rt_stimer_policy mcast_loopback mcast_ttl tos pmtu_discovery exit_timeout ser_kill_timeout max_while_loops stun_refresh_interval stun_allow_stun stun_allow_fp server_id description descr desc loadpath mpath fork_delay modinit_delay http_reply_hack latency_limit_action latency_limit_db latency_limit_db mem_join mem_safety msg_time tcp_clone_rcvbuf tls_max_connections contained syn region serBlock start='{' end='}' contained contains=serBlock,@serCodeElements syn match serRouteBlock '\(failure_\|onreply_\|branch_\|event_\|onsend_\|request_\|reply_\)\?route\(\s*\[[^\]]\+\]\)\?' contained contains=serNumber,serString,serIdentifier syn region serRrouteBlockFold matchgroup=serRouteBlock start="\(failure_\|onreply_\|branch_\|event_\|onsend_\|request_\|reply_\)\?route\(\s*\[[^\]]\+\]\)\?\s*\n\?{" matchgroup=NONE end="}" contains=serBlock,@serCodeElements syn cluster serCodeElements contains=serHashDefine,serCppComment,serHashComment,serNumber,serString,serVariable,serOperator,serStatement,serKeyword,serCoreKeyword,serCoreValue,serCoreFunction,serIdentifier hi def link serCppComment Comment hi def link serHashComment Comment hi def link serHashDefine Special hi def link serTodo Todo hi def link serConfigModparam Function hi def link serConfigModule Keyword hi def link serKeyword Keyword hi def link serCoreKeyword Special hi def link serCoreValue Special hi def link serCoreFunction Function hi def link serRouteBlock Type hi def link serRrouteBlockFold Type hi def link serIdentifier Identifier hi def link serSpecial Special hi def link serCoreParameter Keyword hi def link serOperator Operator hi def link serStatement Conditional hi def link serNumber Number hi def link serVariable Identifier hi def link serString String hi def link serStringEscape Special let b:current_syntax = "ser" kamailio-4.0.4/utils/misc/vim/README0000644000000000000000000000103312223032460015540 0ustar rootrootOverview -------- Vim scripts to auto-detect SER and Kamailio configuration files and enable syntax highlighting for them. Install ------- Run 'make install' in this folder, or: - copy ftdetect/ser.vim to ~/.vim/ftdetect/ser.vim - copy syntax/ser.vim to ~/.vim/syntax/ser.vim Usage ----- - autodetection is based on .cfg extension and match of #!SER, #!KAMAILIO (and other variants), modparam or route keywords in first 400 lines of file. You can enable syntax highlighting for files not auto-detected with vim command: "setf ser". kamailio-4.0.4/utils/misc/vim/Makefile0000644000000000000000000000021112223032460016315 0ustar rootrootall: install: mkdir -p ~/.vim/ftdetect mkdir -p ~/.vim/syntax cp ftdetect/ser.vim ~/.vim/ftdetect/ cp syntax/ser.vim ~/.vim/syntax/ kamailio-4.0.4/utils/kamctl/0000755000000000000000000000000012223032461014411 5ustar rootrootkamailio-4.0.4/utils/kamctl/kamdbctl.db_berkeley0000644000000000000000000003413512223032460020370 0ustar rootroot# # $Id$ # # Script for maintaining Kamailio Berkeley DB tables # Copyright (C) 2007 Cisco Systems # # This file is part of Kamailio, a free SIP server. # # Kamailio is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # Kamailio is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # History: # -------- # 2007-09-19 genesis (wiquan) # #constants PATH=$PATH:/usr/local/BerkeleyDB.4.6/bin DELIM="|" BACKUP_CMD="tar czvf " RESTORE_CMD="tar xzvf " #berkeley db utility program that writes out db to plain text #small hack to autodetect the db dump command, debian prefix the version.. which db_dump > /dev/null ret=$? if [ $ret -eq 0 ] ; then DUMP_CMD="db_dump" fi ; which db4.4_dump > /dev/null ret=$? if [ $ret -eq 0 ] ; then DUMP_CMD="db4.4_dump" fi ; which db4.5_dump > /dev/null ret=$? if [ $ret -eq 0 ] ; then DUMP_CMD="db4.5_dump" fi ; which db4.6_dump > /dev/null ret=$? if [ $ret -eq 0 ] ; then DUMP_CMD="db4.6_dump" fi ; #berkeley db utility program that imports data from plain text file #small hack to autodetect the db load command, debian prefix the version.. which db_load > /dev/null ret=$? if [ $ret -eq 0 ] ; then LOAD_CMD="db_load" fi ; which db4.4_load > /dev/null ret=$? if [ $ret -eq 0 ] ; then LOAD_CMD="db4.4_load" fi ; which db4.5_load > /dev/null ret=$? if [ $ret -eq 0 ] ; then LOAD_CMD="db4.5_load" fi ; which db4.6_load > /dev/null ret=$? if [ $ret -eq 0 ] ; then LOAD_CMD="db4.6_load" fi ; # path to the database schemas DATA_DIR="/usr/local/share/kamailio" if [ -d "$DATA_DIR/db_berkeley/kamailio" ] ; then DB_SCHEMA="$DATA_DIR/db_berkeley/kamailio" else DB_SCHEMA="./db_berkeley/kamailio" fi # path to the db_berkeley database if [ -z "$DB_PATH" ]; then DB_PATH="/usr/local/etc/kamailio/db_berkeley" fi berkeley_usage() { COMMAND=`basename $0` cat < (db_dump the underlying db file to STDOUT) $COMMAND swap (installs db.new by db -> db.old; db.new -> db) $COMMAND append (appends data to an existing db;output DB_PATH/db.new) $COMMAND newappend (appends data to a new instance of db; output DB_PATH/db.new) $COMMAND export (exports table data to plain-txt files in dump_dir) $COMMAND import (imports plain-txt table data and creates new db tables in db_path) EOF } #usage # # # kamailio_berkeley() # parms: { case $1 in list|ls) ls -l $DB_PATH exit $? ;; cat) shift kamailio_cat $1 $DB_PATH exit $? ;; swap) shift kamailio_swap $1 $DB_PATH exit $? ;; append) shift kamailio_append $1 $2 $DB_PATH exit $? ;; newappend) shift kamailio_newappend $1 $2 $DB_PATH exit $? ;; export) shift kamailio_export $1 $DB_PATH exit $? ;; migrate) shift kamailio_migrate $1 $DB_PATH exit $? ;; import) shift kamailio_import $1 $DB_PATH exit $? ;; *) berkeley_usage exit 1; ;; esac } ## # EXPORT existing data to plain-txt files in DUMP_DIR # eg. DB_PATH/version ---> DUMP_DIR/version.txt # # Export is used as part of a DB migration process to another # major version of berkeley db. kamailio_export() # parms: [DB_PATH] { if [ $# -lt 2 ]; then echo "kamailio_dump parms: [DB_PATH]" exit 1 fi # Assert: the DB_PATH directory should already exist if [ ! -d $2 ] ; then merr "BerkeleyDB directory does not exist at: [$2]" exit 1 fi # Assert: DB_PATH directory should already contain table 'version' if [ ! -f $2/version ] ; then merr "BerkeleyDB directory does not have VERSION table at: [$2]" exit 1 fi # Create dir at to store the exported data if [ ! -d $1 ] ; then minfo "creating DUMP_DIR at: [$1]" mkdir -p $1 else mdbg "Cleaning out DUMP_DIR to get ready for new data" rm -rf $1/* fi # DUMP_CMD will result in something like this: # # VERSION=3 # format=print # type=hash # h_nelem=2 # db_pagesize=4096 # HEADER=END # METADATA_COLUMNS # callid(str) method(str) from_tag(str) to_tag(str) sip_code(str) sip_reason(str) time(datetime) # METADATA_KEY # 0 # DATA=END # # However, we are only interested in the indented stuff between # 'HEADER=END' and 'DATA=END', # as everything else is DB instance specific. That is, we are interested in this part: # # METADATA_COLUMNS # callid(str) method(str) from_tag(str) to_tag(str) sip_code(str) sip_reason(str) time(datetime) # METADATA_KEY # 0 # # The following PERL filter will do this processing. # # perl -pe 's/^\w.*// ; s/^\s(.*)/$1/' # Dump the STANDARD tables to plain-text files in DUMP_DIR for TABLE in $STANDARD_TABLES; do if [ -f $2/$TABLE ] ; then mdbg "Exporting standard table: $TABLE" $DUMP_CMD -p -h $2 $TABLE | perl -pe 's/^\w.*// ; s/^\s(.*)/$1/' > $1/$TABLE.txt # Check return code to make sure the export worked ok if [ $? -ne 0 ] ; then merr "Export of standard table failed [$TABLE]" # there was a problem, but it is not something # we can handle here; We can deal with this at import # time. fi else mwarn "Table not found: [$TABLE]" fi done # Dump the PRESENCE tables to plain-text files in DUMP_DIR for TABLE in $PRESENCE_TABLES; do if [ -f $2/$TABLE ] ; then mdbg "Exporting presence table: $TABLE" $DUMP_CMD -p -h $2 $TABLE | perl -pe 's/^\w.*// ; s/^\s(.*)/$1/' > $1/$TABLE.txt if [ $? -ne 0 ] ; then merr "Export of presence table failed [$TABLE]" fi else mwarn "Table not found: [$TABLE]" fi done # Dump the EXTRA tables to plain-text files in DUMP_DIR for TABLE in $EXTRA_TABLES; do if [ -f $2/$TABLE ] ; then mdbg "Exporting extra table: $TABLE" $DUMP_CMD -p -h $2 $TABLE | perl -pe 's/^\w.*// ; s/^\s(.*)/$1/' > $1/$TABLE.txt if [ $? -ne 0 ] ; then merr "Export of extra table failed [$TABLE]" fi else mwarn "Table not found: [$TABLE]" fi done mdbg "All tables are now exported to DUMP_DIR: [$1]" return 0 } ## # MIGRATE (schema) # Examine each plain-txt file in DUMP_DIR # (Assumes that kamailio_export was already invoked) # # Migrate converts data from schema-old to schema-new in place. # # After this step is complete the IMPORT should be executed. kamailio_migrate() # parms: [DB_PATH] { merr "db_berkeley migrate not implemented" exit 1 } ## # IMPORT existing plain-txt files from DUMP_DIR to DB_PATH # eg. DUMP_DIR/version.txt --> DB_PATH/version # # import is used as part of DB migrate to another major version of berkeley db. # this will over-write anything in DB_PATH kamailio_import() # parms: [DB_PATH] { if [ $# -lt 2 ]; then echo "kamailio_dump parms: [DB_PATH]" exit 1 fi # Assert: DUMP_DIR (source dir) already exists if [ ! -d $1 ] ; then merr "Berkeley DUMP_DIR directory does not exist: [$1]" exit 1; fi # Assert: DUMP_DIR directory should already contain table 'version.txt' if [ ! -f $1/version.txt ] ; then merr "DUMP_DIR directory does not have VERSION.txt data at: [$1]" exit 1 fi # Assert: destination dir exists [DB_PATH] if [ ! -d $2 ] ; then mdbg "Berkeley DB_PATH directory is being created: [$2]" mkdir -p $2 else # Wipe out the destination dir to make room for new data mwarn "Berkeley DB_PATH directory is being purged at: [$2]" rm -rf $2/* fi # Creates STANDARD tables from plain-text files in DUMP_DIR for TABLE in $STANDARD_TABLES; do if [ -s $1/$TABLE.txt ] ; then mdbg "Importing standard table: $TABLE" $LOAD_CMD -T -t hash -f $1/$TABLE.txt -h $2 $TABLE # Check return code to make sure the export worked ok if [ $? -ne 0 ] ; then merr "Import of standard table failed [$TABLE.txt]" merr "Create this missing table with kambdb_recover." fi else merr "Import data not found for table: [$TABLE.txt]" merr "Create this missing table with kambdb_recover." fi done # Creates PRESENCE tables from plain-text files in DUMP_DIR for TABLE in $PRESENCE_TABLES; do if [ -s $1/$TABLE.txt ] ; then mdbg "Importing presence table: $TABLE" $LOAD_CMD -T -t hash -f $1/$TABLE.txt -h $2 $TABLE # Check return code to make sure the export worked ok if [ $? -ne 0 ] ; then merr "Import of presence table failed [$TABLE.txt]" merr "Create this missing table with kambdb_recover." fi else mwarn "Import data not found for table: [$TABLE.txt]" fi done # Creates EXTRA tables from plain-text files in DUMP_DIR for TABLE in $EXTRA_TABLES; do if [ -s $1/$TABLE.txt ] ; then mdbg "Importing extra table: $TABLE" $LOAD_CMD -T -t hash -f $1/$TABLE.txt -h $2 $TABLE # Check return code to make sure the export worked ok if [ $? -ne 0 ] ; then merr "Import of extra table failed [$TABLE.txt]" merr "Create this missing table with kambdb_recover." fi else mwarn "Import data not found for table: [$TABLE.txt]" fi done mdbg "All tables are now imported to DB_PATH: [$2]" return 0 } kamailio_swap() # parms: [DB_PATH] { if [ $# -lt 2 ]; then echo "kamailio_swap parms: [DB_PATH]" exit 1 fi DB=$2/$1 DBNEW=$DB.new DBOLD=$DB.old cp $DB $DBOLD mv $DBNEW $DB } ##### # append process is: # 1. copy DB_PATH/db to DB_PATH/db.new # 2. appends contents of newdata to DB_PATH/db.new # kamailio_append() # parms: [DB_PATH] { if [ $# -lt 3 ]; then echo "kamailio_append parms: [DB_PATH]" exit 1 fi DB=$3/$1 DBNEW=$DB.new if [ -f $DB.new ] ; then rm $DB.new fi cp $DB $DBNEW # echo "$LOAD_CMD -T -t hash -f $2 -h $3 $1.new" $LOAD_CMD -T -t hash -f $2 -h $3 $1.new # echo "$LOAD_CMD -r fileid -h $3 $1.new" $LOAD_CMD -r fileid -h $3 $1.new } ##### # newappend process is: # 1. create a new temp DBENV in /tmp/sc- # 2. appends contents of newdata to /tmp/sc-/db # 3. move /tmp/sc-/db over to DB_PATH/db.new # 4. delete temp DBENV dir /tmp/sc- # kamailio_newappend() # parms: [DB_PATH] { if [ $# -lt 3 ]; then echo "kamailio_append parms: [DB_PATH]" exit 1 fi DB=$3/$1 DBNEW=$DB.new if [ -f $DBNEW ] ; then rm $DBNEW fi TMPENV=/tmp/sc-$$ kamailio_create $TMPENV cd $OLDPWD $LOAD_CMD -T -t hash -f $2 -h $TMPENV $1 mv $TMPENV/$1 $DBNEW rm -rf $TMPENV } # cat all rows to STDOUT kamailio_cat() # pars: { if [ $# -ne 2 ] ; then echo "kamailio_cat params [DB_PATH]" exit 1 fi $DUMP_CMD -p -h $2 $1 } kamailio_drop() # pars: { if [ $# -ne 1 ] ; then echo "kamailio_drop function takes one param" exit 1 fi if [ ! -d $1 ] ; then echo "Directory does not exist: $1" fi minfo "Dropping Berkeley DB database at: $1 ..." # core if [ -f $1/version ] ; then for TABLE in $STANDARD_TABLES; do mdbg "Dropping core table: $TABLE" rm -f $1/$TABLE done fi # presence if [ -f $1/presentity ] ; then for TABLE in $PRESENCE_TABLES; do mdbg "Dropping presence table: $TABLE" rm -f $1/$TABLE done fi # extra tables if [ -f $1/cpl ] ; then for TABLE in $EXTRA_TABLES; do mdbg "Dropping extra table: $TABLE" rm -f $1/$TABLE done fi # delete db files and directory rm -rf $1/__db.001 rm -rf $1/__db.002 rm -rf $1/__db.003 rm -rf $1/__db.004 rmdir $1 } kamailio_create() # pars: { if [ $# -ne 1 ] ; then echo "kamailio_create param [DB_PATH]" exit 1 fi DB_PATH=$1 if [ ! -d $1 ] ; then minfo "creating Berkeley DB database at: [$1]" mkdir -p $DB_PATH fi for TABLE in $STANDARD_TABLES; do mdbg "Creating standard table: $TABLE" $LOAD_CMD -T -t hash -f $DB_SCHEMA/$TABLE -h $1 $TABLE if [ $? -ne 0 ] ; then merr "Creating standard tables failed!" exit 1 fi done get_answer $INSTALL_PRESENCE_TABLES "Install presence related tables? (y/n): " if [ "$ANSWER" = "y" ]; then presence_create $1 fi get_answer $INSTALL_EXTRA_TABLES "Install tables for $EXTRA_MODULES? (y/n): " if [ "$ANSWER" = "y" ]; then extra_create $1 fi } # kamailio_create presence_create() # pars: { if [ $# -ne 1 ] ; then merr "presence_create param [DB_PATH]" exit 1 fi DB_PATH=$1 if [ ! -d $1 ] ; then # Assert: the directory should already exist merr "BerkeleyDB directory does not exist at: [$1]" exit 1 fi if [ ! -f $1/version ] ; then # Assert: directory should already contain table 'version' merr "BerkeleyDB directory does not have VERSION table at: [$1]" exit 1 fi for TABLE in $PRESENCE_TABLES; do mdbg "Creating presence table: $TABLE" $LOAD_CMD -T -t hash -f $DB_SCHEMA/$TABLE -h $1 $TABLE if [ $? -ne 0 ] ; then merr "Creating presence tables failed!" exit 1 fi done } # end presence_create extra_create() # pars: { if [ $# -ne 1 ] ; then merr "extra_create function takes one param (DB_PATH)" exit 1 fi DB_PATH=$1 if [ ! -d $1 ] ; then # Assert: the directory should already exist merr "BerkeleyDB directory does not exist at: [$1]" exit 1 fi if [ ! -f $1/version ] ; then # Assert: directory should already contain table 'version' merr "BerkeleyDB directory does not have VERSION table at: [$1]" exit 1 fi for TABLE in $EXTRA_TABLES; do mdbg "Creating extra table: $TABLE" $LOAD_CMD -T -t hash -f $DB_SCHEMA/$TABLE -h $1 $TABLE if [ $? -ne 0 ] ; then merr "Creating extra tables failed!" exit 1 fi done } # end extra_create kamailio-4.0.4/utils/kamctl/kamdbctl.sqlite0000644000000000000000000000612412223032460017417 0ustar rootroot# $Id$ # # Script for adding and dropping Kamailio sqlite tables # # path to the database schemas DATA_DIR="/usr/local/share/kamailio" if [ -d "$DATA_DIR/db_sqlite" ] ; then DB_SCHEMA="$DATA_DIR/db_sqlite" else DB_SCHEMA="./db_sqlite" fi ################################################################# # config vars ################################################################# CMD="sqlite3" DUMP_CMD="sql_dump" ################################################################# sql_dump() { $CMD ${1:-$DB_PATH} .dump } # execute sql command with optional db name sql_query() { $CMD "$@" } kamailio_drop() # pars: { if [ $# -ne 1 ] ; then merr "kamailio_drop function takes one param" exit 1 fi if ! rm $1; then merr "Dropping database $1 failed!" exit 1 fi minfo "Database $1 dropped" } #kamailio_drop kamailio_create () # pars: { if [ $# -ne 1 ] ; then merr "kamailio_create function takes one param" exit 1 fi minfo "creating database $1 ..." if [ $? -ne 0 ] ; then merr "Creating database failed!" exit 1 fi #sql_query "$1" "CREATE FUNCTION "concat" (text,text) RETURNS text AS 'SELECT \$1 || \$2;' LANGUAGE 'sql'; # CREATE FUNCTION "rand" () RETURNS double precision AS 'SELECT random();' LANGUAGE 'sql';" # emulate mysql proprietary functions used by the lcr module in postgresql #if [ $? -ne 0 ] ; then # merr "Creating mysql emulation functions failed!" # exit 1 #fi for TABLE in $STANDARD_MODULES; do mdbg "Creating core table: $TABLE" sql_query "$1" < $DB_SCHEMA/$TABLE-create.sql if [ $? -ne 0 ] ; then merr "Creating core tables failed!" exit 1 fi done if [ -e $DB_SCHEMA/extensions-create.sql ] then minfo "Creating custom extensions tables" sql_query $1 < $DB_SCHEMA/extensions-create.sql if [ $? -ne 0 ] ; then merr "Creating custom extensions tables failed!" exit 1 fi fi minfo "Core Kamailio tables succesfully created." get_answer $INSTALL_PRESENCE_TABLES "Install presence related tables? (y/n): " if [ "$ANSWER" = "y" ]; then presence_create $1 fi get_answer $INSTALL_EXTRA_TABLES "Install tables for $EXTRA_MODULES? (y/n): " if [ "$ANSWER" = "y" ]; then extra_create $1 fi } # kamailio_create presence_create () # pars: { if [ $# -ne 1 ] ; then merr "presence_create function takes one param" exit 1 fi minfo "creating presence tables into $1 ..." sql_query "$1" < $DB_SCHEMA/presence-create.sql if [ $? -ne 0 ] ; then merr "Failed to create presence tables!" exit 1 fi sql_query "$1" < $DB_SCHEMA/rls-create.sql if [ $? -ne 0 ] ; then merr "Failed to create rls-presence tables!" exit 1 fi minfo "Presence tables succesfully created." } # end presence_create extra_create () # pars: { if [ $# -ne 1 ] ; then merr "extra_create function takes one param" exit 1 fi minfo "creating extra tables into $1 ..." for TABLE in $EXTRA_MODULES; do mdbg "Creating extra table: $TABLE" sql_query "$1" < $DB_SCHEMA/$TABLE-create.sql if [ $? -ne 0 ] ; then merr "Creating extra tables failed!" exit 1 fi done minfo "Extra tables succesfully created." } # end extra_create kamailio-4.0.4/utils/kamctl/kamdbctl.mysql0000644000000000000000000003150212223032461017262 0ustar rootroot# $Id$ # # Script for adding and dropping Kamailio MySQL tables # # History: # 2006-04-07 removed gen_ha1 dependency - use md5sum; # separated the serweb from kamailio tables; # fixed the reinstall functionality (bogdan) # 2006-05-16 added ability to specify MD5 from a configuration file # FreeBSD does not have the md5sum function (norm) # 2006-09-02 Added address table (juhe) # 2006-10-27 subscriber table cleanup; some columns are created only if # serweb is installed (bogdan) # 2007-02-28 DB migration added (bogdan) # 2007-05-21 Move SQL database definitions out of this script (henning) # 2007-05-31 Move common definitions to kamdbctl.base file (henningw) # 2007-06-11 Use a common control tool for database tasks, like the kamctl # path to the database schemas DATA_DIR="/usr/local/share/kamailio" if [ -d "$DATA_DIR/mysql" ] ; then DB_SCHEMA="$DATA_DIR/mysql" else DB_SCHEMA="./mysql" fi ################################################################# # config vars ################################################################# # full privileges MySQL user if [ -z "$DBROOTUSER" ]; then DBROOTUSER="root" fi # Uncomment this to set the database root password if you want to run this # script without any user prompt. This is unsafe, but useful e.g. for # automatic testing. #PW="" CMD="mysql -h $DBHOST -u$DBROOTUSER " DUMP_CMD="mysqldump -h $DBHOST -u$DBROOTUSER -c -t " ################################################################# # read password prompt_pw() { savetty=`stty -g` echo -n "MySQL password for $DBROOTUSER: " stty -echo read PW stty $savetty echo export PW } # execute sql command with optional db name # and password parameters given sql_query() { if [ $# -gt 1 ] ; then if [ -n "$1" ]; then DB="$1" # no quoting, mysql client don't like this else DB="" fi shift if [ -n "$PW" ]; then $CMD "-p$PW" $DB -e "$@" else $CMD $DB -e "$@" fi else if [ -n "$PW" ]; then $CMD "-p$PW" "$@" else $CMD "$@" fi fi } kamailio_drop() # pars: { if [ $# -ne 1 ] ; then merr "kamailio_drop function takes two params" exit 1 fi sql_query "" "DROP DATABASE $1;" if [ $? -ne 0 ] ; then merr "Dropping database $1 failed!" exit 1 fi minfo "Database $1 deleted" } db_charset_test() { if [ -n "$PW" ]; then CURRCHARSET=`echo "show variables like '%character_set_server%'" | $CMD "-p$PW" | $AWK '{print $2}' | $SED -e 1d` ALLCHARSETS=`echo "show character set" | $CMD "-p$PW" | $AWK '{print $1}' | $SED -e 1d | $GREP -iv -e "utf8\|ucs2"` else CURRCHARSET=`echo "show variables like '%character_set_server%'" | $CMD | $AWK '{print $2}' | $SED -e 1d` ALLCHARSETS=`echo "show character set" | $CMD | $AWK '{print $1}' | $SED -e 1d | $GREP -iv -e "utf8\|ucs2"` fi while [ `echo "$ALLCHARSETS" | $GREP -icw $CURRCHARSET` = "0" ] do mwarn "Your current default mysql characters set cannot be used to create DB. Please choice another one from the following list:" mecho "$ALLCHARSETS" mecho "Enter character set name: " read CURRCHARSET if [ `echo $CURRCHARSET | $GREP -cE "\w+"` = "0" ]; then merr "can't continue: user break" exit 1 fi done CHARSET=$CURRCHARSET } kamailio_db_create () # pars: { if [ $# -ne 1 ] ; then merr "kamailio_db_create function takes one param" exit 1 fi minfo "test server charset" db_charset_test minfo "creating database $1 ..." sql_query "" "CREATE DATABASE $1 CHARACTER SET $CHARSET;" if [ $? -ne 0 ] ; then merr "Creating database $1 failed!" exit 1 fi } kamailio_db_grant () # pars: { if [ $# -ne 1 ] ; then merr "kamailio_db_grant function takes one param" exit 1 fi minfo "granting privileges to database $1 ..." # Users: kamailio is the regular user, kamailioro only for reading sql_query "" "GRANT ALL PRIVILEGES ON $1.* TO '${DBRWUSER}'@'$DBHOST' IDENTIFIED BY '$DBRWPW'; GRANT SELECT ON $1.* TO '${DBROUSER}'@'$DBHOST' IDENTIFIED BY '$DBROPW';" if [ $? -ne 0 ] ; then merr "granting privileges to database $1 failed!" exit 1 fi if [ "$DBHOST" != "localhost" ] ; then sql_query "" "GRANT ALL PRIVILEGES ON $1.* TO '$DBRWUSER'@'localhost' IDENTIFIED BY '$DBRWPW'; GRANT SELECT ON $1.* TO '$DBROUSER'@'localhost' IDENTIFIED BY '$DBROPW';" if [ $? -ne 0 ] ; then merr "granting localhost privileges to database $1 failed!" exit 1 fi fi if [ ! -z "$DBACCESSHOST" ] ; then sql_query "" "GRANT ALL PRIVILEGES ON $1.* TO '$DBRWUSER'@'$DBACCESSHOST' IDENTIFIED BY '$DBRWPW'; GRANT SELECT ON $1.* TO '$DBROUSER'@'$DBACCESSHOST' IDENTIFIED BY '$DBROPW';" if [ $? -ne 0 ] ; then merr "granting access host privileges to database $1 failed!" exit 1 fi fi } kamailio_db_revoke () # pars: { if [ $# -ne 1 ] ; then merr "kamailio_db_revoke function takes one param" exit 1 fi minfo "revoking privileges to database $1 ..." # Users: kamailio is the regular user, kamailioro only for reading sql_query "" "REVOKE ALL PRIVILEGES ON $1.* FROM '${DBRWUSER}'@'$DBHOST'; REVOKE SELECT ON $1.* FROM '${DBROUSER}'@'$DBHOST';" if [ $? -ne 0 ] ; then merr "revoking privileges to database $1 failed!" exit 1 fi if [ "$DBHOST" != "localhost" ] ; then sql_query "" "REVOKE ALL PRIVILEGES ON $1.* FROM '$DBRWUSER'@'localhost'; REVOKE SELECT ON $1.* FROM '$DBROUSER'@'localhost';" if [ $? -ne 0 ] ; then merr "granting localhost privileges to database $1 failed!" exit 1 fi fi if [ ! -z "$DBACCESSHOST" ] ; then sql_query "" "REVOKE ALL PRIVILEGES ON $1.* FROM '$DBRWUSER'@'$DBACCESSHOST'; REVOKE SELECT ON $1.* FROM '$DBROUSER'@'$DBACCESSHOST';" if [ $? -ne 0 ] ; then merr "granting access host privileges to database $1 failed!" exit 1 fi fi } kamailio_create () # pars: { if [ $# -ne 1 ] ; then merr "kamailio_create function takes one param" exit 1 fi kamailio_db_create $1 kamailio_db_grant $1 standard_create $1 get_answer $INSTALL_PRESENCE_TABLES "Install presence related tables? (y/n): " if [ "$ANSWER" = "y" ]; then presence_create $1 fi get_answer $INSTALL_EXTRA_TABLES "Install tables for $EXTRA_MODULES? (y/n): " if [ "$ANSWER" = "y" ]; then HAS_EXTRA="yes" extra_create $1 fi get_answer $INSTALL_DBUID_TABLES "Install tables for $DBUID_MODULES? (y/n): " if [ "$ANSWER" = "y" ]; then HAS_EXTRA="yes" dbuid_create $1 fi } # end kamailio_create standard_create () # pars: { if [ $# -ne 1 ] ; then merr "standard_create function takes one param" exit 1 fi minfo "creating standard tables into $1 ..." for TABLE in $STANDARD_MODULES; do mdbg "Creating core table: $TABLE" sql_query $1 < $DB_SCHEMA/$TABLE-create.sql if [ $? -ne 0 ] ; then merr "Creating core tables failed at $TABLE!" exit 1 fi done minfo "Core Kamailio tables succesfully created." if [ -e $DB_SCHEMA/extensions-create.sql ] then minfo "Creating custom extensions tables" sql_query $1 < $DB_SCHEMA/extensions-create.sql if [ $? -ne 0 ] ; then merr "Creating custom extensions tables failed!" exit 1 fi fi } # end standard_create presence_create () # pars: { if [ $# -ne 1 ] ; then merr "presence_create function takes one param" exit 1 fi minfo "creating presence tables into $1 ..." for TABLE in $PRESENCE_MODULES; do mdbg "Creating presence tables for $TABLE" sql_query $1 < $DB_SCHEMA/$TABLE-create.sql if [ $? -ne 0 ] ; then merr "Creating presence tables failed at $TABLE!" exit 1 fi done minfo "Presence tables succesfully created." } # end presence_create extra_create () # pars: { if [ $# -ne 1 ] ; then merr "extra_create function takes one param" exit 1 fi minfo "creating extra tables into $1 ..." for TABLE in $EXTRA_MODULES; do mdbg "Creating extra table: $TABLE" sql_query $1 < $DB_SCHEMA/$TABLE-create.sql if [ $? -ne 0 ] ; then merr "Creating extra tables failed at $TABLE!" exit 1 fi done minfo "Extra tables succesfully created." } # end extra_create dbuid_create () # pars: { if [ $# -ne 1 ] ; then merr "dbuid_create function takes one param" exit 1 fi minfo "creating uid tables into $1 ..." for TABLE in $DBUID_MODULES; do mdbg "Creating uid table: $TABLE" sql_query $1 < $DB_SCHEMA/$TABLE-create.sql if [ $? -ne 0 ] ; then merr "Creating uid tables failed at $TABLE!" exit 1 fi done minfo "UID tables succesfully created." } # end uid_create migrate_table () # 4 paremeters (dst_table, dst_cols, src_table, src_cols) { if [ $# -ne 4 ] ; then merr "migrate_table function takes 4 params $@" exit 1 fi src_cols=`echo $4 | sed s/?/$3./g ` X=`sql_query "" "INSERT into $1 ($2) SELECT $src_cols from $3;" 2>&1` if [ $? -ne 0 ] ; then echo $X | $GREP "ERROR 1146" > /dev/null if [ $? -eq 0 ] ; then echo " -- Migrating $3 to $1.....SKIPPED (no source)" return 0 fi echo "ERROR: failed to migrate $3 to $1!!!" echo -n "Skip it and continue (y/n)? " read INPUT if [ "$INPUT" = "y" ] || [ "$INPUT" = "Y" ] then return 0 fi exit 1; fi minfo " -- Migrating $3 to $1.....OK" } migrate_db () # 2 parameters (src_db, dst_db) { if [ $# -ne 2 ] ; then merr "migrate_db function takes 2 params" exit 1 fi dst_db=$2 src_db=$1 migrate_table ${dst_db}.acc \ "id,method,from_tag,to_tag,callid,sip_code,sip_reason,time" \ ${src_db}.acc \ "?id,?method,?from_tag,?to_tag,?callid,?sip_code,?sip_reason,?time" migrate_table ${dst_db}.missed_calls \ "id,method,from_tag,to_tag,callid,sip_code,sip_reason,time" \ ${src_db}.missed_calls \ "?id,?method,?from_tag,?to_tag,?callid,?sip_code,?sip_reason,?time" migrate_table ${dst_db}.aliases \ "id,username,domain,contact,expires,q,callid,cseq,last_modified,\ flags,cflags,user_agent" \ ${src_db}.aliases \ "?id,?username,?domain,?contact,?expires,?q,?callid,?cseq,?last_modified,\ ?flags,?cflags,?user_agent" migrate_table ${dst_db}.dbaliases \ "id,alias_username,alias_domain,username,domain" \ ${src_db}.dbaliases \ "?id,?alias_username,?alias_domain,?username,?domain" migrate_table ${dst_db}.grp \ "id,username,domain,grp,last_modified" \ ${src_db}.grp \ "?id,?username,?domain,?grp,?last_modified" migrate_table ${dst_db}.re_grp \ "id,reg_exp,group_id" \ ${src_db}.re_grp \ "?id,?reg_exp,?group_id" migrate_table ${dst_db}.silo \ "id,src_addr,dst_addr,username,domain,inc_time,exp_time,snd_time,\ ctype,body" \ ${src_db}.silo \ "?id,?src_addr,?dst_addr,?username,?domain,?inc_time,?exp_time,?snd_time,\ ?ctype,?body" migrate_table ${dst_db}.domain \ "id,domain,last_modified" \ ${src_db}.domain \ "?id,?domain,?last_modified" migrate_table ${dst_db}.uri \ "id,username,domain,uri_user,last_modified" \ ${src_db}.uri \ "?id,?username,?domain,?uri_user,?last_modified" migrate_table ${dst_db}.usr_preferences \ "id,uuid,username,domain,attribute,type,value,last_modified" \ ${src_db}.usr_preferences \ "?id,?uuid,?username,?domain,?attribute,?type,?value,?last_modified" migrate_table ${dst_db}.trusted \ "id,src_ip,proto,from_pattern,tag" \ ${src_db}.trusted \ "?id,?src_ip,?proto,?from_pattern,?tag" migrate_table ${dst_db}.address \ "id,grp,ip_addr,mask,port" \ ${src_db}.address \ "?id,?grp,?ip_addr,?mask,?port" migrate_table ${dst_db}.speed_dial \ "id,username,domain,sd_username,sd_domain,new_uri,\ fname,lname,description" \ ${src_db}.speed_dial \ "?id,?username,?domain,?sd_username,?sd_domain,?new_uri,\ ?fname,?lname,?description" migrate_table ${dst_db}.gw \ "id,gw_name,grp_id,ip_addr,port,uri_scheme,transport,strip,prefix" \ ${src_db}.gw \ "?id,?gw_name,?grp_id,?ip_addr,?port,?uri_scheme,?transport,?strip,?prefix" migrate_table ${dst_db}.gw_grp \ "grp_id,grp_name" \ ${src_db}.gw_grp \ "?grp_id,?grp_name" migrate_table ${dst_db}.lcr \ "id,prefix,from_uri,grp_id,priority" \ ${src_db}.lcr \ "?id,?prefix,?from_uri,?grp_id,?priority" migrate_table ${dst_db}.pdt \ "id,sdomain,prefix,domain" \ ${src_db}.pdt \ "?id,?sdomain,?prefix,?domain" # migrate subscribers with no serweb support migrate_table ${dst_db}.subscriber \ "id,username,domain,password,ha1,ha1b,rpid" \ ${src_db}.subscriber \ "?id,?username,?domain,?password,?ha1,?ha1b,?rpid" if [ "$HAS_EXTRA" = "yes" ] ; then migrate_table ${dst_db}.cpl \ "id,username,domain,cpl_xml,cpl_bin" \ ${src_db}.cpl \ "?id,?username,?domain,?cpl_xml,?cpl_bin" migrate_table ${dst_db}.siptrace \ "id,date,callid,traced_user,msg,method,status,fromip,toip,\ fromtag,direction" \ ${src_db}.siptrace \ "?id,?date,?callid,?traced_user,?msg,?method,?status,?fromip,?toip,\ ?fromtag,?direction" migrate_table ${dst_db}.imc_rooms \ "id,name,domain,flag" \ ${src_db}.imc_rooms \ "?id,?name,?domain,?flag" migrate_table ${dst_db}.imc_members \ "id,username,domain,room,flag" \ ${src_db}.im_members \ "?id,?username,?domain,?room,?flag" fi } #end migrate_db() export PW if [ "$#" -ne 0 ] && [ "$PW" = "" ]; then prompt_pw fi kamailio-4.0.4/utils/kamctl/kamdbctl0000755000000000000000000002362312223032461016126 0ustar rootroot#!/bin/bash # # $Id$ # # control tool for maintaining Kamailio databases # #=================================================================== PATH=$PATH:/usr/local/sbin/ # for testing only, please don't enable this in production environments # as this introduce security risks TEST="false" ### include resource files, if any if [ -f /etc/kamailio/kamctlrc ]; then . /etc/kamailio/kamctlrc fi if [ -f /usr/local/etc/kamailio/kamctlrc ]; then . /usr/local/etc/kamailio/kamctlrc fi if [ -f ~/.kamctlrc ]; then . ~/.kamctlrc fi if [ $TEST = "true" ]; then if [ -f ./kamctlrc ]; then . ./kamctlrc fi fi ### version for this script VERSION='$Revision$' if [ -z "$MYDIR" ] ; then MYDIR=`dirname $0` fi if [ -z "$MYLIBDIR" ] ; then MYLIBDIR="/usr/local/lib/kamailio/kamctl" if [ ! -d "$MYLIBDIR" ]; then MYLIBDIR=$MYDIR fi fi ##### ------------------------------------------------ ##### ### load base functions # if [ -f "$MYLIBDIR/kamdbctl.base" ]; then . "$MYLIBDIR/kamdbctl.base" else echo -e "Cannot load core functions '$MYLIBDIR/kamdbctl.base' - exiting ...\n" exit -1 fi # ##### ------------------------------------------------ ##### ### DBENGINE # unset USED_DBENGINE if [ -z "$DBENGINE" ] ; then merr "database engine not specified, please setup one in the config script" exit 1 fi case $DBENGINE in MYSQL|mysql|MySQL) if [ -f "$MYLIBDIR/kamdbctl.mysql" ]; then . "$MYLIBDIR/kamdbctl.mysql" USED_DBENGINE="mysql" else merr "could not load the script in $MYLIBDIR/kamdbctl.mysql for database engine $DBENGINE" fi ;; PGSQL|pgsql|postgres|postgresql|POSTGRESQL) if [ -f "$MYLIBDIR/kamdbctl.pgsql" ]; then . "$MYLIBDIR/kamdbctl.pgsql" USED_DBENGINE="postgres" else merr "could not load the script in $MYLIBDIR/kamdbctl.pgsql for database engine $DBENGINE" fi ;; ORACLE|oracle|Oracle) if [ -f "$MYLIBDIR/kamdbctl.oracle" ]; then . "$MYLIBDIR/kamdbctl.oracle" USED_DBENGINE="oracle" else merr "could not load the script in $MYLIBDIR/kamdbctl.oracle for database engine $DBENGINE" fi ;; DBTEXT|dbtext|textdb) if [ -f "$MYLIBDIR/kamdbctl.dbtext" ]; then . "$MYLIBDIR/kamdbctl.dbtext" USED_DBENGINE="dbtext" DBNAME=$DB_PATH else merr "could not load the script in $MYLIBDIR/kamdbctl.dbtext for database engine $DBENGINE" fi ;; DB_BERKELEY|db_berkeley|BERKELEY|berkeley) if [ -f "$MYLIBDIR/kamdbctl.db_berkeley" ]; then . "$MYLIBDIR/kamdbctl.db_berkeley" USED_DBENGINE="berkeley" DBNAME=$DB_PATH else merr "could not load the script in $MYLIBDIR/kamdbctl.db_berkeley for database engine $DBENGINE" fi ;; SQLITE|sqlite) if [ -f "$MYLIBDIR/kamdbctl.sqlite" ]; then . "$MYLIBDIR/kamdbctl.sqlite" USED_DBENGINE="sqlite" DBNAME=$DB_PATH else merr "could not load the script in $MYLIBDIR/kamdbctl.sqlite for database engine $DBENGINE" fi ;; esac if [ -z "$USED_DBENGINE" ] ; then merr "database engine not loaded - tried '$DBENGINE'" exit 1 else mdbg "database engine '$USED_DBENGINE' loaded" fi # dump all rows kamailio_dump() # pars: { if [ $# -ne 2 ] ; then merr "kamailio_dump function takes two param" exit 1 fi if [ "$USED_DBENGINE" == "oracle" ]; then oracle_dump $1 $2 elif [ "$PW" = "" ] ; then $DUMP_CMD $1 > $2 else $DUMP_CMD "-p$PW" $1 > $2 fi if [ "$?" -ne 0 ]; then merr "db dump failed" exit 1 fi minfo "db dump successful" } kamailio_restore() #pars: { if [ $# -ne 2 ] ; then merr "kamailio_restore function takes two params" exit 1 fi if [ "$USED_DBENGINE" == "oracle" ]; then oracle_restore $1 $2 else sql_query $1 < $2 fi if [ "$?" -ne 0 ]; then merr "db restore failed" exit 1 fi minfo "db restore successful" } kamailio_pframework_create() #pars: none { if [ -e $DEFAULT_CFG_DIR/pi_framework_sample ] ; then get_answer ask "Sample already exists. Overwrite? (y/n): " if [ "$ANSWER" != "y" ]; then exit 1 fi fi touch $DEFAULT_CFG_DIR/pi_framework_sample if [ $? -ne 0 ] ; then merr "Unable to create $DEFAULT_CFG_DIR/pi_framework_sample" exit 1 fi if [ -d "$DATA_DIR/xhttp_pi" ] ; then PI_MODULES="$STANDARD_MODULES" else merr "Please install first the xhttp_pi module" exit 1 fi get_answer $INSTALL_EXTRA_TABLES "Add provisionning framework for extra tables? (y/n): " if [ "$ANSWER" = "y" ]; then PI_MODULES="$PI_MODULES $EXTRA_MODULES" fi get_answer $INSTALL_PRESENCE_TABLES "Add provisionning framework for presence tables? (y/n): " if [ "$ANSWER" = "y" ]; then PI_PRESENCE_MODULES="TRUE" fi cat $DATA_DIR/xhttp_pi/pi_framework-00 > $DEFAULT_CFG_DIR/pi_framework_sample for TABLE in $PI_MODULES; do if [ -e $DATA_DIR/xhttp_pi/$TABLE-table ]; then cat $DATA_DIR/xhttp_pi/$TABLE-table >> $DEFAULT_CFG_DIR/pi_framework_sample else merr "Unable to configure: $TABLE - missing table descriptor" fi done if [ "$PI_PRESENCE_MODULES" = "TRUE" ]; then if [ -e $DATA_DIR/xhttp_pi/presence-table ]; then cat $DATA_DIR/xhttp_pi/presence-table >> $DEFAULT_CFG_DIR/pi_framework_sample else merr "Unable to configure: presence - missing table descriptor" fi if [ -e $DATA_DIR/xhttp_pi/rls-table ]; then cat $DATA_DIR/xhttp_pi/rls-table >> $DEFAULT_CFG_DIR/pi_framework_sample else merr "Unable to configure: rls - missing table descriptor" fi fi cat $DATA_DIR/xhttp_pi/pi_framework-01 >> $DEFAULT_CFG_DIR/pi_framework_sample for TABLE in $PI_MODULES; do if [ -e $DATA_DIR/xhttp_pi/$TABLE-mod ]; then cat $DATA_DIR/xhttp_pi/$TABLE-mod >> $DEFAULT_CFG_DIR/pi_framework_sample else merr "Unable to configure: $TABLE - missing mod descriptor" fi done if [ "$PI_PRESENCE_MODULES" = "TRUE" ]; then if [ -e $DATA_DIR/xhttp_pi/presence-mod ]; then cat $DATA_DIR/xhttp_pi/presence-mod >> $DEFAULT_CFG_DIR/pi_framework_sample else merr "Unable to configure: presence - missing mod descriptor" fi if [ -e $DATA_DIR/xhttp_pi/rls-mod ]; then cat $DATA_DIR/xhttp_pi/rls-mod >> $DEFAULT_CFG_DIR/pi_framework_sample else merr "Unable to configure: rls - missing mod descriptor" fi fi cat $DATA_DIR/xhttp_pi/pi_framework-02 >> $DEFAULT_CFG_DIR/pi_framework_sample minfo "Sample provisionning framework saved as: $DEFAULT_CFG_DIR/pi_framework_sample" } kamailio_pframework() #pars: { if [ $# -ne 1 ] ; then merr "kamailio_pframework function takes one parameter" exit 1 fi case $1 in create) shift kamailio_pframework_create "$@" exit $? ;; *) merr "Unexpected pframework action: $1" usage exit 1 ;; esac } case $1 in migrate) if [ "$USED_DBENGINE" != "mysql" ] ; then merr "$USED_DBENGINE don't support migrate operation" exit 1 fi if [ $# -ne 3 ] ; then merr "migrate requires 2 parameters: old and new database" exit 1 fi # create new database minfo "Creating new Database $3...." NO_USER_INIT="yes" kamailio_create $3 if [ "$?" -ne 0 ] ; then echo "migrate: creating new database failed" exit 1 fi # migrate data minfo "Migrating data from $2 to $3...." migrate_db $2 $3 minfo "Migration successfully completed." exit 0; ;; copy) # copy database to some other name if [ "$USED_DBENGINE" == "berkeley" -o "$USED_DBENGINE" == "dbtext" ] ; then merr "$USED_DBENGINE don't support this operation" exit 1 fi shift if [ $# -ne 1 ]; then usage exit 1 fi if [ "$USED_DBENGINE" = "sqlite" ]; then cp $DB_PATH $1 exit $? fi tmp_file=`mktemp /tmp/kamdbctl.XXXXXXXXXX` || exit 1 kamailio_dump $DBNAME $tmp_file ret=$? if [ "$ret" -ne 0 ]; then rm $tmp_file exit $ret fi NO_USER_INIT="yes" kamailio_create $1 ret=$? if [ "$ret" -ne 0 ]; then rm $tmp_file exit $ret fi kamailio_restore $1 $tmp_file ret=$? rm -f $tmp_file exit $ret ;; backup) if [ "$USED_DBENGINE" == "berkeley" -o "$USED_DBENGINE" == "dbtext" ] ; then merr "$USED_DBENGINE don't support this operation" exit 1 fi # backup current database shift if [ $# -ne 1 ]; then usage exit 1 fi kamailio_dump $DBNAME $1 exit $? ;; restore) if [ "$USED_DBENGINE" == "berkeley" -o "$USED_DBENGINE" == "dbtext" ] ; then merr "$USED_DBENGINE don't support this operation" exit 1 fi # restore database from a backup shift if [ $# -ne 1 ]; then usage exit 1 fi kamailio_restore $DBNAME $1 exit $? ;; create) # create new database structures shift if [ $# -eq 1 ] ; then DBNAME="$1" fi kamailio_create $DBNAME exit $? ;; presence) presence_create $DBNAME exit $? ;; extra) extra_create $DBNAME exit $? ;; dbuid) dbuid_create $DBNAME exit $? ;; drop) # delete kamailio database # create new database structures shift if [ $# -eq 1 ] ; then DBNAME="$1" fi kamailio_drop $DBNAME exit $? ;; reinit) # delete database and create a new one # create new database structures shift if [ $# -eq 1 ] ; then DBNAME="$1" fi kamailio_drop $DBNAME ret=$? if [ "$ret" -ne 0 ]; then exit $ret fi kamailio_create $DBNAME exit $? ;; dbonly) # create only an empty database if [ "$USED_DBENGINE" != "mysql" ] ; then merr "$USED_DBENGINE db engine doesn't support this operation" exit 1 fi shift if [ $# -eq 1 ] ; then DBNAME="$1" fi kamailio_db_create $DBNAME exit $? ;; grant) # grant privileges to database if [ "$USED_DBENGINE" != "mysql" ] ; then merr "$USED_DBENGINE db engine doesn't support this operation" exit 1 fi shift if [ $# -eq 1 ] ; then DBNAME="$1" fi kamailio_db_grant $DBNAME exit $? ;; revoke) # revoke privileges to database if [ "$USED_DBENGINE" != "mysql" ] ; then merr "$USED_DBENGINE db engine doesn't support this operation" exit 1 fi shift if [ $# -eq 1 ] ; then DBNAME="$1" fi kamailio_db_revoke $DBNAME exit $? ;; bdb|db_berkeley) shift kamailio_berkeley "$@" exit $? ;; pframework) shift kamailio_pframework "$@" exit $? ;; version) echo "$0 $VERSION" ;; *) usage exit 1; ;; esac kamailio-4.0.4/utils/kamctl/oracle/0000755000000000000000000000000012223032461015656 5ustar rootrootkamailio-4.0.4/utils/kamctl/oracle/sca-create.sql0000644000000000000000000000170212223032460020405 0ustar rootrootINSERT INTO version (table_name, table_version) values ('sca_subscriptions','1'); CREATE TABLE sca_subscriptions ( id NUMBER(10) PRIMARY KEY, subscriber VARCHAR2(255), aor VARCHAR2(255), event NUMBER(10) DEFAULT 0 NOT NULL, expires NUMBER(10) DEFAULT 0 NOT NULL, state NUMBER(10) DEFAULT 0 NOT NULL, app_idx NUMBER(10) DEFAULT 0 NOT NULL, call_id VARCHAR2(255), from_tag VARCHAR2(64), to_tag VARCHAR2(64), record_route CLOB, notify_cseq NUMBER(10), subscribe_cseq NUMBER(10), CONSTRAINT ORA_sca_subscriptions_idx UNIQUE (subscriber, call_id, from_tag, to_tag) ); CREATE OR REPLACE TRIGGER sca_subscriptions_tr before insert on sca_subscriptions FOR EACH ROW BEGIN auto_id(:NEW.id); END sca_subscriptions_tr; / BEGIN map2users('sca_subscriptions'); END; / CREATE INDEX ORA_sca_expires_idx ON sca_subscriptions (expires); CREATE INDEX ORA_sca_subscribers_idx ON sca_subscriptions (subscriber, event); kamailio-4.0.4/utils/kamctl/oracle/registrar-create.sql0000644000000000000000000000227512223032460021647 0ustar rootrootINSERT INTO version (table_name, table_version) values ('aliases','6'); CREATE TABLE aliases ( id NUMBER(10) PRIMARY KEY, ruid VARCHAR2(64) DEFAULT '', username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT NULL, contact VARCHAR2(255) DEFAULT '', received VARCHAR2(128) DEFAULT NULL, path VARCHAR2(128) DEFAULT NULL, expires DATE DEFAULT to_date('2030-05-28 21:32:15','yyyy-mm-dd hh24:mi:ss'), q NUMBER(10,2) DEFAULT 1.0 NOT NULL, callid VARCHAR2(255) DEFAULT 'Default-Call-ID', cseq NUMBER(10) DEFAULT 1 NOT NULL, last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), flags NUMBER(10) DEFAULT 0 NOT NULL, cflags NUMBER(10) DEFAULT 0 NOT NULL, user_agent VARCHAR2(255) DEFAULT '', socket VARCHAR2(64) DEFAULT NULL, methods NUMBER(10) DEFAULT NULL, instance VARCHAR2(255) DEFAULT NULL, reg_id NUMBER(10) DEFAULT 0 NOT NULL, CONSTRAINT aliases_ruid_idx UNIQUE (ruid) ); CREATE OR REPLACE TRIGGER aliases_tr before insert on aliases FOR EACH ROW BEGIN auto_id(:NEW.id); END aliases_tr; / BEGIN map2users('aliases'); END; / CREATE INDEX aliases_alias_idx ON aliases (username, domain, contact); kamailio-4.0.4/utils/kamctl/oracle/uri_db-create.sql0000644000000000000000000000101712223032460021102 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uri','1'); CREATE TABLE uri ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', uri_user VARCHAR2(64) DEFAULT '', last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), CONSTRAINT uri_account_idx UNIQUE (username, domain, uri_user) ); CREATE OR REPLACE TRIGGER uri_tr before insert on uri FOR EACH ROW BEGIN auto_id(:NEW.id); END uri_tr; / BEGIN map2users('uri'); END; / kamailio-4.0.4/utils/kamctl/oracle/purple-create.sql0000644000000000000000000000062512223032460021151 0ustar rootrootINSERT INTO version (table_name, table_version) values ('purplemap','1'); CREATE TABLE purplemap ( id NUMBER(10) PRIMARY KEY, sip_user VARCHAR2(128), ext_user VARCHAR2(128), ext_prot VARCHAR2(16), ext_pass VARCHAR2(64) ); CREATE OR REPLACE TRIGGER purplemap_tr before insert on purplemap FOR EACH ROW BEGIN auto_id(:NEW.id); END purplemap_tr; / BEGIN map2users('purplemap'); END; / kamailio-4.0.4/utils/kamctl/oracle/avpops-create.sql0000644000000000000000000000145612223032460021155 0ustar rootrootINSERT INTO version (table_name, table_version) values ('usr_preferences','2'); CREATE TABLE usr_preferences ( id NUMBER(10) PRIMARY KEY, uuid VARCHAR2(64) DEFAULT '', username VARCHAR2(128) DEFAULT 0 NOT NULL, domain VARCHAR2(64) DEFAULT '', attribute VARCHAR2(32) DEFAULT '', type NUMBER(10) DEFAULT 0 NOT NULL, value VARCHAR2(128) DEFAULT '', last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss') ); CREATE OR REPLACE TRIGGER usr_preferences_tr before insert on usr_preferences FOR EACH ROW BEGIN auto_id(:NEW.id); END usr_preferences_tr; / BEGIN map2users('usr_preferences'); END; / CREATE INDEX usr_preferences_ua_idx ON usr_preferences (uuid, attribute); CREATE INDEX usr_preferences_uda_idx ON usr_preferences (username, domain, attribute); kamailio-4.0.4/utils/kamctl/oracle/usrloc-create.sql0000644000000000000000000000403612223032460021151 0ustar rootrootINSERT INTO version (table_name, table_version) values ('location','6'); CREATE TABLE location ( id NUMBER(10) PRIMARY KEY, ruid VARCHAR2(64) DEFAULT '', username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT NULL, contact VARCHAR2(255) DEFAULT '', received VARCHAR2(128) DEFAULT NULL, path VARCHAR2(512) DEFAULT NULL, expires DATE DEFAULT to_date('2030-05-28 21:32:15','yyyy-mm-dd hh24:mi:ss'), q NUMBER(10,2) DEFAULT 1.0 NOT NULL, callid VARCHAR2(255) DEFAULT 'Default-Call-ID', cseq NUMBER(10) DEFAULT 1 NOT NULL, last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), flags NUMBER(10) DEFAULT 0 NOT NULL, cflags NUMBER(10) DEFAULT 0 NOT NULL, user_agent VARCHAR2(255) DEFAULT '', socket VARCHAR2(64) DEFAULT NULL, methods NUMBER(10) DEFAULT NULL, instance VARCHAR2(255) DEFAULT NULL, reg_id NUMBER(10) DEFAULT 0 NOT NULL, CONSTRAINT location_ruid_idx UNIQUE (ruid) ); CREATE OR REPLACE TRIGGER location_tr before insert on location FOR EACH ROW BEGIN auto_id(:NEW.id); END location_tr; / BEGIN map2users('location'); END; / CREATE INDEX location_account_contact_idx ON location (username, domain, contact); CREATE INDEX location_expires_idx ON location (expires); INSERT INTO version (table_name, table_version) values ('location_attrs','1'); CREATE TABLE location_attrs ( id NUMBER(10) PRIMARY KEY, ruid VARCHAR2(64) DEFAULT '', username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT NULL, aname VARCHAR2(64) DEFAULT '', atype NUMBER(10) DEFAULT 0 NOT NULL, avalue VARCHAR2(255) DEFAULT '', last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss') ); CREATE OR REPLACE TRIGGER location_attrs_tr before insert on location_attrs FOR EACH ROW BEGIN auto_id(:NEW.id); END location_attrs_tr; / BEGIN map2users('location_attrs'); END; / CREATE INDEX ORA_account_record_idx ON location_attrs (username, domain, ruid); CREATE INDEX ORA_last_modified_idx ON location_attrs (last_modified); kamailio-4.0.4/utils/kamctl/oracle/standard-create.sql0000644000000000000000000000030712223032460021437 0ustar rootrootCREATE TABLE version ( table_name VARCHAR2(32), table_version NUMBER(10) DEFAULT 0 NOT NULL, CONSTRAINT version_table_name_idx UNIQUE (table_name) ); BEGIN map2users('version'); END; / kamailio-4.0.4/utils/kamctl/oracle/domainpolicy-create.sql0000644000000000000000000000106112223032460022324 0ustar rootrootINSERT INTO version (table_name, table_version) values ('domainpolicy','2'); CREATE TABLE domainpolicy ( id NUMBER(10) PRIMARY KEY, rule VARCHAR2(255), type VARCHAR2(255), att VARCHAR2(255), val VARCHAR2(128), description VARCHAR2(255), CONSTRAINT domainpolicy_rav_idx UNIQUE (rule, att, val) ); CREATE OR REPLACE TRIGGER domainpolicy_tr before insert on domainpolicy FOR EACH ROW BEGIN auto_id(:NEW.id); END domainpolicy_tr; / BEGIN map2users('domainpolicy'); END; / CREATE INDEX domainpolicy_rule_idx ON domainpolicy (rule); kamailio-4.0.4/utils/kamctl/oracle/carrierroute-create.sql0000644000000000000000000000423112223032460022345 0ustar rootrootINSERT INTO version (table_name, table_version) values ('carrierroute','3'); CREATE TABLE carrierroute ( id NUMBER(10) PRIMARY KEY, carrier NUMBER(10) DEFAULT 0 NOT NULL, domain NUMBER(10) DEFAULT 0 NOT NULL, scan_prefix VARCHAR2(64) DEFAULT '', flags NUMBER(10) DEFAULT 0 NOT NULL, mask NUMBER(10) DEFAULT 0 NOT NULL, prob NUMBER DEFAULT 0 NOT NULL, strip NUMBER(10) DEFAULT 0 NOT NULL, rewrite_host VARCHAR2(128) DEFAULT '', rewrite_prefix VARCHAR2(64) DEFAULT '', rewrite_suffix VARCHAR2(64) DEFAULT '', description VARCHAR2(255) DEFAULT NULL ); CREATE OR REPLACE TRIGGER carrierroute_tr before insert on carrierroute FOR EACH ROW BEGIN auto_id(:NEW.id); END carrierroute_tr; / BEGIN map2users('carrierroute'); END; / INSERT INTO version (table_name, table_version) values ('carrierfailureroute','2'); CREATE TABLE carrierfailureroute ( id NUMBER(10) PRIMARY KEY, carrier NUMBER(10) DEFAULT 0 NOT NULL, domain NUMBER(10) DEFAULT 0 NOT NULL, scan_prefix VARCHAR2(64) DEFAULT '', host_name VARCHAR2(128) DEFAULT '', reply_code VARCHAR2(3) DEFAULT '', flags NUMBER(10) DEFAULT 0 NOT NULL, mask NUMBER(10) DEFAULT 0 NOT NULL, next_domain NUMBER(10) DEFAULT 0 NOT NULL, description VARCHAR2(255) DEFAULT NULL ); CREATE OR REPLACE TRIGGER carrierfailureroute_tr before insert on carrierfailureroute FOR EACH ROW BEGIN auto_id(:NEW.id); END carrierfailureroute_tr; / BEGIN map2users('carrierfailureroute'); END; / INSERT INTO version (table_name, table_version) values ('carrier_name','1'); CREATE TABLE carrier_name ( id NUMBER(10) PRIMARY KEY, carrier VARCHAR2(64) DEFAULT NULL ); CREATE OR REPLACE TRIGGER carrier_name_tr before insert on carrier_name FOR EACH ROW BEGIN auto_id(:NEW.id); END carrier_name_tr; / BEGIN map2users('carrier_name'); END; / INSERT INTO version (table_name, table_version) values ('domain_name','1'); CREATE TABLE domain_name ( id NUMBER(10) PRIMARY KEY, domain VARCHAR2(64) DEFAULT NULL ); CREATE OR REPLACE TRIGGER domain_name_tr before insert on domain_name FOR EACH ROW BEGIN auto_id(:NEW.id); END domain_name_tr; / BEGIN map2users('domain_name'); END; / kamailio-4.0.4/utils/kamctl/oracle/domain-create.sql0000644000000000000000000000201712223032460021106 0ustar rootrootINSERT INTO version (table_name, table_version) values ('domain','2'); CREATE TABLE domain ( id NUMBER(10) PRIMARY KEY, domain VARCHAR2(64), did VARCHAR2(64) DEFAULT NULL, last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), CONSTRAINT domain_domain_idx UNIQUE (domain) ); CREATE OR REPLACE TRIGGER domain_tr before insert on domain FOR EACH ROW BEGIN auto_id(:NEW.id); END domain_tr; / BEGIN map2users('domain'); END; / INSERT INTO version (table_name, table_version) values ('domain_attrs','1'); CREATE TABLE domain_attrs ( id NUMBER(10) PRIMARY KEY, did VARCHAR2(64), name VARCHAR2(32), type NUMBER(10), value VARCHAR2(255), last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), CONSTRAINT domain_attrs_domain_attrs_idx UNIQUE (did, name, value) ); CREATE OR REPLACE TRIGGER domain_attrs_tr before insert on domain_attrs FOR EACH ROW BEGIN auto_id(:NEW.id); END domain_attrs_tr; / BEGIN map2users('domain_attrs'); END; / kamailio-4.0.4/utils/kamctl/oracle/permissions-create.sql0000644000000000000000000000160412223032460022213 0ustar rootrootINSERT INTO version (table_name, table_version) values ('trusted','5'); CREATE TABLE trusted ( id NUMBER(10) PRIMARY KEY, src_ip VARCHAR2(50), proto VARCHAR2(4), from_pattern VARCHAR2(64) DEFAULT NULL, tag VARCHAR2(64) ); CREATE OR REPLACE TRIGGER trusted_tr before insert on trusted FOR EACH ROW BEGIN auto_id(:NEW.id); END trusted_tr; / BEGIN map2users('trusted'); END; / CREATE INDEX trusted_peer_idx ON trusted (src_ip); INSERT INTO version (table_name, table_version) values ('address','6'); CREATE TABLE address ( id NUMBER(10) PRIMARY KEY, grp NUMBER(10) DEFAULT 1 NOT NULL, ip_addr VARCHAR2(50), mask NUMBER(10) DEFAULT 32 NOT NULL, port NUMBER(5) DEFAULT 0 NOT NULL, tag VARCHAR2(64) ); CREATE OR REPLACE TRIGGER address_tr before insert on address FOR EACH ROW BEGIN auto_id(:NEW.id); END address_tr; / BEGIN map2users('address'); END; / kamailio-4.0.4/utils/kamctl/oracle/README.TYPES0000644000000000000000000000011012223032460017430 0ustar rootrootBITMASK => NUMBER(11) INTEGER => NUMBER(<=10) BLOB => VARCHAR2(4000) kamailio-4.0.4/utils/kamctl/oracle/htable-create.sql0000644000000000000000000000075012223032460021100 0ustar rootrootINSERT INTO version (table_name, table_version) values ('htable','2'); CREATE TABLE htable ( id NUMBER(10) PRIMARY KEY, key_name VARCHAR2(64) DEFAULT '', key_type NUMBER(10) DEFAULT 0 NOT NULL, value_type NUMBER(10) DEFAULT 0 NOT NULL, key_value VARCHAR2(128) DEFAULT '', expires NUMBER(10) DEFAULT 0 NOT NULL ); CREATE OR REPLACE TRIGGER htable_tr before insert on htable FOR EACH ROW BEGIN auto_id(:NEW.id); END htable_tr; / BEGIN map2users('htable'); END; / kamailio-4.0.4/utils/kamctl/oracle/matrix-create.sql0000644000000000000000000000056212223032460021146 0ustar rootrootINSERT INTO version (table_name, table_version) values ('matrix','1'); CREATE TABLE matrix ( first NUMBER(10), second NUMBER(5), res NUMBER(10) ); CREATE OR REPLACE TRIGGER matrix_tr before insert on matrix FOR EACH ROW BEGIN auto_id(:NEW.id); END matrix_tr; / BEGIN map2users('matrix'); END; / CREATE INDEX matrix_matrix_idx ON matrix (first, second); kamailio-4.0.4/utils/kamctl/oracle/inc/0000755000000000000000000000000012223032460016426 5ustar rootrootkamailio-4.0.4/utils/kamctl/oracle/inc/_createsch.tmpl0000644000000000000000000000037312223032460021427 0ustar rootrootcreate sequence seq_%DBROOTUSER% minvalue 1 maxvalue 9999999999999999999999999999 start with 1 increment by 1 cache 20; create or replace procedure auto_id(fld out number) is begin SELECT seq_%DBROOTUSER%.NEXTVAL INTO fld FROM dual; end auto_id; / kamailio-4.0.4/utils/kamctl/oracle/inc/_create_compat.sql0000644000000000000000000000607612223032460022125 0ustar rootrootcreate or replace function now(v1 in number := 0) return date is Result date; begin SELECT sysdate INTO Result FROM dual; return Result; end now; / create or replace function rand(v1 in number := 0) return number is Result number; begin SELECT dbms_random.value INTO Result FROM dual; return Result; end rand; / create or replace function concat(v1 in varchar2, v2 in varchar2, v3 in varchar2) return varchar2 IS Result varchar2(4000); begin SELECT v1||v2||v3 INTO Result from dual; return Result; end concat; / create or replace TYPE TABLE_STRING IS TABLE OF VARCHAR2(4000); / create or replace function DUMP_TABLES(P_OWNER in VARCHAR2) RETURN TABLE_STRING PIPELINED IS CURSOR COLUMNS_CUR (P_OWNER in VARCHAR2, P_TABLE in VARCHAR2) IS SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE FROM ALL_TAB_COLUMNS WHERE OWNER = UPPER(P_OWNER) AND TABLE_NAME = UPPER(P_TABLE) ORDER BY COLUMN_ID; COLUMN_REC COLUMNS_CUR%ROWTYPE; TABLE_REC_CUR SYS_REFCURSOR; L_QUERY VARCHAR2(8000); L_QUERY1 VARCHAR2(8000); L_QUERY2 VARCHAR2(8000); L_LINE VARCHAR2(8000); L_COMA CHAR(2) := ' '; FIRST_ROW BOOLEAN := TRUE; BEGIN FOR cur IN (SELECT TABLE_NAME FROM all_tables WHERE owner=UPPER(P_OWNER)) LOOP L_QUERY1 := 'SELECT ''INSERT INTO ' || cur.table_name; L_QUERY2 :='('; OPEN COLUMNS_CUR(P_OWNER, cur.table_name); FIRST_ROW := TRUE; LOOP FETCH COLUMNS_CUR INTO COLUMN_REC; IF FIRST_ROW AND COLUMNS_CUR%NOTFOUND THEN PIPE ROW('Table ''' || P_OWNER || '.' || cur.table_name || ''' not found'); END IF; EXIT WHEN COLUMNS_CUR%NOTFOUND; IF FIRST_ROW THEN L_QUERY2 := L_QUERY2 || COLUMN_REC.COLUMN_NAME; L_QUERY := ' VALUES ('' || '; ELSE L_QUERY2 := L_QUERY2||','||COLUMN_REC.COLUMN_NAME; L_COMA := ', '; L_QUERY := L_QUERY || ' || '', '' || '; END IF; IF COLUMN_REC.DATA_TYPE = 'VARCHAR2' OR COLUMN_REC.DATA_TYPE = 'CHAR' OR COLUMN_REC.DATA_TYPE = 'CLOB' THEN L_QUERY := L_QUERY || 'NVL2(' || COLUMN_REC.COLUMN_NAME || ', '''''''' || REPLACE(' || COLUMN_REC.COLUMN_NAME || ', '''''''', '''''''''''') || '''''''', ''NULL'')'; ELSIF COLUMN_REC.DATA_TYPE = 'DATE' THEN L_QUERY := L_QUERY || 'NVL2(' || COLUMN_REC.COLUMN_NAME || ', ''TO_DATE('''''' || TO_CHAR(' || COLUMN_REC.COLUMN_NAME || ', ''yyyy-mm-dd hh24:mi:ss'') || '''''', ''''yyyy-mm-dd hh24:mi:ss'''')'', ''NULL'')'; ELSIF COLUMN_REC.DATA_TYPE = 'BLOB' THEN L_QUERY := L_QUERY || 'NVL2(' || COLUMN_REC.COLUMN_NAME || ', ''UNSUPPORTED:NON EMPTY BLOB'', ''NULL'')'; ELSE L_QUERY := L_QUERY || 'NVL(TO_CHAR(' || COLUMN_REC.COLUMN_NAME || '), ''NULL'')'; END IF; FIRST_ROW := FALSE; END LOOP; IF NOT FIRST_ROW THEN L_QUERY :=L_QUERY1||L_QUERY2||')'|| L_QUERY || ' || '');'' AS LINE FROM ' || COLUMN_REC.TABLE_NAME; END IF; CLOSE COLUMNS_CUR; /* IF FIRST_ROW THEN RETURN; END IF;*/ OPEN TABLE_REC_CUR FOR L_QUERY; LOOP FETCH TABLE_REC_CUR INTO L_LINE; EXIT WHEN TABLE_REC_CUR%NOTFOUND; PIPE ROW(L_LINE); END LOOP; CLOSE TABLE_REC_CUR; END LOOP; RETURN; END; / kamailio-4.0.4/utils/kamctl/oracle/inc/_dropsch.tmpl0000644000000000000000000000066712223032460021136 0ustar rootroot--DROP USER %DBROUSER% CASCADE; --DROP USER %DBRWUSER% CASCADE; BEGIN FOR cur IN (SELECT 'DROP TABLE '||table_name||' CASCADE CONSTRAINTS PURGE' stmt from all_tables where owner=UPPER('%DBROOTUSER%')) LOOP EXECUTE IMMEDIATE cur.stmt; END LOOP; FOR cur IN (SELECT 'DROP '||object_type||' '||object_name stmt from all_objects where owner=UPPER('%DBROOTUSER%')) LOOP EXECUTE IMMEDIATE cur.stmt; END LOOP; END; / kamailio-4.0.4/utils/kamctl/oracle/inc/_grantfunc.tmpl0000644000000000000000000000176512223032460021463 0ustar rootrootBEGIN FOR cur IN (SELECT OBJECT_NAME FROM All_Procedures WHERE owner=UPPER('%DBROOTUSER%') and object_name <> 'MAP2USERS') LOOP EXECUTE IMMEDIATE('GRANT EXECUTE ON %DBROOTUSER%.'||cur.OBJECT_NAME ||' to %DBRWUSER%'); EXECUTE IMMEDIATE('GRANT EXECUTE ON %DBROOTUSER%.'||cur.OBJECT_NAME ||' to %DBROUSER%'); EXECUTE IMMEDIATE('CREATE OR REPLACE SYNONYM %DBRWUSER%.'||cur.OBJECT_NAME ||' for %DBROOTUSER%.'||cur.OBJECT_NAME); EXECUTE IMMEDIATE('CREATE OR REPLACE SYNONYM %DBROUSER%.'||cur.OBJECT_NAME ||' for %DBROOTUSER%.'||cur.OBJECT_NAME); END LOOP; END; / CREATE OR REPLACE PROCEDURE map2users(tbl varchar2) is BEGIN EXECUTE IMMEDIATE('GRANT SELECT ON '||tbl||' to %DBROUSER%'); EXECUTE IMMEDIATE('GRANT SELECT,INSERT,UPDATE,DELETE ON '||tbl ||' to %DBRWUSER%'); EXECUTE IMMEDIATE('CREATE OR REPLACE SYNONYM %DBROUSER%.'||tbl ||' for %DBROOTUSER%.'||tbl); EXECUTE IMMEDIATE('CREATE OR REPLACE SYNONYM %DBRWUSER%.'||tbl ||' for %DBROOTUSER%.'||tbl); END map2users; / kamailio-4.0.4/utils/kamctl/oracle/inc/_grantroot.tmpl0000644000000000000000000000052712223032460021506 0ustar rootroot--create user %DBROOTUSER% identified by %DBROOTPW% -- default tablespace DATA temporary tablespace TEMP profile DEFAULT; grant connect to %DBROOTUSER% with admin option; grant resource, create any synonym, create role, create user, drop user, unlimited tablespace to %DBROOTUSER%; grant select on sys.dba_role_privs to %DBROOTUSER%; kamailio-4.0.4/utils/kamctl/oracle/pdt-create.sql0000644000000000000000000000063512223032460020432 0ustar rootrootINSERT INTO version (table_name, table_version) values ('pdt','1'); CREATE TABLE pdt ( id NUMBER(10) PRIMARY KEY, sdomain VARCHAR2(128), prefix VARCHAR2(32), domain VARCHAR2(128) DEFAULT '', CONSTRAINT pdt_sdomain_prefix_idx UNIQUE (sdomain, prefix) ); CREATE OR REPLACE TRIGGER pdt_tr before insert on pdt FOR EACH ROW BEGIN auto_id(:NEW.id); END pdt_tr; / BEGIN map2users('pdt'); END; / kamailio-4.0.4/utils/kamctl/oracle/uid_uri_db-create.sql0000644000000000000000000000222612223032460021746 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_uri','3'); CREATE TABLE uid_uri ( id NUMBER(10) PRIMARY KEY, uuid VARCHAR2(64), did VARCHAR2(64), username VARCHAR2(64), flags NUMBER(10) DEFAULT 0 NOT NULL, scheme VARCHAR2(8) DEFAULT 'sip' ); CREATE OR REPLACE TRIGGER uid_uri_tr before insert on uid_uri FOR EACH ROW BEGIN auto_id(:NEW.id); END uid_uri_tr; / BEGIN map2users('uid_uri'); END; / CREATE INDEX uid_uri_uri_idx1 ON uid_uri (username, did, scheme); CREATE INDEX uid_uri_uri_uid ON uid_uri (uuid); INSERT INTO version (table_name, table_version) values ('uid_uri_attrs','2'); CREATE TABLE uid_uri_attrs ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64), did VARCHAR2(64), name VARCHAR2(32), value VARCHAR2(128), type NUMBER(10) DEFAULT 0 NOT NULL, flags NUMBER(10) DEFAULT 0 NOT NULL, scheme VARCHAR2(8) DEFAULT 'sip', CONSTRAINT uid_uri_attrs_uriattrs_idx UNIQUE (username, did, name, value, scheme) ); CREATE OR REPLACE TRIGGER uid_uri_attrs_tr before insert on uid_uri_attrs FOR EACH ROW BEGIN auto_id(:NEW.id); END uid_uri_attrs_tr; / BEGIN map2users('uid_uri_attrs'); END; / kamailio-4.0.4/utils/kamctl/oracle/uid_gflags-create.sql0000644000000000000000000000101512223032460021740 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_global_attrs','1'); CREATE TABLE uid_global_attrs ( id NUMBER(10) PRIMARY KEY, name VARCHAR2(32), type NUMBER(10) DEFAULT 0 NOT NULL, value VARCHAR2(128), flags NUMBER(10) DEFAULT 0 NOT NULL, CONSTRAINT ORA_global_attrs_idx UNIQUE (name, value) ); CREATE OR REPLACE TRIGGER uid_global_attrs_tr before insert on uid_global_attrs FOR EACH ROW BEGIN auto_id(:NEW.id); END uid_global_attrs_tr; / BEGIN map2users('uid_global_attrs'); END; / kamailio-4.0.4/utils/kamctl/oracle/drouting-create.sql0000644000000000000000000000351412223032460021475 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dr_gateways','3'); CREATE TABLE dr_gateways ( gwid NUMBER(10) PRIMARY KEY, type NUMBER(10) DEFAULT 0 NOT NULL, address VARCHAR2(128), strip NUMBER(10) DEFAULT 0 NOT NULL, pri_prefix VARCHAR2(64) DEFAULT NULL, attrs VARCHAR2(255) DEFAULT NULL, description VARCHAR2(128) DEFAULT '' ); CREATE OR REPLACE TRIGGER dr_gateways_tr before insert on dr_gateways FOR EACH ROW BEGIN auto_id(:NEW.id); END dr_gateways_tr; / BEGIN map2users('dr_gateways'); END; / INSERT INTO version (table_name, table_version) values ('dr_rules','3'); CREATE TABLE dr_rules ( ruleid NUMBER(10) PRIMARY KEY, groupid VARCHAR2(255), prefix VARCHAR2(64), timerec VARCHAR2(255), priority NUMBER(10) DEFAULT 0 NOT NULL, routeid VARCHAR2(64), gwlist VARCHAR2(255), description VARCHAR2(128) DEFAULT '' ); CREATE OR REPLACE TRIGGER dr_rules_tr before insert on dr_rules FOR EACH ROW BEGIN auto_id(:NEW.id); END dr_rules_tr; / BEGIN map2users('dr_rules'); END; / INSERT INTO version (table_name, table_version) values ('dr_gw_lists','1'); CREATE TABLE dr_gw_lists ( id NUMBER(10) PRIMARY KEY, gwlist VARCHAR2(255), description VARCHAR2(128) DEFAULT '' ); CREATE OR REPLACE TRIGGER dr_gw_lists_tr before insert on dr_gw_lists FOR EACH ROW BEGIN auto_id(:NEW.id); END dr_gw_lists_tr; / BEGIN map2users('dr_gw_lists'); END; / INSERT INTO version (table_name, table_version) values ('dr_groups','2'); CREATE TABLE dr_groups ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64), domain VARCHAR2(128) DEFAULT '', groupid NUMBER(10) DEFAULT 0 NOT NULL, description VARCHAR2(128) DEFAULT '' ); CREATE OR REPLACE TRIGGER dr_groups_tr before insert on dr_groups FOR EACH ROW BEGIN auto_id(:NEW.id); END dr_groups_tr; / BEGIN map2users('dr_groups'); END; / kamailio-4.0.4/utils/kamctl/oracle/msilo-create.sql0000644000000000000000000000142712223032460020766 0ustar rootrootINSERT INTO version (table_name, table_version) values ('silo','7'); CREATE TABLE silo ( id NUMBER(10) PRIMARY KEY, src_addr VARCHAR2(128) DEFAULT '', dst_addr VARCHAR2(128) DEFAULT '', username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', inc_time NUMBER(10) DEFAULT 0 NOT NULL, exp_time NUMBER(10) DEFAULT 0 NOT NULL, snd_time NUMBER(10) DEFAULT 0 NOT NULL, ctype VARCHAR2(32) DEFAULT 'text/plain', body BLOB DEFAULT '', extra_hdrs CLOB DEFAULT '', callid VARCHAR2(128) DEFAULT '', status NUMBER(10) DEFAULT 0 NOT NULL ); CREATE OR REPLACE TRIGGER silo_tr before insert on silo FOR EACH ROW BEGIN auto_id(:NEW.id); END silo_tr; / BEGIN map2users('silo'); END; / CREATE INDEX silo_account_idx ON silo (username, domain); kamailio-4.0.4/utils/kamctl/oracle/dialog-create.sql0000644000000000000000000000300112223032460021070 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dialog','7'); CREATE TABLE dialog ( id NUMBER(10) PRIMARY KEY, hash_entry NUMBER(10), hash_id NUMBER(10), callid VARCHAR2(255), from_uri VARCHAR2(128), from_tag VARCHAR2(64), to_uri VARCHAR2(128), to_tag VARCHAR2(64), caller_cseq VARCHAR2(20), callee_cseq VARCHAR2(20), caller_route_set VARCHAR2(512), callee_route_set VARCHAR2(512), caller_contact VARCHAR2(128), callee_contact VARCHAR2(128), caller_sock VARCHAR2(64), callee_sock VARCHAR2(64), state NUMBER(10), start_time NUMBER(10), timeout NUMBER(10) DEFAULT 0 NOT NULL, sflags NUMBER(10) DEFAULT 0 NOT NULL, iflags NUMBER(10) DEFAULT 0 NOT NULL, toroute_name VARCHAR2(32), req_uri VARCHAR2(128), xdata VARCHAR2(512) ); CREATE OR REPLACE TRIGGER dialog_tr before insert on dialog FOR EACH ROW BEGIN auto_id(:NEW.id); END dialog_tr; / BEGIN map2users('dialog'); END; / CREATE INDEX dialog_hash_idx ON dialog (hash_entry, hash_id); INSERT INTO version (table_name, table_version) values ('dialog_vars','1'); CREATE TABLE dialog_vars ( id NUMBER(10) PRIMARY KEY, hash_entry NUMBER(10), hash_id NUMBER(10), dialog_key VARCHAR2(128), dialog_value VARCHAR2(512) ); CREATE OR REPLACE TRIGGER dialog_vars_tr before insert on dialog_vars FOR EACH ROW BEGIN auto_id(:NEW.id); END dialog_vars_tr; / BEGIN map2users('dialog_vars'); END; / CREATE INDEX dialog_vars_hash_idx ON dialog_vars (hash_entry, hash_id); kamailio-4.0.4/utils/kamctl/oracle/acc-create.sql0000644000000000000000000000221212223032461020363 0ustar rootrootINSERT INTO version (table_name, table_version) values ('acc','4'); CREATE TABLE acc ( id NUMBER(10) PRIMARY KEY, method VARCHAR2(16) DEFAULT '', from_tag VARCHAR2(64) DEFAULT '', to_tag VARCHAR2(64) DEFAULT '', callid VARCHAR2(255) DEFAULT '', sip_code VARCHAR2(3) DEFAULT '', sip_reason VARCHAR2(32) DEFAULT '', time DATE ); CREATE OR REPLACE TRIGGER acc_tr before insert on acc FOR EACH ROW BEGIN auto_id(:NEW.id); END acc_tr; / BEGIN map2users('acc'); END; / CREATE INDEX acc_callid_idx ON acc (callid); INSERT INTO version (table_name, table_version) values ('missed_calls','3'); CREATE TABLE missed_calls ( id NUMBER(10) PRIMARY KEY, method VARCHAR2(16) DEFAULT '', from_tag VARCHAR2(64) DEFAULT '', to_tag VARCHAR2(64) DEFAULT '', callid VARCHAR2(255) DEFAULT '', sip_code VARCHAR2(3) DEFAULT '', sip_reason VARCHAR2(32) DEFAULT '', time DATE ); CREATE OR REPLACE TRIGGER missed_calls_tr before insert on missed_calls FOR EACH ROW BEGIN auto_id(:NEW.id); END missed_calls_tr; / BEGIN map2users('missed_calls'); END; / CREATE INDEX missed_calls_callid_idx ON missed_calls (callid); kamailio-4.0.4/utils/kamctl/oracle/pipelimit-create.sql0000644000000000000000000000062612223032460021637 0ustar rootrootINSERT INTO version (table_name, table_version) values ('pl_pipes','1'); CREATE TABLE pl_pipes ( id NUMBER(10) PRIMARY KEY, pipeid VARCHAR2(64) DEFAULT '', algorithm VARCHAR2(32) DEFAULT '', plimit NUMBER(10) DEFAULT 0 NOT NULL ); CREATE OR REPLACE TRIGGER pl_pipes_tr before insert on pl_pipes FOR EACH ROW BEGIN auto_id(:NEW.id); END pl_pipes_tr; / BEGIN map2users('pl_pipes'); END; / kamailio-4.0.4/utils/kamctl/oracle/uid_auth_db-create.sql0000644000000000000000000000153512223032460022112 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_credentials','7'); CREATE TABLE uid_credentials ( id NUMBER(10) PRIMARY KEY, auth_username VARCHAR2(64), did VARCHAR2(64) DEFAULT '_default', realm VARCHAR2(64), password VARCHAR2(28) DEFAULT '', flags NUMBER(10) DEFAULT 0 NOT NULL, ha1 VARCHAR2(32), ha1b VARCHAR2(32) DEFAULT '', uuid VARCHAR2(64) ); CREATE OR REPLACE TRIGGER uid_credentials_tr before insert on uid_credentials FOR EACH ROW BEGIN auto_id(:NEW.id); END uid_credentials_tr; / BEGIN map2users('uid_credentials'); END; / CREATE INDEX uid_credentials_cred_idx ON uid_credentials (auth_username, did); CREATE INDEX uid_credentials_uuid ON uid_credentials (uuid); CREATE INDEX uid_credentials_did_idx ON uid_credentials (did); CREATE INDEX uid_credentials_realm_idx ON uid_credentials (realm); kamailio-4.0.4/utils/kamctl/oracle/mtree-create.sql0000644000000000000000000000153612223032460020760 0ustar rootrootINSERT INTO version (table_name, table_version) values ('mtree','1'); CREATE TABLE mtree ( id NUMBER(10) PRIMARY KEY, tprefix VARCHAR2(32) DEFAULT '', tvalue VARCHAR2(128) DEFAULT '', CONSTRAINT mtree_tprefix_idx UNIQUE (tprefix) ); CREATE OR REPLACE TRIGGER mtree_tr before insert on mtree FOR EACH ROW BEGIN auto_id(:NEW.id); END mtree_tr; / BEGIN map2users('mtree'); END; / INSERT INTO version (table_name, table_version) values ('mtrees','2'); CREATE TABLE mtrees ( id NUMBER(10) PRIMARY KEY, tname VARCHAR2(128) DEFAULT '', tprefix VARCHAR2(32) DEFAULT '', tvalue VARCHAR2(128) DEFAULT '', CONSTRAINT ORA_tname_tprefix_tvalue_idx UNIQUE (tname, tprefix, tvalue) ); CREATE OR REPLACE TRIGGER mtrees_tr before insert on mtrees FOR EACH ROW BEGIN auto_id(:NEW.id); END mtrees_tr; / BEGIN map2users('mtrees'); END; / kamailio-4.0.4/utils/kamctl/oracle/imc-create.sql0000644000000000000000000000161612223032460020413 0ustar rootrootINSERT INTO version (table_name, table_version) values ('imc_rooms','1'); CREATE TABLE imc_rooms ( id NUMBER(10) PRIMARY KEY, name VARCHAR2(64), domain VARCHAR2(64), flag NUMBER(10), CONSTRAINT imc_rooms_name_domain_idx UNIQUE (name, domain) ); CREATE OR REPLACE TRIGGER imc_rooms_tr before insert on imc_rooms FOR EACH ROW BEGIN auto_id(:NEW.id); END imc_rooms_tr; / BEGIN map2users('imc_rooms'); END; / INSERT INTO version (table_name, table_version) values ('imc_members','1'); CREATE TABLE imc_members ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64), domain VARCHAR2(64), room VARCHAR2(64), flag NUMBER(10), CONSTRAINT imc_members_account_room_idx UNIQUE (username, domain, room) ); CREATE OR REPLACE TRIGGER imc_members_tr before insert on imc_members FOR EACH ROW BEGIN auto_id(:NEW.id); END imc_members_tr; / BEGIN map2users('imc_members'); END; / kamailio-4.0.4/utils/kamctl/oracle/userblacklist-create.sql0000644000000000000000000000206612223032460022512 0ustar rootrootINSERT INTO version (table_name, table_version) values ('userblacklist','1'); CREATE TABLE userblacklist ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', prefix VARCHAR2(64) DEFAULT '', whitelist NUMBER(5) DEFAULT 0 NOT NULL ); CREATE OR REPLACE TRIGGER userblacklist_tr before insert on userblacklist FOR EACH ROW BEGIN auto_id(:NEW.id); END userblacklist_tr; / BEGIN map2users('userblacklist'); END; / CREATE INDEX ORA_userblacklist_idx ON userblacklist (username, domain, prefix); INSERT INTO version (table_name, table_version) values ('globalblacklist','1'); CREATE TABLE globalblacklist ( id NUMBER(10) PRIMARY KEY, prefix VARCHAR2(64) DEFAULT '', whitelist NUMBER(5) DEFAULT 0 NOT NULL, description VARCHAR2(255) DEFAULT NULL ); CREATE OR REPLACE TRIGGER globalblacklist_tr before insert on globalblacklist FOR EACH ROW BEGIN auto_id(:NEW.id); END globalblacklist_tr; / BEGIN map2users('globalblacklist'); END; / CREATE INDEX ORA_globalblacklist_idx ON globalblacklist (prefix); kamailio-4.0.4/utils/kamctl/oracle/alias_db-create.sql0000644000000000000000000000112712223032460021376 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dbaliases','1'); CREATE TABLE dbaliases ( id NUMBER(10) PRIMARY KEY, alias_username VARCHAR2(64) DEFAULT '', alias_domain VARCHAR2(64) DEFAULT '', username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', CONSTRAINT dbaliases_alias_idx UNIQUE (alias_username, alias_domain) ); CREATE OR REPLACE TRIGGER dbaliases_tr before insert on dbaliases FOR EACH ROW BEGIN auto_id(:NEW.id); END dbaliases_tr; / BEGIN map2users('dbaliases'); END; / CREATE INDEX dbaliases_target_idx ON dbaliases (username, domain); kamailio-4.0.4/utils/kamctl/oracle/uac-create.sql0000644000000000000000000000132612223032460020411 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uacreg','1'); CREATE TABLE uacreg ( id NUMBER(10) PRIMARY KEY, l_uuid VARCHAR2(64) DEFAULT '', l_username VARCHAR2(64) DEFAULT '', l_domain VARCHAR2(128) DEFAULT '', r_username VARCHAR2(64) DEFAULT '', r_domain VARCHAR2(128) DEFAULT '', realm VARCHAR2(64) DEFAULT '', auth_username VARCHAR2(64) DEFAULT '', auth_password VARCHAR2(64) DEFAULT '', auth_proxy VARCHAR2(64) DEFAULT '', expires NUMBER(10) DEFAULT 0 NOT NULL, CONSTRAINT uacreg_l_uuid_idx UNIQUE (l_uuid) ); CREATE OR REPLACE TRIGGER uacreg_tr before insert on uacreg FOR EACH ROW BEGIN auto_id(:NEW.id); END uacreg_tr; / BEGIN map2users('uacreg'); END; / kamailio-4.0.4/utils/kamctl/oracle/dispatcher-create.sql0000644000000000000000000000104212223032460021762 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dispatcher','4'); CREATE TABLE dispatcher ( id NUMBER(10) PRIMARY KEY, setid NUMBER(10) DEFAULT 0 NOT NULL, destination VARCHAR2(192) DEFAULT '', flags NUMBER(10) DEFAULT 0 NOT NULL, priority NUMBER(10) DEFAULT 0 NOT NULL, attrs VARCHAR2(128) DEFAULT '', description VARCHAR2(64) DEFAULT '' ); CREATE OR REPLACE TRIGGER dispatcher_tr before insert on dispatcher FOR EACH ROW BEGIN auto_id(:NEW.id); END dispatcher_tr; / BEGIN map2users('dispatcher'); END; / kamailio-4.0.4/utils/kamctl/oracle/speeddial-create.sql0000644000000000000000000000127112223032460021572 0ustar rootrootINSERT INTO version (table_name, table_version) values ('speed_dial','2'); CREATE TABLE speed_dial ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', sd_username VARCHAR2(64) DEFAULT '', sd_domain VARCHAR2(64) DEFAULT '', new_uri VARCHAR2(128) DEFAULT '', fname VARCHAR2(64) DEFAULT '', lname VARCHAR2(64) DEFAULT '', description VARCHAR2(64) DEFAULT '', CONSTRAINT speed_dial_speed_dial_idx UNIQUE (username, domain, sd_domain, sd_username) ); CREATE OR REPLACE TRIGGER speed_dial_tr before insert on speed_dial FOR EACH ROW BEGIN auto_id(:NEW.id); END speed_dial_tr; / BEGIN map2users('speed_dial'); END; / kamailio-4.0.4/utils/kamctl/oracle/siptrace-create.sql0000644000000000000000000000172212223032460021453 0ustar rootrootINSERT INTO version (table_name, table_version) values ('sip_trace','3'); CREATE TABLE sip_trace ( id NUMBER(10) PRIMARY KEY, time_stamp DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), time_us NUMBER(10) DEFAULT 0 NOT NULL, callid VARCHAR2(255) DEFAULT '', traced_user VARCHAR2(128) DEFAULT '', msg CLOB, method VARCHAR2(50) DEFAULT '', status VARCHAR2(128) DEFAULT '', fromip VARCHAR2(50) DEFAULT '', toip VARCHAR2(50) DEFAULT '', fromtag VARCHAR2(64) DEFAULT '', direction VARCHAR2(4) DEFAULT '' ); CREATE OR REPLACE TRIGGER sip_trace_tr before insert on sip_trace FOR EACH ROW BEGIN auto_id(:NEW.id); END sip_trace_tr; / BEGIN map2users('sip_trace'); END; / CREATE INDEX sip_trace_traced_user_idx ON sip_trace (traced_user); CREATE INDEX sip_trace_date_idx ON sip_trace (time_stamp); CREATE INDEX sip_trace_fromip_idx ON sip_trace (fromip); CREATE INDEX sip_trace_callid_idx ON sip_trace (callid); kamailio-4.0.4/utils/kamctl/oracle/dialplan-create.sql0000644000000000000000000000074712223032460021433 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dialplan','1'); CREATE TABLE dialplan ( id NUMBER(10) PRIMARY KEY, dpid NUMBER(10), pr NUMBER(10), match_op NUMBER(10), match_exp VARCHAR2(64), match_len NUMBER(10), subst_exp VARCHAR2(64), repl_exp VARCHAR2(32), attrs VARCHAR2(32) ); CREATE OR REPLACE TRIGGER dialplan_tr before insert on dialplan FOR EACH ROW BEGIN auto_id(:NEW.id); END dialplan_tr; / BEGIN map2users('dialplan'); END; / kamailio-4.0.4/utils/kamctl/oracle/group-create.sql0000644000000000000000000000165012223032460020775 0ustar rootrootINSERT INTO version (table_name, table_version) values ('grp','2'); CREATE TABLE grp ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', grp VARCHAR2(64) DEFAULT '', last_modified DATE DEFAULT to_date('1900-01-01 00:00:01','yyyy-mm-dd hh24:mi:ss'), CONSTRAINT grp_account_group_idx UNIQUE (username, domain, grp) ); CREATE OR REPLACE TRIGGER grp_tr before insert on grp FOR EACH ROW BEGIN auto_id(:NEW.id); END grp_tr; / BEGIN map2users('grp'); END; / INSERT INTO version (table_name, table_version) values ('re_grp','1'); CREATE TABLE re_grp ( id NUMBER(10) PRIMARY KEY, reg_exp VARCHAR2(128) DEFAULT '', group_id NUMBER(10) DEFAULT 0 NOT NULL ); CREATE OR REPLACE TRIGGER re_grp_tr before insert on re_grp FOR EACH ROW BEGIN auto_id(:NEW.id); END re_grp_tr; / BEGIN map2users('re_grp'); END; / CREATE INDEX re_grp_group_idx ON re_grp (group_id); kamailio-4.0.4/utils/kamctl/oracle/lcr-create.sql0000644000000000000000000000355312223032460020425 0ustar rootrootINSERT INTO version (table_name, table_version) values ('lcr_gw','3'); CREATE TABLE lcr_gw ( id NUMBER(10) PRIMARY KEY, lcr_id NUMBER(5), gw_name VARCHAR2(128), ip_addr VARCHAR2(50), hostname VARCHAR2(64), port NUMBER(5), params VARCHAR2(64), uri_scheme NUMBER(5), transport NUMBER(5), strip NUMBER(5), prefix VARCHAR2(16) DEFAULT NULL, tag VARCHAR2(64) DEFAULT NULL, flags NUMBER(10) DEFAULT 0 NOT NULL, defunct NUMBER(10) DEFAULT NULL ); CREATE OR REPLACE TRIGGER lcr_gw_tr before insert on lcr_gw FOR EACH ROW BEGIN auto_id(:NEW.id); END lcr_gw_tr; / BEGIN map2users('lcr_gw'); END; / CREATE INDEX lcr_gw_lcr_id_idx ON lcr_gw (lcr_id); INSERT INTO version (table_name, table_version) values ('lcr_rule_target','1'); CREATE TABLE lcr_rule_target ( id NUMBER(10) PRIMARY KEY, lcr_id NUMBER(5), rule_id NUMBER(10), gw_id NUMBER(10), priority NUMBER(5), weight NUMBER(10) DEFAULT 1 NOT NULL, CONSTRAINT ORA_rule_id_gw_id_idx UNIQUE (rule_id, gw_id) ); CREATE OR REPLACE TRIGGER lcr_rule_target_tr before insert on lcr_rule_target FOR EACH ROW BEGIN auto_id(:NEW.id); END lcr_rule_target_tr; / BEGIN map2users('lcr_rule_target'); END; / CREATE INDEX lcr_rule_target_lcr_id_idx ON lcr_rule_target (lcr_id); INSERT INTO version (table_name, table_version) values ('lcr_rule','2'); CREATE TABLE lcr_rule ( id NUMBER(10) PRIMARY KEY, lcr_id NUMBER(5), prefix VARCHAR2(16) DEFAULT NULL, from_uri VARCHAR2(64) DEFAULT NULL, request_uri VARCHAR2(64) DEFAULT NULL, stopper NUMBER(10) DEFAULT 0 NOT NULL, enabled NUMBER(10) DEFAULT 1 NOT NULL, CONSTRAINT ORA_lcr_id_prefix_from_uri_idx UNIQUE (lcr_id, prefix, from_uri) ); CREATE OR REPLACE TRIGGER lcr_rule_tr before insert on lcr_rule FOR EACH ROW BEGIN auto_id(:NEW.id); END lcr_rule_tr; / BEGIN map2users('lcr_rule'); END; / kamailio-4.0.4/utils/kamctl/oracle/auth_db-create.sql0000644000000000000000000000126212223032460021246 0ustar rootrootINSERT INTO version (table_name, table_version) values ('subscriber','6'); CREATE TABLE subscriber ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64) DEFAULT '', domain VARCHAR2(64) DEFAULT '', password VARCHAR2(25) DEFAULT '', email_address VARCHAR2(64) DEFAULT '', ha1 VARCHAR2(64) DEFAULT '', ha1b VARCHAR2(64) DEFAULT '', rpid VARCHAR2(64) DEFAULT NULL, CONSTRAINT subscriber_account_idx UNIQUE (username, domain) ); CREATE OR REPLACE TRIGGER subscriber_tr before insert on subscriber FOR EACH ROW BEGIN auto_id(:NEW.id); END subscriber_tr; / BEGIN map2users('subscriber'); END; / CREATE INDEX subscriber_username_idx ON subscriber (username); kamailio-4.0.4/utils/kamctl/oracle/admin/0000755000000000000000000000000012223032460016745 5ustar rootrootkamailio-4.0.4/utils/kamctl/oracle/admin/_create_as_sys.tmpl0000644000000000000000000000100112223032460022616 0ustar rootrootcreate user %DBROOTUSER% identified by %DBROOTPW% default tablespace DATA temporary tablespace TEMP profile DEFAULT; -- Grant/Revoke role privileges grant connect to %DBROOTUSER% with admin option; grant resource to %DBROOTUSER%; -- Grant/Revoke system privileges grant create any synonym to %DBROOTUSER%; grant create role to %DBROOTUSER%; grant create user to %DBROOTUSER%; grant drop user to %DBROOTUSER%; grant unlimited tablespace to %DBROOTUSER%; grant select on sys.dba_role_privs to %DBROOTUSER%; kamailio-4.0.4/utils/kamctl/oracle/admin/README0000644000000000000000000000022112223032460017620 0ustar rootrootreplace '%DBROOTUSER%' to user (scheme) name and '%DBROOTPW%' to password in _create_as_sys_tmpl, and tell Oracle administrator to execute it :) kamailio-4.0.4/utils/kamctl/oracle/admin/_drop_as_sys.tmpl0000644000000000000000000000004012223032460022321 0ustar rootrootDROP USER %DBROOTUSER% CASCADE; kamailio-4.0.4/utils/kamctl/oracle/presence-create.sql0000644000000000000000000001067212223032460021451 0ustar rootrootINSERT INTO version (table_name, table_version) values ('presentity','3'); CREATE TABLE presentity ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64), domain VARCHAR2(64), event VARCHAR2(64), etag VARCHAR2(64), expires NUMBER(10), received_time NUMBER(10), body BLOB, sender VARCHAR2(128), CONSTRAINT presentity_presentity_idx UNIQUE (username, domain, event, etag) ); CREATE OR REPLACE TRIGGER presentity_tr before insert on presentity FOR EACH ROW BEGIN auto_id(:NEW.id); END presentity_tr; / BEGIN map2users('presentity'); END; / CREATE INDEX presentity_presentity_expires ON presentity (expires); CREATE INDEX presentity_account_idx ON presentity (username, domain, event); INSERT INTO version (table_name, table_version) values ('active_watchers','11'); CREATE TABLE active_watchers ( id NUMBER(10) PRIMARY KEY, presentity_uri VARCHAR2(128), watcher_username VARCHAR2(64), watcher_domain VARCHAR2(64), to_user VARCHAR2(64), to_domain VARCHAR2(64), event VARCHAR2(64) DEFAULT 'presence', event_id VARCHAR2(64), to_tag VARCHAR2(64), from_tag VARCHAR2(64), callid VARCHAR2(255), local_cseq NUMBER(10), remote_cseq NUMBER(10), contact VARCHAR2(128), record_route CLOB, expires NUMBER(10), status NUMBER(10) DEFAULT 2 NOT NULL, reason VARCHAR2(64), version NUMBER(10) DEFAULT 0 NOT NULL, socket_info VARCHAR2(64), local_contact VARCHAR2(128), from_user VARCHAR2(64), from_domain VARCHAR2(64), updated NUMBER(10), updated_winfo NUMBER(10), CONSTRAINT ORA_active_watchers_idx UNIQUE (callid, to_tag, from_tag) ); CREATE OR REPLACE TRIGGER active_watchers_tr before insert on active_watchers FOR EACH ROW BEGIN auto_id(:NEW.id); END active_watchers_tr; / BEGIN map2users('active_watchers'); END; / CREATE INDEX ORA_active_watchers_expires ON active_watchers (expires); CREATE INDEX ORA_active_watchers_pres ON active_watchers (presentity_uri, event); CREATE INDEX active_watchers_updated_idx ON active_watchers (updated); CREATE INDEX ORA_updated_winfo_idx ON active_watchers (updated_winfo, presentity_uri); INSERT INTO version (table_name, table_version) values ('watchers','3'); CREATE TABLE watchers ( id NUMBER(10) PRIMARY KEY, presentity_uri VARCHAR2(128), watcher_username VARCHAR2(64), watcher_domain VARCHAR2(64), event VARCHAR2(64) DEFAULT 'presence', status NUMBER(10), reason VARCHAR2(64), inserted_time NUMBER(10), CONSTRAINT watchers_watcher_idx UNIQUE (presentity_uri, watcher_username, watcher_domain, event) ); CREATE OR REPLACE TRIGGER watchers_tr before insert on watchers FOR EACH ROW BEGIN auto_id(:NEW.id); END watchers_tr; / BEGIN map2users('watchers'); END; / INSERT INTO version (table_name, table_version) values ('xcap','4'); CREATE TABLE xcap ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64), domain VARCHAR2(64), doc BLOB, doc_type NUMBER(10), etag VARCHAR2(64), source NUMBER(10), doc_uri VARCHAR2(255), port NUMBER(10), CONSTRAINT xcap_doc_uri_idx UNIQUE (doc_uri) ); CREATE OR REPLACE TRIGGER xcap_tr before insert on xcap FOR EACH ROW BEGIN auto_id(:NEW.id); END xcap_tr; / BEGIN map2users('xcap'); END; / CREATE INDEX xcap_account_doc_type_idx ON xcap (username, domain, doc_type); CREATE INDEX xcap_account_doc_type_uri_idx ON xcap (username, domain, doc_type, doc_uri); CREATE INDEX xcap_account_doc_uri_idx ON xcap (username, domain, doc_uri); INSERT INTO version (table_name, table_version) values ('pua','7'); CREATE TABLE pua ( id NUMBER(10) PRIMARY KEY, pres_uri VARCHAR2(128), pres_id VARCHAR2(255), event NUMBER(10), expires NUMBER(10), desired_expires NUMBER(10), flag NUMBER(10), etag VARCHAR2(64), tuple_id VARCHAR2(64), watcher_uri VARCHAR2(128), call_id VARCHAR2(255), to_tag VARCHAR2(64), from_tag VARCHAR2(64), cseq NUMBER(10), record_route CLOB, contact VARCHAR2(128), remote_contact VARCHAR2(128), version NUMBER(10), extra_headers CLOB, CONSTRAINT pua_pua_idx UNIQUE (etag, tuple_id, call_id, from_tag) ); CREATE OR REPLACE TRIGGER pua_tr before insert on pua FOR EACH ROW BEGIN auto_id(:NEW.id); END pua_tr; / BEGIN map2users('pua'); END; / CREATE INDEX pua_expires_idx ON pua (expires); CREATE INDEX pua_dialog1_idx ON pua (pres_id, pres_uri); CREATE INDEX pua_dialog2_idx ON pua (call_id, from_tag); CREATE INDEX pua_record_idx ON pua (pres_id); kamailio-4.0.4/utils/kamctl/oracle/uid_avp_db-create.sql0000644000000000000000000000104612223032460021734 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_user_attrs','3'); CREATE TABLE uid_user_attrs ( id NUMBER(10) PRIMARY KEY, uuid VARCHAR2(64), name VARCHAR2(32), value VARCHAR2(128), type NUMBER(10) DEFAULT 0 NOT NULL, flags NUMBER(10) DEFAULT 0 NOT NULL, CONSTRAINT uid_user_attrs_userattrs_idx UNIQUE (uuid, name, value) ); CREATE OR REPLACE TRIGGER uid_user_attrs_tr before insert on uid_user_attrs FOR EACH ROW BEGIN auto_id(:NEW.id); END uid_user_attrs_tr; / BEGIN map2users('uid_user_attrs'); END; / kamailio-4.0.4/utils/kamctl/oracle/rls-create.sql0000644000000000000000000000413612223032460020443 0ustar rootrootINSERT INTO version (table_name, table_version) values ('rls_presentity','1'); CREATE TABLE rls_presentity ( id NUMBER(10) PRIMARY KEY, rlsubs_did VARCHAR2(255), resource_uri VARCHAR2(128), content_type VARCHAR2(255), presence_state BLOB, expires NUMBER(10), updated NUMBER(10), auth_state NUMBER(10), reason VARCHAR2(64), CONSTRAINT ORA_rls_presentity_idx UNIQUE (rlsubs_did, resource_uri) ); CREATE OR REPLACE TRIGGER rls_presentity_tr before insert on rls_presentity FOR EACH ROW BEGIN auto_id(:NEW.id); END rls_presentity_tr; / BEGIN map2users('rls_presentity'); END; / CREATE INDEX rls_presentity_rlsubs_idx ON rls_presentity (rlsubs_did); CREATE INDEX rls_presentity_updated_idx ON rls_presentity (updated); CREATE INDEX rls_presentity_expires_idx ON rls_presentity (expires); INSERT INTO version (table_name, table_version) values ('rls_watchers','3'); CREATE TABLE rls_watchers ( id NUMBER(10) PRIMARY KEY, presentity_uri VARCHAR2(128), to_user VARCHAR2(64), to_domain VARCHAR2(64), watcher_username VARCHAR2(64), watcher_domain VARCHAR2(64), event VARCHAR2(64) DEFAULT 'presence', event_id VARCHAR2(64), to_tag VARCHAR2(64), from_tag VARCHAR2(64), callid VARCHAR2(255), local_cseq NUMBER(10), remote_cseq NUMBER(10), contact VARCHAR2(128), record_route CLOB, expires NUMBER(10), status NUMBER(10) DEFAULT 2 NOT NULL, reason VARCHAR2(64), version NUMBER(10) DEFAULT 0 NOT NULL, socket_info VARCHAR2(64), local_contact VARCHAR2(128), from_user VARCHAR2(64), from_domain VARCHAR2(64), updated NUMBER(10), CONSTRAINT rls_watchers_rls_watcher_idx UNIQUE (callid, to_tag, from_tag) ); CREATE OR REPLACE TRIGGER rls_watchers_tr before insert on rls_watchers FOR EACH ROW BEGIN auto_id(:NEW.id); END rls_watchers_tr; / BEGIN map2users('rls_watchers'); END; / CREATE INDEX ORA_rls_watchers_update ON rls_watchers (watcher_username, watcher_domain, event); CREATE INDEX ORA_rls_watchers_expires ON rls_watchers (expires); CREATE INDEX rls_watchers_updated_idx ON rls_watchers (updated); kamailio-4.0.4/utils/kamctl/oracle/uid_domain-create.sql0000644000000000000000000000214712223032460021753 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_domain','2'); CREATE TABLE uid_domain ( id NUMBER(10) PRIMARY KEY, did VARCHAR2(64), domain VARCHAR2(64), flags NUMBER(10) DEFAULT 0 NOT NULL, CONSTRAINT uid_domain_domain_idx UNIQUE (domain) ); CREATE OR REPLACE TRIGGER uid_domain_tr before insert on uid_domain FOR EACH ROW BEGIN auto_id(:NEW.id); END uid_domain_tr; / BEGIN map2users('uid_domain'); END; / CREATE INDEX uid_domain_did_idx ON uid_domain (did); INSERT INTO version (table_name, table_version) values ('uid_domain_attrs','1'); CREATE TABLE uid_domain_attrs ( id NUMBER(10) PRIMARY KEY, did VARCHAR2(64), name VARCHAR2(32), type NUMBER(10) DEFAULT 0 NOT NULL, value VARCHAR2(128), flags NUMBER(10) DEFAULT 0 NOT NULL, CONSTRAINT ORA_domain_attr_idx UNIQUE (did, name, value) ); CREATE OR REPLACE TRIGGER uid_domain_attrs_tr before insert on uid_domain_attrs FOR EACH ROW BEGIN auto_id(:NEW.id); END uid_domain_attrs_tr; / BEGIN map2users('uid_domain_attrs'); END; / CREATE INDEX uid_domain_attrs_domain_did ON uid_domain_attrs (did, flags); kamailio-4.0.4/utils/kamctl/oracle/cpl-create.sql0000644000000000000000000000064112223032460020416 0ustar rootrootINSERT INTO version (table_name, table_version) values ('cpl','1'); CREATE TABLE cpl ( id NUMBER(10) PRIMARY KEY, username VARCHAR2(64), domain VARCHAR2(64) DEFAULT '', cpl_xml CLOB, cpl_bin CLOB, CONSTRAINT cpl_account_idx UNIQUE (username, domain) ); CREATE OR REPLACE TRIGGER cpl_tr before insert on cpl FOR EACH ROW BEGIN auto_id(:NEW.id); END cpl_tr; / BEGIN map2users('cpl'); END; / kamailio-4.0.4/utils/kamctl/db_berkeley/0000755000000000000000000000000012223032460016657 5ustar rootrootkamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/0000755000000000000000000000000012223032461020446 5ustar rootrootkamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/dr_rules0000644000000000000000000000034012223032460022204 0ustar rootrootMETADATA_COLUMNS ruleid(int) groupid(str) prefix(str) timerec(str) priority(int) routeid(str) gwlist(str) description(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|0|NIL|NIL|'' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/mtrees0000644000000000000000000000022212223032460021663 0ustar rootrootMETADATA_COLUMNS id(int) tname(str) tprefix(str) tvalue(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|'' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/dbaliases0000644000000000000000000000026512223032460022322 0ustar rootrootMETADATA_COLUMNS id(int) alias_username(str) alias_domain(str) username(str) domain(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|'' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/presentity0000644000000000000000000000036012223032460022575 0ustar rootrootMETADATA_COLUMNS id(int) username(str) domain(str) event(str) etag(str) expires(int) received_time(int) body(str) sender(str) METADATA_KEY 1 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/carrierroute0000644000000000000000000000045012223032460023075 0ustar rootrootMETADATA_COLUMNS id(int) carrier(int) domain(int) scan_prefix(str) flags(int) mask(int) prob(double) strip(int) rewrite_host(str) rewrite_prefix(str) rewrite_suffix(str) description(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|0|0|''|0|0|0|0|''|''|''|NULL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/carrier_name0000644000000000000000000000017112223032460023016 0ustar rootrootMETADATA_COLUMNS id(int) carrier(str) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NULL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/grp0000644000000000000000000000030512223032460021156 0ustar rootrootMETADATA_COLUMNS id(int) username(str) domain(str) grp(str) last_modified(datetime) METADATA_KEY 1 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|'1900-01-01 00:00:01' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/rls_watchers0000644000000000000000000000104412223032460023067 0ustar rootrootMETADATA_COLUMNS id(int) presentity_uri(str) to_user(str) to_domain(str) watcher_username(str) watcher_domain(str) event(str) event_id(str) to_tag(str) from_tag(str) callid(str) local_cseq(int) remote_cseq(int) contact(str) record_route(str) expires(int) status(int) reason(str) version(int) socket_info(str) local_contact(str) from_user(str) from_domain(str) updated(int) METADATA_KEY 4 5 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|'presence'|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|2|NIL|0|NIL|NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/cpl0000644000000000000000000000025412223032460021147 0ustar rootrootMETADATA_COLUMNS id(int) username(str) domain(str) cpl_xml(str) cpl_bin(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|''|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/domain_attrs0000644000000000000000000000031312223032460023051 0ustar rootrootMETADATA_COLUMNS id(int) did(str) name(str) type(int) value(str) last_modified(datetime) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|'1900-01-01 00:00:01' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/uid_user_attrs0000644000000000000000000000025012223032460023421 0ustar rootrootMETADATA_COLUMNS id(int) uid(str) name(str) value(str) type(int) flags(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|0|0 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/location_attrs0000644000000000000000000000036212223032460023416 0ustar rootrootMETADATA_COLUMNS id(int) ruid(str) username(str) domain(str) aname(str) atype(int) avalue(str) last_modified(datetime) METADATA_KEY 1 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|NULL|''|0|''|'1900-01-01 00:00:01' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/uid_global_attrs0000644000000000000000000000023312223032460023704 0ustar rootrootMETADATA_COLUMNS id(int) name(str) type(int) value(str) flags(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|0|NIL|0 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/subscriber0000644000000000000000000000033012223032460022527 0ustar rootrootMETADATA_COLUMNS id(int) username(str) domain(str) password(str) email_address(str) ha1(str) ha1b(str) rpid(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|''|''|''|NULL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/aliases0000644000000000000000000000071412223032460022013 0ustar rootrootMETADATA_COLUMNS id(int) ruid(str) username(str) domain(str) contact(str) received(str) path(str) expires(datetime) q(double) callid(str) cseq(int) last_modified(datetime) flags(int) cflags(int) user_agent(str) socket(str) methods(int) instance(str) reg_id(int) METADATA_KEY 1 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|NULL|''|NULL|NULL|'2030-05-28 21:32:15'|1.0|'Default-Call-ID'|1|'1900-01-01 00:00:01'|0|0|''|NULL|NULL|NULL|0 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/dr_groups0000644000000000000000000000025112223032460022372 0ustar rootrootMETADATA_COLUMNS id(int) username(str) domain(str) groupid(int) description(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|''|0|'' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/userblacklist0000644000000000000000000000024512223032460023240 0ustar rootrootMETADATA_COLUMNS id(int) username(str) domain(str) prefix(str) whitelist(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|0 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/dr_gw_lists0000644000000000000000000000021112223032460022702 0ustar rootrootMETADATA_COLUMNS id(int) gwlist(str) description(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|'' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/usr_preferences0000644000000000000000000000035712223032460023567 0ustar rootrootMETADATA_COLUMNS id(int) uuid(str) username(str) domain(str) attribute(str) type(int) value(str) last_modified(datetime) METADATA_KEY 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|0|''|''|0|''|'1900-01-01 00:00:01' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/uid_domain0000644000000000000000000000021712223032460022500 0ustar rootrootMETADATA_COLUMNS id(int) did(str) domain(str) flags(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|0 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/lcr_rule0000644000000000000000000000031712223032460022200 0ustar rootrootMETADATA_COLUMNS id(int) lcr_id(int) prefix(str) from_uri(str) request_uri(str) stopper(int) enabled(int) METADATA_KEY 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NULL|NULL|NULL|0|1 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/trusted0000644000000000000000000000025212223032460022061 0ustar rootrootMETADATA_COLUMNS id(int) src_ip(str) proto(str) from_pattern(str) tag(str) METADATA_KEY 1 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NULL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/lcr_rule_target0000644000000000000000000000026512223032460023550 0ustar rootrootMETADATA_COLUMNS id(int) lcr_id(int) rule_id(int) gw_id(int) priority(int) weight(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|1 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/dialog0000644000000000000000000000103612223032460021627 0ustar rootrootMETADATA_COLUMNS id(int) hash_entry(int) hash_id(int) callid(str) from_uri(str) from_tag(str) to_uri(str) to_tag(str) caller_cseq(str) callee_cseq(str) caller_route_set(str) callee_route_set(str) caller_contact(str) callee_contact(str) caller_sock(str) callee_sock(str) state(int) start_time(int) timeout(int) sflags(int) iflags(int) toroute_name(str) req_uri(str) xdata(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|0|0|0|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/uid_uri_attrs0000644000000000000000000000031412223032460023243 0ustar rootrootMETADATA_COLUMNS id(int) username(str) did(str) name(str) value(str) type(int) flags(int) scheme(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|0|0|'sip' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/dialplan0000644000000000000000000000035312223032460022155 0ustar rootrootMETADATA_COLUMNS id(int) dpid(int) pr(int) match_op(int) match_exp(str) match_len(int) subst_exp(str) repl_exp(str) attrs(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/missed_calls0000644000000000000000000000033412223032460023032 0ustar rootrootMETADATA_COLUMNS id(int) method(str) from_tag(str) to_tag(str) callid(str) sip_code(str) sip_reason(str) time(datetime) METADATA_KEY 4 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|''|''|''|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/htable0000644000000000000000000000027112223032460021627 0ustar rootrootMETADATA_COLUMNS id(int) key_name(str) key_type(int) value_type(int) key_value(str) expires(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|0|0|''|0 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/address0000644000000000000000000000025112223032460022013 0ustar rootrootMETADATA_COLUMNS id(int) grp(int) ip_addr(str) mask(int) port(int) tag(str) METADATA_KEY 1 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|1|NIL|32|0|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/dispatcher0000644000000000000000000000031312223032460022513 0ustar rootrootMETADATA_COLUMNS id(int) setid(int) destination(str) flags(int) priority(int) attrs(str) description(str) METADATA_KEY 1 3 4 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|0|''|0|0|''|'' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/location0000644000000000000000000000071412223032460022202 0ustar rootrootMETADATA_COLUMNS id(int) ruid(str) username(str) domain(str) contact(str) received(str) path(str) expires(datetime) q(double) callid(str) cseq(int) last_modified(datetime) flags(int) cflags(int) user_agent(str) socket(str) methods(int) instance(str) reg_id(int) METADATA_KEY 1 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|NULL|''|NULL|NULL|'2030-05-28 21:32:15'|1.0|'Default-Call-ID'|1|'1900-01-01 00:00:01'|0|0|''|NULL|NULL|NULL|0 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/silo0000644000000000000000000000046312223032460021341 0ustar rootrootMETADATA_COLUMNS id(int) src_addr(str) dst_addr(str) username(str) domain(str) inc_time(int) exp_time(int) snd_time(int) ctype(str) body(str) extra_hdrs(str) callid(str) status(int) METADATA_KEY 3 4 11 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|''|0|0|0|'text/plain'|''|''|''|0 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/sca_subscriptions0000644000000000000000000000046412223032460024131 0ustar rootrootMETADATA_COLUMNS id(int) subscriber(str) aor(str) event(int) expires(int) state(int) app_idx(int) call_id(str) from_tag(str) to_tag(str) record_route(str) notify_cseq(int) subscribe_cseq(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|0|0|0|0|NIL|NIL|NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/uacreg0000644000000000000000000000043012223032460021633 0ustar rootrootMETADATA_COLUMNS id(int) l_uuid(str) l_username(str) l_domain(str) r_username(str) r_domain(str) realm(str) auth_username(str) auth_password(str) auth_proxy(str) expires(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|''|''|''|''|''|''|0 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/acc0000644000000000000000000000033412223032460021116 0ustar rootrootMETADATA_COLUMNS id(int) method(str) from_tag(str) to_tag(str) callid(str) sip_code(str) sip_reason(str) time(datetime) METADATA_KEY 4 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|''|''|''|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/rls_presentity0000644000000000000000000000040512223032460023455 0ustar rootrootMETADATA_COLUMNS id(int) rlsubs_did(str) resource_uri(str) content_type(str) presence_state(str) expires(int) updated(int) auth_state(int) reason(str) METADATA_KEY 1 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/uri0000644000000000000000000000031212223032460021163 0ustar rootrootMETADATA_COLUMNS id(int) username(str) domain(str) uri_user(str) last_modified(datetime) METADATA_KEY 1 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|'1900-01-01 00:00:01' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/version0000644000000000000000000000312312223032461022055 0ustar rootrootMETADATA_COLUMNS table_name(str) table_version(int) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|0 acc| acc|4 active_watchers| active_watchers|11 address| address|6 aliases| aliases|6 carrier_name| carrier_name|1 carrierfailureroute| carrierfailureroute|2 carrierroute| carrierroute|3 cpl| cpl|1 dbaliases| dbaliases|1 dialog| dialog|7 dialog_vars| dialog_vars|1 dialplan| dialplan|1 dispatcher| dispatcher|4 domain| domain|2 domain_attrs| domain_attrs|1 domain_name| domain_name|1 domainpolicy| domainpolicy|2 dr_gateways| dr_gateways|3 dr_groups| dr_groups|2 dr_gw_lists| dr_gw_lists|1 dr_rules| dr_rules|3 globalblacklist| globalblacklist|1 grp| grp|2 htable| htable|2 imc_members| imc_members|1 imc_rooms| imc_rooms|1 lcr_gw| lcr_gw|3 lcr_rule| lcr_rule|2 lcr_rule_target| lcr_rule_target|1 location| location|6 location_attrs| location_attrs|1 matrix| matrix|1 missed_calls| missed_calls|3 mtree| mtree|1 mtrees| mtrees|2 pdt| pdt|1 pl_pipes| pl_pipes|1 presentity| presentity|3 pua| pua|7 purplemap| purplemap|1 re_grp| re_grp|1 rls_presentity| rls_presentity|1 rls_watchers| rls_watchers|3 sca_subscriptions| sca_subscriptions|1 silo| silo|7 sip_trace| sip_trace|3 speed_dial| speed_dial|2 subscriber| subscriber|6 trusted| trusted|5 uacreg| uacreg|1 uid_credentials| uid_credentials|7 uid_domain| uid_domain|2 uid_domain_attrs| uid_domain_attrs|1 uid_global_attrs| uid_global_attrs|1 uid_uri| uid_uri|3 uid_uri_attrs| uid_uri_attrs|2 uid_user_attrs| uid_user_attrs|3 uri| uri|1 userblacklist| userblacklist|1 usr_preferences| usr_preferences|2 watchers| watchers|3 xcap| xcap|4 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/sip_trace0000644000000000000000000000045512223032460022345 0ustar rootrootMETADATA_COLUMNS id(int) time_stamp(datetime) time_us(int) callid(str) traced_user(str) msg(str) method(str) status(str) fromip(str) toip(str) fromtag(str) direction(str) METADATA_KEY 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|'1900-01-01 00:00:01'|0|''|''|NIL|''|''|''|''|''|'' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/domainpolicy0000644000000000000000000000026412223032460023061 0ustar rootrootMETADATA_COLUMNS id(int) rule(str) type(str) att(str) val(str) description(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/matrix0000644000000000000000000000020512223032460021671 0ustar rootrootMETADATA_COLUMNS first(int) second(int) res(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/uid_credentials0000644000000000000000000000034412223032460023527 0ustar rootrootMETADATA_COLUMNS id(int) auth_username(str) did(str) realm(str) password(str) flags(int) ha1(str) ha1b(str) uid(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|'_default'|NIL|''|0|NIL|''|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/active_watchers0000644000000000000000000000107312223032460023544 0ustar rootrootMETADATA_COLUMNS id(int) presentity_uri(str) watcher_username(str) watcher_domain(str) to_user(str) to_domain(str) event(str) event_id(str) to_tag(str) from_tag(str) callid(str) local_cseq(int) remote_cseq(int) contact(str) record_route(str) expires(int) status(int) reason(str) version(int) socket_info(str) local_contact(str) from_user(str) from_domain(str) updated(int) updated_winfo(int) METADATA_KEY 1 6 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|'presence'|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|2|NIL|0|NIL|NIL|NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/purplemap0000644000000000000000000000026312223032460022376 0ustar rootrootMETADATA_COLUMNS id(int) sip_user(str) ext_user(str) ext_prot(str) ext_pass(str) METADATA_KEY 1 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/xcap0000644000000000000000000000034712223032460021327 0ustar rootrootMETADATA_COLUMNS id(int) username(str) domain(str) doc(str) doc_type(int) etag(str) source(int) doc_uri(str) port(int) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/pdt0000644000000000000000000000022712223032460021160 0ustar rootrootMETADATA_COLUMNS id(int) sdomain(str) prefix(str) domain(str) METADATA_KEY 1 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|'' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/re_grp0000644000000000000000000000020612223032460021644 0ustar rootrootMETADATA_COLUMNS id(int) reg_exp(str) group_id(int) METADATA_KEY 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|0 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/watchers0000644000000000000000000000040012223032460022202 0ustar rootrootMETADATA_COLUMNS id(int) presentity_uri(str) watcher_username(str) watcher_domain(str) event(str) status(int) reason(str) inserted_time(int) METADATA_KEY 1 4 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|'presence'|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/lcr_gw0000644000000000000000000000047512223032460021653 0ustar rootrootMETADATA_COLUMNS id(int) lcr_id(int) gw_name(str) ip_addr(str) hostname(str) port(int) params(str) uri_scheme(int) transport(int) strip(int) prefix(str) tag(str) flags(int) defunct(int) METADATA_KEY 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NULL|NULL|0|NULL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/dialog_vars0000644000000000000000000000027012223032460022661 0ustar rootrootMETADATA_COLUMNS id(int) hash_entry(int) hash_id(int) dialog_key(str) dialog_value(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/globalblacklist0000644000000000000000000000023312223032460023517 0ustar rootrootMETADATA_COLUMNS id(int) prefix(str) whitelist(int) description(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|0|NULL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/uid_domain_attrs0000644000000000000000000000025012223032460023712 0ustar rootrootMETADATA_COLUMNS id(int) did(str) name(str) type(int) value(str) flags(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|0|NIL|0 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/carrierfailureroute0000644000000000000000000000040312223032460024443 0ustar rootrootMETADATA_COLUMNS id(int) carrier(int) domain(int) scan_prefix(str) host_name(str) reply_code(str) flags(int) mask(int) next_domain(int) description(str) METADATA_KEY 1 2 8 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|0|0|''|''|''|0|0|0|NULL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/pl_pipes0000644000000000000000000000022412223032460022201 0ustar rootrootMETADATA_COLUMNS id(int) pipeid(str) algorithm(str) plimit(int) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|0 kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/uid_uri0000644000000000000000000000026012223032460022026 0ustar rootrootMETADATA_COLUMNS id(int) uid(str) did(str) username(str) flags(int) scheme(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|0|'sip' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/domain0000644000000000000000000000026112223032460021636 0ustar rootrootMETADATA_COLUMNS id(int) domain(str) did(str) last_modified(datetime) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NULL|'1900-01-01 00:00:01' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/speed_dial0000644000000000000000000000035712223032460022466 0ustar rootrootMETADATA_COLUMNS id(int) username(str) domain(str) sd_username(str) sd_domain(str) new_uri(str) fname(str) lname(str) description(str) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|''|''|''|''|''|''|'' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/domain_name0000644000000000000000000000017012223032460022635 0ustar rootrootMETADATA_COLUMNS id(int) domain(str) METADATA_KEY 0 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NULL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/dr_gateways0000644000000000000000000000031212223032460022675 0ustar rootrootMETADATA_COLUMNS gwid(int) type(int) address(str) strip(int) pri_prefix(str) attrs(str) description(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|0|NIL|0|NULL|NULL|'' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/imc_members0000644000000000000000000000025112223032460022650 0ustar rootrootMETADATA_COLUMNS id(int) username(str) domain(str) room(str) flag(int) METADATA_KEY 1 2 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/pua0000644000000000000000000000066012223032460021157 0ustar rootrootMETADATA_COLUMNS id(int) pres_uri(str) pres_id(str) event(int) expires(int) desired_expires(int) flag(int) etag(str) tuple_id(str) watcher_uri(str) call_id(str) to_tag(str) from_tag(str) cseq(int) record_route(str) contact(str) remote_contact(str) version(int) extra_headers(str) METADATA_KEY 1 3 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/mtree0000644000000000000000000000020412223032460021500 0ustar rootrootMETADATA_COLUMNS id(int) tprefix(str) tvalue(str) METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|''|'' kamailio-4.0.4/utils/kamctl/db_berkeley/kamailio/imc_rooms0000644000000000000000000000022512223032460022356 0ustar rootrootMETADATA_COLUMNS id(int) name(str) domain(str) flag(int) METADATA_KEY 1 2 METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NIL|NIL|NIL|NIL kamailio-4.0.4/utils/kamctl/kamdbctl.80000644000000000000000000000355312223032460016270 0ustar rootroot.\" $Id$ .TH kamdbctl 8 05.02.2009 Kamailio "Kamailio" .SH NAME kamdbctl \- Kamailio database control tool .SH SYNOPSIS .B kamdbctl .BI command [ .BI parameters ] .SH DESCRIPTION .B kamdbctl is a script to maintain the database needed by some Kamalio modules. .SH Configuration Before you can use .B kamdbctl you have to select a database engine in the kamctlrc file. Just change the DBENGINE parameter in the respective section in kamctlrc. .TP Valid values are: MYSQL, PGSQL, ORACLE, DB_BERKELEY, DBTEXT. .TP The default is 'none'. .TP The username, password, database name and other settings can be configured by editing the kamctrl script. .SH COMMANDS .B backup Dumps the current database to a file. .TP .B copy Creates a new database from an existing one. .TP .B create Creates a new database with the specified name, or the default if no name is given. .TP .B drop Entirely deletes the database with the specified name, or the default if no name is given. .TP .B extra Adds the extra tables .TP .B migrate Migrates database tables from an old version to the actual version. .TP .B presence Adds the presence related tables. .TP .B reinit Entirely deletes and than re-creates tables. .TP .B restore Restores tables from a file. .SH NOTES Not all databases scripts support all commands. .SH FILES .PD 0 .I /etc/kamailio/.kamctlrc .br .I /usr/local/etc/kamailio/.kamctlrc .br .I ~/.kamctlrc .br .SH AUTHORS see .B /usr/share/doc/kamailio/AUTHORS .SH SEE ALSO .BR kamailio(8), .BR kamailio.cfg(5) .PP Full documentation on Kamailio is available at .I http://www.kamailio.org/. .PP Mailing lists: .nf users@lists.kamailio.org - Kamailio user community .nf devel@lists.kamailio.org - Kamailio development, new features and unstable version kamailio-4.0.4/utils/kamctl/db_sqlite/0000755000000000000000000000000012223032461016357 5ustar rootrootkamailio-4.0.4/utils/kamctl/db_sqlite/sca-create.sql0000644000000000000000000000152512223032460021111 0ustar rootrootINSERT INTO version (table_name, table_version) values ('sca_subscriptions','1'); CREATE TABLE sca_subscriptions ( id INTEGER PRIMARY KEY NOT NULL, subscriber VARCHAR(255) NOT NULL, aor VARCHAR(255) NOT NULL, event INTEGER DEFAULT 0 NOT NULL, expires INTEGER DEFAULT 0 NOT NULL, state INTEGER DEFAULT 0 NOT NULL, app_idx INTEGER DEFAULT 0 NOT NULL, call_id VARCHAR(255) NOT NULL, from_tag VARCHAR(64) NOT NULL, to_tag VARCHAR(64) NOT NULL, record_route TEXT, notify_cseq INTEGER NOT NULL, subscribe_cseq INTEGER NOT NULL, CONSTRAINT sca_subscriptions_sca_subscriptions_idx UNIQUE (subscriber, call_id, from_tag, to_tag) ); CREATE INDEX sca_subscriptions_sca_expires_idx ON sca_subscriptions (expires); CREATE INDEX sca_subscriptions_sca_subscribers_idx ON sca_subscriptions (subscriber, event); kamailio-4.0.4/utils/kamctl/db_sqlite/registrar-create.sql0000644000000000000000000000206112223032460022341 0ustar rootrootINSERT INTO version (table_name, table_version) values ('aliases','6'); CREATE TABLE aliases ( id INTEGER PRIMARY KEY NOT NULL, ruid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT NULL, contact VARCHAR(255) DEFAULT '' NOT NULL, received VARCHAR(128) DEFAULT NULL, path VARCHAR(128) DEFAULT NULL, expires TIMESTAMP WITHOUT TIME ZONE DEFAULT '2030-05-28 21:32:15' NOT NULL, q REAL DEFAULT 1.0 NOT NULL, callid VARCHAR(255) DEFAULT 'Default-Call-ID' NOT NULL, cseq INTEGER DEFAULT 1 NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, cflags INTEGER DEFAULT 0 NOT NULL, user_agent VARCHAR(255) DEFAULT '' NOT NULL, socket VARCHAR(64) DEFAULT NULL, methods INTEGER DEFAULT NULL, instance VARCHAR(255) DEFAULT NULL, reg_id INTEGER DEFAULT 0 NOT NULL, CONSTRAINT aliases_ruid_idx UNIQUE (ruid) ); CREATE INDEX aliases_alias_idx ON aliases (username, domain, contact); kamailio-4.0.4/utils/kamctl/db_sqlite/uri_db-create.sql0000644000000000000000000000064112223032460021605 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uri','1'); CREATE TABLE uri ( id INTEGER PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, uri_user VARCHAR(64) DEFAULT '' NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT uri_account_idx UNIQUE (username, domain, uri_user) ); kamailio-4.0.4/utils/kamctl/db_sqlite/purple-create.sql0000644000000000000000000000042012223032460021643 0ustar rootrootINSERT INTO version (table_name, table_version) values ('purplemap','1'); CREATE TABLE purplemap ( id INTEGER PRIMARY KEY NOT NULL, sip_user VARCHAR(128) NOT NULL, ext_user VARCHAR(128) NOT NULL, ext_prot VARCHAR(16) NOT NULL, ext_pass VARCHAR(64) ); kamailio-4.0.4/utils/kamctl/db_sqlite/avpops-create.sql0000644000000000000000000000122312223032460021646 0ustar rootrootINSERT INTO version (table_name, table_version) values ('usr_preferences','2'); CREATE TABLE usr_preferences ( id INTEGER PRIMARY KEY NOT NULL, uuid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(128) DEFAULT 0 NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, attribute VARCHAR(32) DEFAULT '' NOT NULL, type INTEGER DEFAULT 0 NOT NULL, value VARCHAR(128) DEFAULT '' NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL ); CREATE INDEX usr_preferences_ua_idx ON usr_preferences (uuid, attribute); CREATE INDEX usr_preferences_uda_idx ON usr_preferences (username, domain, attribute); kamailio-4.0.4/utils/kamctl/db_sqlite/usrloc-create.sql0000644000000000000000000000341412223032460021651 0ustar rootrootINSERT INTO version (table_name, table_version) values ('location','6'); CREATE TABLE location ( id INTEGER PRIMARY KEY NOT NULL, ruid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT NULL, contact VARCHAR(255) DEFAULT '' NOT NULL, received VARCHAR(128) DEFAULT NULL, path VARCHAR(512) DEFAULT NULL, expires TIMESTAMP WITHOUT TIME ZONE DEFAULT '2030-05-28 21:32:15' NOT NULL, q REAL DEFAULT 1.0 NOT NULL, callid VARCHAR(255) DEFAULT 'Default-Call-ID' NOT NULL, cseq INTEGER DEFAULT 1 NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, cflags INTEGER DEFAULT 0 NOT NULL, user_agent VARCHAR(255) DEFAULT '' NOT NULL, socket VARCHAR(64) DEFAULT NULL, methods INTEGER DEFAULT NULL, instance VARCHAR(255) DEFAULT NULL, reg_id INTEGER DEFAULT 0 NOT NULL, CONSTRAINT location_ruid_idx UNIQUE (ruid) ); CREATE INDEX location_account_contact_idx ON location (username, domain, contact); CREATE INDEX location_expires_idx ON location (expires); INSERT INTO version (table_name, table_version) values ('location_attrs','1'); CREATE TABLE location_attrs ( id INTEGER PRIMARY KEY NOT NULL, ruid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT NULL, aname VARCHAR(64) DEFAULT '' NOT NULL, atype INTEGER DEFAULT 0 NOT NULL, avalue VARCHAR(255) DEFAULT '' NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL ); CREATE INDEX location_attrs_account_record_idx ON location_attrs (username, domain, ruid); CREATE INDEX location_attrs_last_modified_idx ON location_attrs (last_modified); kamailio-4.0.4/utils/kamctl/db_sqlite/standard-create.sql0000644000000000000000000000025012223032460022135 0ustar rootrootCREATE TABLE version ( table_name VARCHAR(32) NOT NULL, table_version INTEGER DEFAULT 0 NOT NULL, CONSTRAINT version_table_name_idx UNIQUE (table_name) ); kamailio-4.0.4/utils/kamctl/db_sqlite/domainpolicy-create.sql0000644000000000000000000000063512223032460023033 0ustar rootrootINSERT INTO version (table_name, table_version) values ('domainpolicy','2'); CREATE TABLE domainpolicy ( id INTEGER PRIMARY KEY NOT NULL, rule VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, att VARCHAR(255), val VARCHAR(128), description VARCHAR(255) NOT NULL, CONSTRAINT domainpolicy_rav_idx UNIQUE (rule, att, val) ); CREATE INDEX domainpolicy_rule_idx ON domainpolicy (rule); kamailio-4.0.4/utils/kamctl/db_sqlite/carrierroute-create.sql0000644000000000000000000000276512223032460023060 0ustar rootrootINSERT INTO version (table_name, table_version) values ('carrierroute','3'); CREATE TABLE carrierroute ( id INTEGER PRIMARY KEY NOT NULL, carrier INTEGER DEFAULT 0 NOT NULL, domain INTEGER DEFAULT 0 NOT NULL, scan_prefix VARCHAR(64) DEFAULT '' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, mask INTEGER DEFAULT 0 NOT NULL, prob REAL DEFAULT 0 NOT NULL, strip INTEGER DEFAULT 0 NOT NULL, rewrite_host VARCHAR(128) DEFAULT '' NOT NULL, rewrite_prefix VARCHAR(64) DEFAULT '' NOT NULL, rewrite_suffix VARCHAR(64) DEFAULT '' NOT NULL, description VARCHAR(255) DEFAULT NULL ); INSERT INTO version (table_name, table_version) values ('carrierfailureroute','2'); CREATE TABLE carrierfailureroute ( id INTEGER PRIMARY KEY NOT NULL, carrier INTEGER DEFAULT 0 NOT NULL, domain INTEGER DEFAULT 0 NOT NULL, scan_prefix VARCHAR(64) DEFAULT '' NOT NULL, host_name VARCHAR(128) DEFAULT '' NOT NULL, reply_code VARCHAR(3) DEFAULT '' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, mask INTEGER DEFAULT 0 NOT NULL, next_domain INTEGER DEFAULT 0 NOT NULL, description VARCHAR(255) DEFAULT NULL ); INSERT INTO version (table_name, table_version) values ('carrier_name','1'); CREATE TABLE carrier_name ( id INTEGER PRIMARY KEY NOT NULL, carrier VARCHAR(64) DEFAULT NULL ); INSERT INTO version (table_name, table_version) values ('domain_name','1'); CREATE TABLE domain_name ( id INTEGER PRIMARY KEY NOT NULL, domain VARCHAR(64) DEFAULT NULL ); kamailio-4.0.4/utils/kamctl/db_sqlite/domain-create.sql0000644000000000000000000000137012223032460021610 0ustar rootrootINSERT INTO version (table_name, table_version) values ('domain','2'); CREATE TABLE domain ( id INTEGER PRIMARY KEY NOT NULL, domain VARCHAR(64) NOT NULL, did VARCHAR(64) DEFAULT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT domain_domain_idx UNIQUE (domain) ); INSERT INTO version (table_name, table_version) values ('domain_attrs','1'); CREATE TABLE domain_attrs ( id INTEGER PRIMARY KEY NOT NULL, did VARCHAR(64) NOT NULL, name VARCHAR(32) NOT NULL, type INTEGER NOT NULL, value VARCHAR(255) NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT domain_attrs_domain_attrs_idx UNIQUE (did, name, value) ); kamailio-4.0.4/utils/kamctl/db_sqlite/permissions-create.sql0000644000000000000000000000115112223032460022711 0ustar rootrootINSERT INTO version (table_name, table_version) values ('trusted','5'); CREATE TABLE trusted ( id INTEGER PRIMARY KEY NOT NULL, src_ip VARCHAR(50) NOT NULL, proto VARCHAR(4) NOT NULL, from_pattern VARCHAR(64) DEFAULT NULL, tag VARCHAR(64) ); CREATE INDEX trusted_peer_idx ON trusted (src_ip); INSERT INTO version (table_name, table_version) values ('address','6'); CREATE TABLE address ( id INTEGER PRIMARY KEY NOT NULL, grp INTEGER DEFAULT 1 NOT NULL, ip_addr VARCHAR(50) NOT NULL, mask INTEGER DEFAULT 32 NOT NULL, port SMALLINT DEFAULT 0 NOT NULL, tag VARCHAR(64) ); kamailio-4.0.4/utils/kamctl/db_sqlite/htable-create.sql0000644000000000000000000000053712223032460021604 0ustar rootrootINSERT INTO version (table_name, table_version) values ('htable','2'); CREATE TABLE htable ( id INTEGER PRIMARY KEY NOT NULL, key_name VARCHAR(64) DEFAULT '' NOT NULL, key_type INTEGER DEFAULT 0 NOT NULL, value_type INTEGER DEFAULT 0 NOT NULL, key_value VARCHAR(128) DEFAULT '' NOT NULL, expires INTEGER DEFAULT 0 NOT NULL ); kamailio-4.0.4/utils/kamctl/db_sqlite/matrix-create.sql0000644000000000000000000000035712223032460021651 0ustar rootrootINSERT INTO version (table_name, table_version) values ('matrix','1'); CREATE TABLE matrix ( first INTEGER NOT NULL, second SMALLINT NOT NULL, res INTEGER NOT NULL ); CREATE INDEX matrix_matrix_idx ON matrix (first, second); kamailio-4.0.4/utils/kamctl/db_sqlite/pdt-create.sql0000644000000000000000000000046012223032460021127 0ustar rootrootINSERT INTO version (table_name, table_version) values ('pdt','1'); CREATE TABLE pdt ( id INTEGER PRIMARY KEY NOT NULL, sdomain VARCHAR(128) NOT NULL, prefix VARCHAR(32) NOT NULL, domain VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT pdt_sdomain_prefix_idx UNIQUE (sdomain, prefix) ); kamailio-4.0.4/utils/kamctl/db_sqlite/uid_uri_db-create.sql0000644000000000000000000000160712223032460022451 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_uri','3'); CREATE TABLE uid_uri ( id INTEGER PRIMARY KEY NOT NULL, uid VARCHAR(64) NOT NULL, did VARCHAR(64) NOT NULL, username VARCHAR(64) NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, scheme VARCHAR(8) DEFAULT 'sip' NOT NULL ); CREATE INDEX uid_uri_uri_idx1 ON uid_uri (username, did, scheme); CREATE INDEX uid_uri_uri_uid ON uid_uri (uid); INSERT INTO version (table_name, table_version) values ('uid_uri_attrs','2'); CREATE TABLE uid_uri_attrs ( id INTEGER PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, did VARCHAR(64) NOT NULL, name VARCHAR(32) NOT NULL, value VARCHAR(128), type INTEGER DEFAULT 0 NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, scheme VARCHAR(8) DEFAULT 'sip' NOT NULL, CONSTRAINT uid_uri_attrs_uriattrs_idx UNIQUE (username, did, name, value, scheme) ); kamailio-4.0.4/utils/kamctl/db_sqlite/uid_gflags-create.sql0000644000000000000000000000054212223032460022445 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_global_attrs','1'); CREATE TABLE uid_global_attrs ( id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(32) NOT NULL, type INTEGER DEFAULT 0 NOT NULL, value VARCHAR(128), flags INTEGER DEFAULT 0 NOT NULL, CONSTRAINT uid_global_attrs_global_attrs_idx UNIQUE (name, value) ); kamailio-4.0.4/utils/kamctl/db_sqlite/drouting-create.sql0000644000000000000000000000245212223032460022176 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dr_gateways','3'); CREATE TABLE dr_gateways ( gwid INTEGER PRIMARY KEY NOT NULL, type INTEGER DEFAULT 0 NOT NULL, address VARCHAR(128) NOT NULL, strip INTEGER DEFAULT 0 NOT NULL, pri_prefix VARCHAR(64) DEFAULT NULL, attrs VARCHAR(255) DEFAULT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ); INSERT INTO version (table_name, table_version) values ('dr_rules','3'); CREATE TABLE dr_rules ( ruleid INTEGER PRIMARY KEY NOT NULL, groupid VARCHAR(255) NOT NULL, prefix VARCHAR(64) NOT NULL, timerec VARCHAR(255) NOT NULL, priority INTEGER DEFAULT 0 NOT NULL, routeid VARCHAR(64) NOT NULL, gwlist VARCHAR(255) NOT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ); INSERT INTO version (table_name, table_version) values ('dr_gw_lists','1'); CREATE TABLE dr_gw_lists ( id INTEGER PRIMARY KEY NOT NULL, gwlist VARCHAR(255) NOT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ); INSERT INTO version (table_name, table_version) values ('dr_groups','2'); CREATE TABLE dr_groups ( id INTEGER PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(128) DEFAULT '' NOT NULL, groupid INTEGER DEFAULT 0 NOT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ); kamailio-4.0.4/utils/kamctl/db_sqlite/msilo-create.sql0000644000000000000000000000130412223032460021461 0ustar rootrootINSERT INTO version (table_name, table_version) values ('silo','7'); CREATE TABLE silo ( id INTEGER PRIMARY KEY NOT NULL, src_addr VARCHAR(128) DEFAULT '' NOT NULL, dst_addr VARCHAR(128) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, inc_time INTEGER DEFAULT 0 NOT NULL, exp_time INTEGER DEFAULT 0 NOT NULL, snd_time INTEGER DEFAULT 0 NOT NULL, ctype VARCHAR(32) DEFAULT 'text/plain' NOT NULL, body BLOB DEFAULT '' NOT NULL, extra_hdrs TEXT DEFAULT '' NOT NULL, callid VARCHAR(128) DEFAULT '' NOT NULL, status INTEGER DEFAULT 0 NOT NULL ); CREATE INDEX silo_account_idx ON silo (username, domain); kamailio-4.0.4/utils/kamctl/db_sqlite/dialog-create.sql0000644000000000000000000000252212223032460021600 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dialog','7'); CREATE TABLE dialog ( id INTEGER PRIMARY KEY NOT NULL, hash_entry INTEGER NOT NULL, hash_id INTEGER NOT NULL, callid VARCHAR(255) NOT NULL, from_uri VARCHAR(128) NOT NULL, from_tag VARCHAR(64) NOT NULL, to_uri VARCHAR(128) NOT NULL, to_tag VARCHAR(64) NOT NULL, caller_cseq VARCHAR(20) NOT NULL, callee_cseq VARCHAR(20) NOT NULL, caller_route_set VARCHAR(512), callee_route_set VARCHAR(512), caller_contact VARCHAR(128) NOT NULL, callee_contact VARCHAR(128) NOT NULL, caller_sock VARCHAR(64) NOT NULL, callee_sock VARCHAR(64) NOT NULL, state INTEGER NOT NULL, start_time INTEGER NOT NULL, timeout INTEGER DEFAULT 0 NOT NULL, sflags INTEGER DEFAULT 0 NOT NULL, iflags INTEGER DEFAULT 0 NOT NULL, toroute_name VARCHAR(32), req_uri VARCHAR(128) NOT NULL, xdata VARCHAR(512) ); CREATE INDEX dialog_hash_idx ON dialog (hash_entry, hash_id); INSERT INTO version (table_name, table_version) values ('dialog_vars','1'); CREATE TABLE dialog_vars ( id INTEGER PRIMARY KEY NOT NULL, hash_entry INTEGER NOT NULL, hash_id INTEGER NOT NULL, dialog_key VARCHAR(128) NOT NULL, dialog_value VARCHAR(512) NOT NULL ); CREATE INDEX dialog_vars_hash_idx ON dialog_vars (hash_entry, hash_id); kamailio-4.0.4/utils/kamctl/db_sqlite/acc-create.sql0000644000000000000000000000177412223032461021100 0ustar rootrootINSERT INTO version (table_name, table_version) values ('acc','4'); CREATE TABLE acc ( id INTEGER PRIMARY KEY NOT NULL, method VARCHAR(16) DEFAULT '' NOT NULL, from_tag VARCHAR(64) DEFAULT '' NOT NULL, to_tag VARCHAR(64) DEFAULT '' NOT NULL, callid VARCHAR(255) DEFAULT '' NOT NULL, sip_code VARCHAR(3) DEFAULT '' NOT NULL, sip_reason VARCHAR(32) DEFAULT '' NOT NULL, time TIMESTAMP WITHOUT TIME ZONE NOT NULL ); CREATE INDEX acc_callid_idx ON acc (callid); INSERT INTO version (table_name, table_version) values ('missed_calls','3'); CREATE TABLE missed_calls ( id INTEGER PRIMARY KEY NOT NULL, method VARCHAR(16) DEFAULT '' NOT NULL, from_tag VARCHAR(64) DEFAULT '' NOT NULL, to_tag VARCHAR(64) DEFAULT '' NOT NULL, callid VARCHAR(255) DEFAULT '' NOT NULL, sip_code VARCHAR(3) DEFAULT '' NOT NULL, sip_reason VARCHAR(32) DEFAULT '' NOT NULL, time TIMESTAMP WITHOUT TIME ZONE NOT NULL ); CREATE INDEX missed_calls_callid_idx ON missed_calls (callid); kamailio-4.0.4/utils/kamctl/db_sqlite/pipelimit-create.sql0000644000000000000000000000041312223032460022332 0ustar rootrootINSERT INTO version (table_name, table_version) values ('pl_pipes','1'); CREATE TABLE pl_pipes ( id INTEGER PRIMARY KEY NOT NULL, pipeid VARCHAR(64) DEFAULT '' NOT NULL, algorithm VARCHAR(32) DEFAULT '' NOT NULL, plimit INTEGER DEFAULT 0 NOT NULL ); kamailio-4.0.4/utils/kamctl/db_sqlite/uid_auth_db-create.sql0000644000000000000000000000132712223032460022612 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_credentials','7'); CREATE TABLE uid_credentials ( id INTEGER PRIMARY KEY NOT NULL, auth_username VARCHAR(64) NOT NULL, did VARCHAR(64) DEFAULT '_default' NOT NULL, realm VARCHAR(64) NOT NULL, password VARCHAR(28) DEFAULT '' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, ha1 VARCHAR(32) NOT NULL, ha1b VARCHAR(32) DEFAULT '' NOT NULL, uid VARCHAR(64) NOT NULL ); CREATE INDEX uid_credentials_cred_idx ON uid_credentials (auth_username, did); CREATE INDEX uid_credentials_uid ON uid_credentials (uid); CREATE INDEX uid_credentials_did_idx ON uid_credentials (did); CREATE INDEX uid_credentials_realm_idx ON uid_credentials (realm); kamailio-4.0.4/utils/kamctl/db_sqlite/mtree-create.sql0000644000000000000000000000115312223032460021454 0ustar rootrootINSERT INTO version (table_name, table_version) values ('mtree','1'); CREATE TABLE mtree ( id INTEGER PRIMARY KEY NOT NULL, tprefix VARCHAR(32) DEFAULT '' NOT NULL, tvalue VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT mtree_tprefix_idx UNIQUE (tprefix) ); INSERT INTO version (table_name, table_version) values ('mtrees','2'); CREATE TABLE mtrees ( id INTEGER PRIMARY KEY NOT NULL, tname VARCHAR(128) DEFAULT '' NOT NULL, tprefix VARCHAR(32) DEFAULT '' NOT NULL, tvalue VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT mtrees_tname_tprefix_tvalue_idx UNIQUE (tname, tprefix, tvalue) ); kamailio-4.0.4/utils/kamctl/db_sqlite/imc-create.sql0000644000000000000000000000120012223032460021101 0ustar rootrootINSERT INTO version (table_name, table_version) values ('imc_rooms','1'); CREATE TABLE imc_rooms ( id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, flag INTEGER NOT NULL, CONSTRAINT imc_rooms_name_domain_idx UNIQUE (name, domain) ); INSERT INTO version (table_name, table_version) values ('imc_members','1'); CREATE TABLE imc_members ( id INTEGER PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, room VARCHAR(64) NOT NULL, flag INTEGER NOT NULL, CONSTRAINT imc_members_account_room_idx UNIQUE (username, domain, room) ); kamailio-4.0.4/utils/kamctl/db_sqlite/userblacklist-create.sql0000644000000000000000000000140712223032460023211 0ustar rootrootINSERT INTO version (table_name, table_version) values ('userblacklist','1'); CREATE TABLE userblacklist ( id INTEGER PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, prefix VARCHAR(64) DEFAULT '' NOT NULL, whitelist SMALLINT DEFAULT 0 NOT NULL ); CREATE INDEX userblacklist_userblacklist_idx ON userblacklist (username, domain, prefix); INSERT INTO version (table_name, table_version) values ('globalblacklist','1'); CREATE TABLE globalblacklist ( id INTEGER PRIMARY KEY NOT NULL, prefix VARCHAR(64) DEFAULT '' NOT NULL, whitelist SMALLINT DEFAULT 0 NOT NULL, description VARCHAR(255) DEFAULT NULL ); CREATE INDEX globalblacklist_globalblacklist_idx ON globalblacklist (prefix); kamailio-4.0.4/utils/kamctl/db_sqlite/alias_db-create.sql0000644000000000000000000000073112223032460022077 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dbaliases','1'); CREATE TABLE dbaliases ( id INTEGER PRIMARY KEY NOT NULL, alias_username VARCHAR(64) DEFAULT '' NOT NULL, alias_domain VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, CONSTRAINT dbaliases_alias_idx UNIQUE (alias_username, alias_domain) ); CREATE INDEX dbaliases_target_idx ON dbaliases (username, domain); kamailio-4.0.4/utils/kamctl/db_sqlite/uac-create.sql0000644000000000000000000000121212223032460021104 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uacreg','1'); CREATE TABLE uacreg ( id INTEGER PRIMARY KEY NOT NULL, l_uuid VARCHAR(64) DEFAULT '' NOT NULL, l_username VARCHAR(64) DEFAULT '' NOT NULL, l_domain VARCHAR(128) DEFAULT '' NOT NULL, r_username VARCHAR(64) DEFAULT '' NOT NULL, r_domain VARCHAR(128) DEFAULT '' NOT NULL, realm VARCHAR(64) DEFAULT '' NOT NULL, auth_username VARCHAR(64) DEFAULT '' NOT NULL, auth_password VARCHAR(64) DEFAULT '' NOT NULL, auth_proxy VARCHAR(64) DEFAULT '' NOT NULL, expires INTEGER DEFAULT 0 NOT NULL, CONSTRAINT uacreg_l_uuid_idx UNIQUE (l_uuid) ); kamailio-4.0.4/utils/kamctl/db_sqlite/dispatcher-create.sql0000644000000000000000000000062112223032460022465 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dispatcher','4'); CREATE TABLE dispatcher ( id INTEGER PRIMARY KEY NOT NULL, setid INTEGER DEFAULT 0 NOT NULL, destination VARCHAR(192) DEFAULT '' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, priority INTEGER DEFAULT 0 NOT NULL, attrs VARCHAR(128) DEFAULT '' NOT NULL, description VARCHAR(64) DEFAULT '' NOT NULL ); kamailio-4.0.4/utils/kamctl/db_sqlite/speeddial-create.sql0000644000000000000000000000113012223032460022265 0ustar rootrootINSERT INTO version (table_name, table_version) values ('speed_dial','2'); CREATE TABLE speed_dial ( id INTEGER PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, sd_username VARCHAR(64) DEFAULT '' NOT NULL, sd_domain VARCHAR(64) DEFAULT '' NOT NULL, new_uri VARCHAR(128) DEFAULT '' NOT NULL, fname VARCHAR(64) DEFAULT '' NOT NULL, lname VARCHAR(64) DEFAULT '' NOT NULL, description VARCHAR(64) DEFAULT '' NOT NULL, CONSTRAINT speed_dial_speed_dial_idx UNIQUE (username, domain, sd_domain, sd_username) ); kamailio-4.0.4/utils/kamctl/db_sqlite/siptrace-create.sql0000644000000000000000000000156712223032460022163 0ustar rootrootINSERT INTO version (table_name, table_version) values ('sip_trace','3'); CREATE TABLE sip_trace ( id INTEGER PRIMARY KEY NOT NULL, time_stamp TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, time_us INTEGER DEFAULT 0 NOT NULL, callid VARCHAR(255) DEFAULT '' NOT NULL, traced_user VARCHAR(128) DEFAULT '' NOT NULL, msg TEXT NOT NULL, method VARCHAR(50) DEFAULT '' NOT NULL, status VARCHAR(128) DEFAULT '' NOT NULL, fromip VARCHAR(50) DEFAULT '' NOT NULL, toip VARCHAR(50) DEFAULT '' NOT NULL, fromtag VARCHAR(64) DEFAULT '' NOT NULL, direction VARCHAR(4) DEFAULT '' NOT NULL ); CREATE INDEX sip_trace_traced_user_idx ON sip_trace (traced_user); CREATE INDEX sip_trace_date_idx ON sip_trace (time_stamp); CREATE INDEX sip_trace_fromip_idx ON sip_trace (fromip); CREATE INDEX sip_trace_callid_idx ON sip_trace (callid); kamailio-4.0.4/utils/kamctl/db_sqlite/dialplan-create.sql0000644000000000000000000000060712223032460022127 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dialplan','1'); CREATE TABLE dialplan ( id INTEGER PRIMARY KEY NOT NULL, dpid INTEGER NOT NULL, pr INTEGER NOT NULL, match_op INTEGER NOT NULL, match_exp VARCHAR(64) NOT NULL, match_len INTEGER NOT NULL, subst_exp VARCHAR(64) NOT NULL, repl_exp VARCHAR(32) NOT NULL, attrs VARCHAR(32) NOT NULL ); kamailio-4.0.4/utils/kamctl/db_sqlite/group-create.sql0000644000000000000000000000125612223032460021500 0ustar rootrootINSERT INTO version (table_name, table_version) values ('grp','2'); CREATE TABLE grp ( id INTEGER PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, grp VARCHAR(64) DEFAULT '' NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT grp_account_group_idx UNIQUE (username, domain, grp) ); INSERT INTO version (table_name, table_version) values ('re_grp','1'); CREATE TABLE re_grp ( id INTEGER PRIMARY KEY NOT NULL, reg_exp VARCHAR(128) DEFAULT '' NOT NULL, group_id INTEGER DEFAULT 0 NOT NULL ); CREATE INDEX re_grp_group_idx ON re_grp (group_id); kamailio-4.0.4/utils/kamctl/db_sqlite/lcr-create.sql0000644000000000000000000000265412223032460021127 0ustar rootrootINSERT INTO version (table_name, table_version) values ('lcr_gw','3'); CREATE TABLE lcr_gw ( id INTEGER PRIMARY KEY NOT NULL, lcr_id SMALLINT NOT NULL, gw_name VARCHAR(128), ip_addr VARCHAR(50), hostname VARCHAR(64), port SMALLINT, params VARCHAR(64), uri_scheme SMALLINT, transport SMALLINT, strip SMALLINT, prefix VARCHAR(16) DEFAULT NULL, tag VARCHAR(64) DEFAULT NULL, flags INTEGER DEFAULT 0 NOT NULL, defunct INTEGER DEFAULT NULL ); CREATE INDEX lcr_gw_lcr_id_idx ON lcr_gw (lcr_id); INSERT INTO version (table_name, table_version) values ('lcr_rule_target','1'); CREATE TABLE lcr_rule_target ( id INTEGER PRIMARY KEY NOT NULL, lcr_id SMALLINT NOT NULL, rule_id INTEGER NOT NULL, gw_id INTEGER NOT NULL, priority SMALLINT NOT NULL, weight INTEGER DEFAULT 1 NOT NULL, CONSTRAINT lcr_rule_target_rule_id_gw_id_idx UNIQUE (rule_id, gw_id) ); CREATE INDEX lcr_rule_target_lcr_id_idx ON lcr_rule_target (lcr_id); INSERT INTO version (table_name, table_version) values ('lcr_rule','2'); CREATE TABLE lcr_rule ( id INTEGER PRIMARY KEY NOT NULL, lcr_id SMALLINT NOT NULL, prefix VARCHAR(16) DEFAULT NULL, from_uri VARCHAR(64) DEFAULT NULL, request_uri VARCHAR(64) DEFAULT NULL, stopper INTEGER DEFAULT 0 NOT NULL, enabled INTEGER DEFAULT 1 NOT NULL, CONSTRAINT lcr_rule_lcr_id_prefix_from_uri_idx UNIQUE (lcr_id, prefix, from_uri) ); kamailio-4.0.4/utils/kamctl/db_sqlite/auth_db-create.sql0000644000000000000000000000107712223032460021753 0ustar rootrootINSERT INTO version (table_name, table_version) values ('subscriber','6'); CREATE TABLE subscriber ( id INTEGER PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, password VARCHAR(25) DEFAULT '' NOT NULL, email_address VARCHAR(64) DEFAULT '' NOT NULL, ha1 VARCHAR(64) DEFAULT '' NOT NULL, ha1b VARCHAR(64) DEFAULT '' NOT NULL, rpid VARCHAR(64) DEFAULT NULL, CONSTRAINT subscriber_account_idx UNIQUE (username, domain) ); CREATE INDEX subscriber_username_idx ON subscriber (username); kamailio-4.0.4/utils/kamctl/db_sqlite/presence-create.sql0000644000000000000000000001020612223032460022143 0ustar rootrootINSERT INTO version (table_name, table_version) values ('presentity','3'); CREATE TABLE presentity ( id INTEGER PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, event VARCHAR(64) NOT NULL, etag VARCHAR(64) NOT NULL, expires INTEGER NOT NULL, received_time INTEGER NOT NULL, body BLOB NOT NULL, sender VARCHAR(128) NOT NULL, CONSTRAINT presentity_presentity_idx UNIQUE (username, domain, event, etag) ); CREATE INDEX presentity_presentity_expires ON presentity (expires); CREATE INDEX presentity_account_idx ON presentity (username, domain, event); INSERT INTO version (table_name, table_version) values ('active_watchers','11'); CREATE TABLE active_watchers ( id INTEGER PRIMARY KEY NOT NULL, presentity_uri VARCHAR(128) NOT NULL, watcher_username VARCHAR(64) NOT NULL, watcher_domain VARCHAR(64) NOT NULL, to_user VARCHAR(64) NOT NULL, to_domain VARCHAR(64) NOT NULL, event VARCHAR(64) DEFAULT 'presence' NOT NULL, event_id VARCHAR(64), to_tag VARCHAR(64) NOT NULL, from_tag VARCHAR(64) NOT NULL, callid VARCHAR(255) NOT NULL, local_cseq INTEGER NOT NULL, remote_cseq INTEGER NOT NULL, contact VARCHAR(128) NOT NULL, record_route TEXT, expires INTEGER NOT NULL, status INTEGER DEFAULT 2 NOT NULL, reason VARCHAR(64) NOT NULL, version INTEGER DEFAULT 0 NOT NULL, socket_info VARCHAR(64) NOT NULL, local_contact VARCHAR(128) NOT NULL, from_user VARCHAR(64) NOT NULL, from_domain VARCHAR(64) NOT NULL, updated INTEGER NOT NULL, updated_winfo INTEGER NOT NULL, CONSTRAINT active_watchers_active_watchers_idx UNIQUE (callid, to_tag, from_tag) ); CREATE INDEX active_watchers_active_watchers_expires ON active_watchers (expires); CREATE INDEX active_watchers_active_watchers_pres ON active_watchers (presentity_uri, event); CREATE INDEX active_watchers_updated_idx ON active_watchers (updated); CREATE INDEX active_watchers_updated_winfo_idx ON active_watchers (updated_winfo, presentity_uri); INSERT INTO version (table_name, table_version) values ('watchers','3'); CREATE TABLE watchers ( id INTEGER PRIMARY KEY NOT NULL, presentity_uri VARCHAR(128) NOT NULL, watcher_username VARCHAR(64) NOT NULL, watcher_domain VARCHAR(64) NOT NULL, event VARCHAR(64) DEFAULT 'presence' NOT NULL, status INTEGER NOT NULL, reason VARCHAR(64), inserted_time INTEGER NOT NULL, CONSTRAINT watchers_watcher_idx UNIQUE (presentity_uri, watcher_username, watcher_domain, event) ); INSERT INTO version (table_name, table_version) values ('xcap','4'); CREATE TABLE xcap ( id INTEGER PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, doc BLOB NOT NULL, doc_type INTEGER NOT NULL, etag VARCHAR(64) NOT NULL, source INTEGER NOT NULL, doc_uri VARCHAR(255) NOT NULL, port INTEGER NOT NULL, CONSTRAINT xcap_doc_uri_idx UNIQUE (doc_uri) ); CREATE INDEX xcap_account_doc_type_idx ON xcap (username, domain, doc_type); CREATE INDEX xcap_account_doc_type_uri_idx ON xcap (username, domain, doc_type, doc_uri); CREATE INDEX xcap_account_doc_uri_idx ON xcap (username, domain, doc_uri); INSERT INTO version (table_name, table_version) values ('pua','7'); CREATE TABLE pua ( id INTEGER PRIMARY KEY NOT NULL, pres_uri VARCHAR(128) NOT NULL, pres_id VARCHAR(255) NOT NULL, event INTEGER NOT NULL, expires INTEGER NOT NULL, desired_expires INTEGER NOT NULL, flag INTEGER NOT NULL, etag VARCHAR(64) NOT NULL, tuple_id VARCHAR(64), watcher_uri VARCHAR(128) NOT NULL, call_id VARCHAR(255) NOT NULL, to_tag VARCHAR(64) NOT NULL, from_tag VARCHAR(64) NOT NULL, cseq INTEGER NOT NULL, record_route TEXT, contact VARCHAR(128) NOT NULL, remote_contact VARCHAR(128) NOT NULL, version INTEGER NOT NULL, extra_headers TEXT NOT NULL, CONSTRAINT pua_pua_idx UNIQUE (etag, tuple_id, call_id, from_tag) ); CREATE INDEX pua_expires_idx ON pua (expires); CREATE INDEX pua_dialog1_idx ON pua (pres_id, pres_uri); CREATE INDEX pua_dialog2_idx ON pua (call_id, from_tag); CREATE INDEX pua_record_idx ON pua (pres_id); kamailio-4.0.4/utils/kamctl/db_sqlite/uid_avp_db-create.sql0000644000000000000000000000057412223032460022442 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_user_attrs','3'); CREATE TABLE uid_user_attrs ( id INTEGER PRIMARY KEY NOT NULL, uid VARCHAR(64) NOT NULL, name VARCHAR(32) NOT NULL, value VARCHAR(128), type INTEGER DEFAULT 0 NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, CONSTRAINT uid_user_attrs_userattrs_idx UNIQUE (uid, name, value) ); kamailio-4.0.4/utils/kamctl/db_sqlite/rls-create.sql0000644000000000000000000000373712223032460021152 0ustar rootrootINSERT INTO version (table_name, table_version) values ('rls_presentity','1'); CREATE TABLE rls_presentity ( id INTEGER PRIMARY KEY NOT NULL, rlsubs_did VARCHAR(255) NOT NULL, resource_uri VARCHAR(128) NOT NULL, content_type VARCHAR(255) NOT NULL, presence_state BLOB NOT NULL, expires INTEGER NOT NULL, updated INTEGER NOT NULL, auth_state INTEGER NOT NULL, reason VARCHAR(64) NOT NULL, CONSTRAINT rls_presentity_rls_presentity_idx UNIQUE (rlsubs_did, resource_uri) ); CREATE INDEX rls_presentity_rlsubs_idx ON rls_presentity (rlsubs_did); CREATE INDEX rls_presentity_updated_idx ON rls_presentity (updated); CREATE INDEX rls_presentity_expires_idx ON rls_presentity (expires); INSERT INTO version (table_name, table_version) values ('rls_watchers','3'); CREATE TABLE rls_watchers ( id INTEGER PRIMARY KEY NOT NULL, presentity_uri VARCHAR(128) NOT NULL, to_user VARCHAR(64) NOT NULL, to_domain VARCHAR(64) NOT NULL, watcher_username VARCHAR(64) NOT NULL, watcher_domain VARCHAR(64) NOT NULL, event VARCHAR(64) DEFAULT 'presence' NOT NULL, event_id VARCHAR(64), to_tag VARCHAR(64) NOT NULL, from_tag VARCHAR(64) NOT NULL, callid VARCHAR(255) NOT NULL, local_cseq INTEGER NOT NULL, remote_cseq INTEGER NOT NULL, contact VARCHAR(128) NOT NULL, record_route TEXT, expires INTEGER NOT NULL, status INTEGER DEFAULT 2 NOT NULL, reason VARCHAR(64) NOT NULL, version INTEGER DEFAULT 0 NOT NULL, socket_info VARCHAR(64) NOT NULL, local_contact VARCHAR(128) NOT NULL, from_user VARCHAR(64) NOT NULL, from_domain VARCHAR(64) NOT NULL, updated INTEGER NOT NULL, CONSTRAINT rls_watchers_rls_watcher_idx UNIQUE (callid, to_tag, from_tag) ); CREATE INDEX rls_watchers_rls_watchers_update ON rls_watchers (watcher_username, watcher_domain, event); CREATE INDEX rls_watchers_rls_watchers_expires ON rls_watchers (expires); CREATE INDEX rls_watchers_updated_idx ON rls_watchers (updated); kamailio-4.0.4/utils/kamctl/db_sqlite/uid_domain-create.sql0000644000000000000000000000144512223032460022454 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_domain','2'); CREATE TABLE uid_domain ( id INTEGER PRIMARY KEY NOT NULL, did VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, CONSTRAINT uid_domain_domain_idx UNIQUE (domain) ); CREATE INDEX uid_domain_did_idx ON uid_domain (did); INSERT INTO version (table_name, table_version) values ('uid_domain_attrs','1'); CREATE TABLE uid_domain_attrs ( id INTEGER PRIMARY KEY NOT NULL, did VARCHAR(64), name VARCHAR(32) NOT NULL, type INTEGER DEFAULT 0 NOT NULL, value VARCHAR(128), flags INTEGER DEFAULT 0 NOT NULL, CONSTRAINT uid_domain_attrs_domain_attr_idx UNIQUE (did, name, value) ); CREATE INDEX uid_domain_attrs_domain_did ON uid_domain_attrs (did, flags); kamailio-4.0.4/utils/kamctl/db_sqlite/cpl-create.sql0000644000000000000000000000045412223032460021121 0ustar rootrootINSERT INTO version (table_name, table_version) values ('cpl','1'); CREATE TABLE cpl ( id INTEGER PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, cpl_xml TEXT, cpl_bin TEXT, CONSTRAINT cpl_account_idx UNIQUE (username, domain) ); kamailio-4.0.4/utils/kamctl/kamdbctl.oracle0000644000000000000000000001707112223032460017366 0ustar rootroot#!/bin/sh # $Id$ # # Script for adding and dropping Kamailio Oracle tables # # History: ##In you not have 'AS SYSDBA' access to database connect, comment next string ##and Oracle administrator must create DBROOTUSER ##(see scripts/oracle/admin/_create_as_sys.tmpl) DBSYSUSER="sys" # path to the database schemas DATA_DIR="/usr/local/share/kamailio" if [ -d "$DATA_DIR/oracle" ] ; then DB_SCHEMA="$DATA_DIR/oracle" else DB_SCHEMA="./oracle" fi ##### ----------------------------------------------- ##### ### load ORACLE SQL base # if [ -f "$MYLIBDIR/kamdbfunc.oracle" ]; then . "$MYLIBDIR/kamdbfunc.oracle" else echo "Cannot load ORACLE core functions '$MYLIBDIR/kamdbfunc.oracle' - exiting ..." exit -1 fi if [ -z "$SQLPLUS" ] ; then SQLPLUS=`which sqlplus 2>/dev/null` if [ -z "$SQLPLUS" ]; then merr "'sqlplus' tool not found: set SQLPLUS variable to correct tool path" exit 1 fi export SQLPLUS fi if [ -z "$KAMAILIO_ORASEL" ] ; then KAMAILIO_ORASEL=`which kamailio_orasel 2>/dev/null` if [ -n "$SQLPLUS" ]; then export KAMAILIO_ORASEL fi fi ################################################################# ORALOG=/tmp/opensrdbctl.log if [ -z "$SED" ]; then SED="sed" fi SED_ROOTUSER="$SED -e s/%DBROOTUSER%/$DBROOTUSER/g" SED_USERS="$SED_ROOTUSER;s/%DBROUSER%/$DBROUSER/g;s/%DBRWUSER%/$DBRWUSER/g" ################################################################# # config vars ################################################################# # 'db'-privileges (scheme) Oracle user if [ -z "$DBROOTUSER" ]; then merr "scheme owner (pivileged user) 'DBROOTUSER' must be defined." exit 1 fi ################################################################# oracle_root_cmd() { prompt_oracle_pw root SUFF="" if [ -n "$1" ]; then SUFF="@$1" fi ORACLE_ROOT_CMD="$SQLPLUS -S -L -R 3 $DBROOTUSER/${DBROOTPW}$SUFF" export ORACLE_ROOT_CMD } kamailio_drop() # pars: { if [ $# -ne 1 ] ; then merr "kamailio_drop function takes one params" exit 1 fi oracle_root_cmd $1 echo "DROP USER $DBROUSER CASCADE; DROP USER $DBRWUSER CASCADE;" | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then mwarn "Could not drop $DBRWUSER or $DBROUSER users, try to continue.." else minfo "Database user deleted" fi $SED_ROOTUSER $DB_SCHEMA/inc/_dropsch.tmpl | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Dropping scheme for '$DBROOTUSER' in database '$1' failed!" exit 1 fi if [ -n "$DBSYSUSER" ]; then get_answer "ask" "Remove user '$DBROOTUSER' (complete remove scheme)? (y/n): " if [ "$ANSWER" = "y" ]; then prompt_oracle_pw sys SUFF="" if [ -n "$1" ]; then SUFF="@$1" fi SYSCMD="$SQLPLUS -S -L -R 3 $DBSYSUSER/${DBSYSPW}$SUFF AS SYSDBA" echo "DROP USER $DBROOTUSER CASCADE;" | $SYSCMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Dropping scheme in database '$1' failed!" exit 1 fi fi fi minfo "Scheme '$DBROOTUSER' in database '$1' dropped" } #kamailio_drop kamailio_create() # pars: { if [ $# -ne 1 ] ; then merr "kamailio_create function takes one param" exit 1 fi minfo "creating scheme for '$DBROOTUSER' in database '$1' ..." if [ -n "$DBSYSUSER" ]; then get_answer "ask" "Create user '$DBROOTUSER' (is new scheme)? (y/n): " if [ "$ANSWER" = "y" ]; then prompt_oracle_pw sys prompt_oracle_pw root SUFF="" if [ -n "$1" ]; then SUFF="@$1" fi SYSCMD="$SQLPLUS -S -L -R 3 $DBSYSUSER/${DBSYSPW}$SUFF AS SYSDBA" echo "create user $DBROOTUSER identified by $DBROOTPW default tablespace DATA temporary tablespace TEMP profile DEFAULT;" | $SYSCMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then mwarn "Create privileged user in database failed, perhaps they allready exist? Try to continue.." fi $SED_ROOTUSER $DB_SCHEMA/inc/_grantroot.tmpl | $SYSCMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Creating scheme in database '$1' failed!" exit 1 fi fi fi oracle_root_cmd $1 $SED_ROOTUSER $DB_SCHEMA/inc/_createsch.tmpl | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Creating scheme for '$DBROOTUSER' in database '$1' failed!" exit 1 fi cat $DB_SCHEMA/inc/_create_compat.sql | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Creating compatibility functions for '$DBROOTUSER' in database '$1' failed!" exit 1 fi prompt_oracle_pw rw prompt_oracle_pw ro echo "create user $DBROUSER identified by $DBROPW default tablespace DATA temporary tablespace TEMP profile DEFAULT; grant connect to $DBROUSER; create user $DBRWUSER identified by $DBRWPW default tablespace DATA temporary tablespace TEMP profile DEFAULT; grant connect to $DBRWUSER;" | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then mwarn "Create user in database scheme failed, perhaps they allready exist? Try to continue.." fi $SED_USERS $DB_SCHEMA/inc/_grantfunc.tmpl | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Make compatibility functions for users failed!" exit 1 fi for TABLE in $STANDARD_MODULES; do mdbg "Creating core table: $TABLE" cat $DB_SCHEMA/$TABLE-create.sql | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Creating core tables failed!" exit 1 fi done if [ -e $DB_SCHEMA/extensions-create.sql ]; then minfo "Creating custom extensions tables" cat $DB_SCHEMA/extensions-create.sql | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Creating custom extensions tables failed!" exit 1 fi fi minfo "Core Kamailio tables succesfully created." get_answer $INSTALL_PRESENCE_TABLES "Install presence related tables? (y/n): " if [ "$ANSWER" = "y" ]; then presence_create $1 fi get_answer $INSTALL_EXTRA_TABLES "Install tables for $EXTRA_MODULES? (y/n): " if [ "$ANSWER" = "y" ]; then extra_create $1 fi } # kamailio_create presence_create() # pars: { if [ $# -ne 1 ] ; then merr "presence_create function takes one param" exit 1 fi minfo "creating presence tables..." oracle_root_cmd $1 cat $DB_SCHEMA/presence-create.sql | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Failed to create presence tables!" exit 1 fi cat $DB_SCHEMA/rls-create.sql | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Failed to create rls-presence tables!" exit 1 fi minfo "Presence tables succesfully created." } extra_create() # pars: { if [ $# -ne 1 ] ; then merr "extra_create function takes one param" exit 1 fi minfo "creating extra tables..." oracle_root_cmd $1 for TABLE in $EXTRA_MODULES; do mdbg "Creating extra table: $TABLE" cat $DB_SCHEMA/$TABLE-create.sql | $ORACLE_ROOT_CMD >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then merr "Creating extra tables failed!" exit 1 fi done minfo "Extra tables succesfully created." } oracle_dump() { if [ $# -ne 2 ] ; then merr "oracle_dump function takes two params" exit 1 fi prompt_oracle_pw rw if [ -n "$1" ]; then SUFF="@$1" fi if [ -n "$KAMAILIO_ORASEL" ]; then $KAMAILIO_ORASEL ${DBROUSER}/${DBROPW}$SUFF -BLNe \ "select * from table(dump_tables('$DBROOTUSER'));" >$2 else echo "set feed 0 lin 8000 pages 0 select * from table(dump_tables('$DBROOTUSER'));" | \ $SQLPLUS -S -L -R 3 ${DBROUSER}/${DBROPW}$SUFF >$2 fi return $? } oracle_restore() { if [ $# -ne 2 ] ; then merr "oracle_restore function takes two params" exit 1 fi prompt_oracle_pw rw SUFF="" if [ -n "$1" ]; then SUFF="@$1" fi cat "$2" | $SQLPLUS -S -L -R 3 ${DBRWUSER}/${DBRWPW}$SUFF >$ORALOG if [ $? -ne 0 ] || check_oracle_log ; then return 1 fi return 0 } kamailio-4.0.4/utils/kamctl/kamctl.fifo0000644000000000000000000000725712223032461016544 0ustar rootroot# # $Id$ # # control tool for maintaining Kamailio # #=================================================================== ##### ----------------------------------------------- ##### ### FIFO specific variables and functions # ##### ----------------------------------------------- ##### ### load CTL base # if [ -f "$MYLIBDIR/kamctl.ctlbase" ]; then . "$MYLIBDIR/kamctl.ctlbase" else mwarn "Cannot load CTL core functions '$MYLIBDIR/kamctl.ctlbase' ..." # exit -1 fi # ##### ----------------------------------------------- ##### ### parameters # if [ -z "$OSER_FIFO" ]; then OSER_FIFO=/tmp/kamailio_fifo fi # ##### ----------------------------------------------- ##### ### functions # usage_fifo() { echo mecho " -- command 'mi' - send raw MI commands" echo cat < $OSER_FIFO # wait for the reader to complete wait rm $path mdbg "FIFO command was:\n$CMD" } CTLCMD=fifo_cmd fifo_kamailio_monitor() { name=kamailio_receiver_$$ path=$CHROOT_DIR/tmp/$name if [ ! -w $OSER_FIFO ]; then merr "Error opening Kamailio's FIFO $OSER_FIFO" merr "Make sure you have the line 'modparam(\"mi_fifo\", \"fifo_name\", \"$OSER_FIFO\")' in your config" merr "and also have loaded the mi_fifo module." exit 1 fi if ! test -p $path; then mkfifo $path if [ $? -ne 0 ] ; then merr "monitor - error opening read fifo $path" exit 1 fi chmod a+w $path fi trap "rm $path; clear; echo monitor ^C-ed; exit 1" 2 attempt=0 if [ "$2" = "" ]; then loops=-1; else loops=$2; fi clear while [ $loops -ne $attempt ] ; do attempt=`$EXPR $attempt + 1` #clear tput clear # print_stats $name $path $attempt mecho "[cycle #: $attempt; if constant make sure server lives]" cat < $path | filter_fl & cat > $OSER_FIFO < $OSER_FIFO << EOF :uptime:$name EOF wait echo mecho "Transaction Statistics: " cat < $path | filter_fl & cat > $OSER_FIFO < $OSER_FIFO < $OSER_FIFO <= 7.3" exit 1 fi fi CMD="psql -q -h $DBHOST -U $DBROOTUSER " DUMP_CMD="pg_dump -h $DBHOST -U $DBROOTUSER -c" ################################################################# # execute sql command with optional db name sql_query() { if [ $# -gt 1 ] ; then if [ -n "$1" ]; then DB="$1" else DB="" fi shift $CMD -d $DB -c "$@" else $CMD "$@" fi } kamailio_drop() # pars: { if [ $# -ne 1 ] ; then merr "kamailio_drop function takes two params" exit 1 fi sql_query "template1" "drop database \"$1\";" if [ $? -ne 0 ] ; then merr "Dropping database $1 failed!" exit 1 fi # postgresql users are not dropped automatically sql_query "template1" "drop user \"$DBRWUSER\"; drop user \"$DBROUSER\";" if [ $? -ne 0 ] ; then mwarn "Could not drop $DBRWUSER or $DBROUSER users, try to continue.." else minfo "Database user deleted" fi minfo "Database $1 dropped" } #kamailio_drop kamailio_create () # pars: { if [ $# -ne 1 ] ; then merr "kamailio_create function takes one param" exit 1 fi minfo "creating database $1 ..." sql_query "template1" "create database \"$1\";" if [ $? -ne 0 ] ; then merr "Creating database failed!" exit 1 fi sql_query "$1" "CREATE FUNCTION "concat" (text,text) RETURNS text AS 'SELECT \$1 || \$2;' LANGUAGE 'sql'; CREATE FUNCTION "rand" () RETURNS double precision AS 'SELECT random();' LANGUAGE 'sql';" # emulate mysql proprietary functions used by the lcr module in postgresql if [ $? -ne 0 ] ; then merr "Creating mysql emulation functions failed!" exit 1 fi for TABLE in $STANDARD_MODULES; do mdbg "Creating core table: $TABLE" sql_query "$1" < $DB_SCHEMA/$TABLE-create.sql if [ $? -ne 0 ] ; then merr "Creating core tables failed!" exit 1 fi done sql_query "$1" "CREATE USER $DBRWUSER WITH PASSWORD '$DBRWPW'; CREATE USER $DBROUSER WITH PASSWORD '$DBROPW';" if [ $? -ne 0 ] ; then mwarn "Create user in database failed, perhaps they allready exist? Try to continue.." fi for TABLE in $STANDARD_TABLES; do sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE $TABLE TO $DBRWUSER;" sql_query "$1" "GRANT SELECT ON TABLE $TABLE TO $DBROUSER;" if [ $TABLE != "version" ] ; then sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE "$TABLE"_id_seq TO $DBRWUSER;" sql_query "$1" "GRANT SELECT ON TABLE "$TABLE"_id_seq TO $DBROUSER;" fi if [ $? -ne 0 ] ; then merr "Grant privileges to standard tables failed!" exit 1 fi done if [ -e $DB_SCHEMA/extensions-create.sql ] then minfo "Creating custom extensions tables" sql_query $1 < $DB_SCHEMA/extensions-create.sql if [ $? -ne 0 ] ; then merr "Creating custom extensions tables failed!" exit 1 fi fi minfo "Core Kamailio tables succesfully created." get_answer $INSTALL_PRESENCE_TABLES "Install presence related tables? (y/n): " if [ "$ANSWER" = "y" ]; then presence_create $1 fi get_answer $INSTALL_EXTRA_TABLES "Install tables for $EXTRA_MODULES? (y/n): " if [ "$ANSWER" = "y" ]; then extra_create $1 fi } # kamailio_create presence_create () # pars: { if [ $# -ne 1 ] ; then merr "presence_create function takes one param" exit 1 fi minfo "creating presence tables into $1 ..." sql_query "$1" < $DB_SCHEMA/presence-create.sql if [ $? -ne 0 ] ; then merr "Failed to create presence tables!" exit 1 fi sql_query "$1" < $DB_SCHEMA/rls-create.sql if [ $? -ne 0 ] ; then merr "Failed to create rls-presence tables!" exit 1 fi for TABLE in $PRESENCE_TABLES; do sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE $TABLE TO $DBRWUSER;" sql_query "$1" "GRANT SELECT ON TABLE $TABLE TO $DBROUSER;" sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE "$TABLE"_id_seq TO $DBRWUSER;" sql_query "$1" "GRANT SELECT ON TABLE "$TABLE"_id_seq TO $DBROUSER;" if [ $? -ne 0 ] ; then merr "Grant privileges to presence tables failed!" exit 1 fi done minfo "Presence tables succesfully created." } # end presence_create extra_create () # pars: { if [ $# -ne 1 ] ; then merr "extra_create function takes one param" exit 1 fi minfo "creating extra tables into $1 ..." for TABLE in $EXTRA_MODULES; do mdbg "Creating extra table: $TABLE" sql_query "$1" < $DB_SCHEMA/$TABLE-create.sql if [ $? -ne 0 ] ; then merr "Creating extra tables failed!" exit 1 fi done for TABLE in $EXTRA_TABLES; do sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE $TABLE TO $DBRWUSER;" sql_query "$1" "GRANT SELECT ON TABLE $TABLE TO $DBROUSER;" if [ $TABLE != "route_tree" ] ; then sql_query "$1" "GRANT ALL PRIVILEGES ON TABLE "$TABLE"_id_seq TO $DBRWUSER;" sql_query "$1" "GRANT SELECT ON TABLE "$TABLE"_id_seq TO $DBROUSER;" fi if [ $? -ne 0 ] ; then merr "Grant privileges to extra tables failed!" exit 1 fi done minfo "Extra tables succesfully created." } # end extra_create dbuid_create () # pars: { if [ $# -ne 1 ] ; then merr "dbuid_create function takes one param" exit 1 fi minfo "creating uid tables into $1 ..." for TABLE in $DBUID_MODULES; do mdbg "Creating uid table: $TABLE" sql_query $1 < $DB_SCHEMA/$TABLE-create.sql if [ $? -ne 0 ] ; then merr "Creating uid tables failed at $TABLE!" exit 1 fi done minfo "UID tables succesfully created." } # end uid_create kamailio-4.0.4/utils/kamctl/dbcassandra/0000755000000000000000000000000012223032460016655 5ustar rootrootkamailio-4.0.4/utils/kamctl/dbcassandra/kamailio/0000755000000000000000000000000012223032460020443 5ustar rootrootkamailio-4.0.4/utils/kamctl/dbcassandra/kamailio/domain_attrs0000644000000000000000000000012312223032460023046 0ustar rootrootdid(string) name(string) type(int) value(string) last_modified(int) did name value kamailio-4.0.4/utils/kamctl/dbcassandra/kamailio/subscriber0000644000000000000000000000017512223032460022534 0ustar rootrootusername(string) domain(string) password(string) email_address(string) ha1(string) ha1b(string) rpid(string) username domain kamailio-4.0.4/utils/kamctl/dbcassandra/kamailio/location0000644000000000000000000000041112223032460022172 0ustar rootrootcallid(string) cflags(int) contact(string) cseq(int) expires(timestamp) flags(int) last_modified(int) methods(int) path(string) q(double) received(string) socket(string) user_agent(string) username(string) ruid(string) instance(string) reg_id(int) username contact kamailio-4.0.4/utils/kamctl/dbcassandra/kamailio/version0000644000000000000000000000006112223032460022050 0ustar rootroottable_name(string) table_version(int) table_name kamailio-4.0.4/utils/kamctl/dbcassandra/kamailio/domain0000644000000000000000000000006512223032460021636 0ustar rootrootdomain(string) did(string) last_modified(int) domain kamailio-4.0.4/utils/kamctl/kamdbctl.base0000644000000000000000000001471012223032461017031 0ustar rootrootPATH=$PATH:/usr/local/sbin # config vars # name of the database to be used by Kamailio DBNAME=${DBNAME:-kamailio} # address of database server DBHOST=${DBHOST:-localhost} # user with full privileges over DBNAME database DBRWUSER=${DBRWUSER:-kamailio} # password user with full privileges over DBNAME database DBRWPW=${DBRWPW:-kamailiorw} # read-only user DBROUSER=${DBROUSER:-kamailioro} # password for read-only user DBROPW=${DBROPW:-kamailioro} # user name column USERCOL=${USERCOL:-username} # Describe what additional tables to install. Valid values for the variables # below are yes/no/ask. With ask it will interactively ask the user for the # answer, while yes/no allow for automated, unassisted installs. INSTALL_EXTRA_TABLES=${INSTALL_EXTRA_TABLES:-ask} INSTALL_PRESENCE_TABLES=${INSTALL_PRESENCE_TABLES:-ask} INSTALL_DBUID_TABLES=${INSTALL_DBUID_TABLES:-ask} # Used by dbtext and db_berkeley to define tables to be created, used by # postgres to do the grants STANDARD_TABLES=${STANDARD_TABLES:-version acc dbaliases domain domain_attrs grp uri speed_dial lcr_gw lcr_rule lcr_rule_target pdt subscriber location location_attrs re_grp trusted address missed_calls usr_preferences aliases silo dialog dialog_vars dispatcher dialplan} EXTRA_TABLES=${EXTRA_TABLES:-imc_members imc_rooms cpl sip_trace domainpolicy carrierroute carrier_name domain_name carrierfailureroute userblacklist globalblacklist htable purplemap uacreg pl_pipes mtree mtrees sca_subscriptions} PRESENCE_TABLES=${PRESENCE_TABLES:-presentity active_watchers watchers xcap pua rls_presentity rls_watchers} DBUID_TABLES=${UID_TABLES:-uid_credentials uid_domain uid_domain_attrs uid_global_attrs uid_uri uid_uri_attrs uid_user_attrs} # SQL definitions # If you change this definitions here, then you must change them # in ../../lib/srdb1/schema/entities.xml too. They are used in this # script and needed to be the same as in the database definitions. # FIXME FOREVER=${FOREVER:-2030-05-28 21:32:15} DEFAULT_ALIASES_EXPIRES=${DEFAULT_ALIASES_EXPIRES:-${FOREVER}} DEFAULT_Q=${DEFAULT_Q:-1.0} DEFAULT_CALLID=${DEFAULT_CALLID:-Default-Call-ID} DEFAULT_CSEQ=${DEFAULT_CSEQ:-13} DEFAULT_LOCATION_EXPIRES=${DEFAULT_LOCATION_EXPIRES:-${FOREVER}} # default location for config files DEFAULT_CFG_DIR=/usr/local/etc/kamailio # default location for data files DEFAULT_DATA_DIR=/usr/local/share/kamailio # Needed programs MD5=${MD5:-md5sum} AWK=${AWK:-awk} GREP=${GREP:-grep} SED=${SED:-sed} # define what modules should be installed STANDARD_MODULES=${STANDARD_MODULES:-standard acc lcr domain group permissions registrar usrloc msilo alias_db uri_db speeddial avpops auth_db pdt dialog dispatcher dialplan} PRESENCE_MODULES=${PRESENCE_MODULES:-presence rls} EXTRA_MODULES=${EXTRA_MODULES:-imc cpl siptrace domainpolicy carrierroute userblacklist htable purple uac pipelimit mtree sca} DBUID_MODULES=${UID_MODULES:-uid_auth_db uid_avp_db uid_domain uid_gflags uid_uri_db} ############################################################ # common functions usage() { COMMAND=`basename $0` cat < ...(creates a new database) $COMMAND drop .....(!entirely deletes tables!) $COMMAND reinit ...(!entirely deletes and than re-creates tables!) $COMMAND backup ...........................(dumps current database to file) $COMMAND restore ..........................(restores tables from a file) $COMMAND copy ...........................(creates a new db from an existing one) $COMMAND migrate ...............(migrates DB from 1.2 to 1.3, not implemented yet!) $COMMAND presence ................................(adds the presence related tables) $COMMAND extra ...................................(adds the extra tables) $COMMAND dbuid ...................................(adds the uid tables) $COMMAND dbonly ..................................(creates empty database) $COMMAND grant ...................................(grant privileges to database) $COMMAND revoke ..................................(revoke privileges to database) if you want to manipulate database as other database user than root, want to change database name from default value "$DBNAME", or want to use other values for users and password, edit the "config vars" section of the command $COMMAND. $COMMAND pframework create .......................(creates a sample provisioning framework file) EOF } #usage # read realm prompt_realm() { printf "Domain (realm) for the default user 'admin': " read SIP_DOMAIN echo } # calculate credentials for admin credentials() { HA1=`echo -n "admin:$SIP_DOMAIN:$DBRWPW" | $MD5 | $AWK '{ print $1 }'` if [ $? -ne 0 ] ; then merr "HA1 calculation failed" exit 1 fi HA1B=`echo -n "admin@$SIP_DOMAIN:$SIP_DOMAIN:$DBRWPW" | $MD5 | $AWK '{ print $1 }'` if [ $? -ne 0 ] ; then merr "HA1B calculation failed" exit 1 fi #PHPLIB_ID of users should be difficulty to guess for security reasons NOW=`date`; PHPLIB_ID=`echo -n "$RANDOM:$NOW:$SIP_DOMAIN" | $MD5 | $AWK '{ print $1 }'` if [ $? -ne 0 ] ; then merr "PHPLIB_ID calculation failed" exit 1 fi } # FIXME use the definition from kamctl.base mdbg() { if [ "0$VERBOSE" -ne 0 ] ; then if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e "\033[1m$1\033[0m" else echo "$1" fi fi } mwarn() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e '\E[37;32m'"\033[1mWARNING: $1\033[0m" else echo "** WARNING: $1" fi } minfo() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e '\E[37;33m'"\033[1mINFO: $1\033[0m" else echo "** INFO: $1" fi } mecho() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e "\033[1m$1\033[0m" else echo "$1" fi } merr() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e '\E[37;31m'"\033[1mERROR: $1\033[0m" else echo "** ERROR: $1" fi } # Get a y/n value from a variable. If the variable contains the keyword # `ask', then interactively ask the user for an answer. # # Arguments: # $1 - variable holding yes/no/ask # $2 - question to print if value of variable is ask # # Return: # On return $ANSWER will be available with y/n # get_answer () { value=$1 question=$2 if [ "${value}" = "ask" ]; then echo -n "$question" read ANSWER else ANSWER=${value} fi ANSWER=${ANSWER:0:1} ANSWER=${ANSWER/Y/y} ANSWER=${ANSWER/N/n} } kamailio-4.0.4/utils/kamctl/README0000644000000000000000000000041412223032460015267 0ustar rootrootSQL creation *must* follow the naming pattern -create.sql where corresponds to the name of the database table. Please use underscores ("_") instead of dashes ("-") in database table names to ensure compatibility with all types of databases. kamailio-4.0.4/utils/kamctl/kamctl.pgsql0000644000000000000000000000263712223032460016743 0ustar rootroot# # $Id$ # # control tool for maintaining Kamailio # #=================================================================== ##### ----------------------------------------------- ##### ### PGSQL specific variables and functions # ##### ----------------------------------------------- ##### ### load SQL base # if [ -f "$MYLIBDIR/kamctl.sqlbase" ]; then . "$MYLIBDIR/kamctl.sqlbase" else echo "Cannot load SQL core functions '$MYLIBDIR/kamctl.sqlbase' - exiting ..." exit -1 fi ##### ----------------------------------------------- ##### ### binaries if [ -z "$PGSQL" ] ; then locate_tool psql if [ -z "$TOOLPATH" ] ; then echo "error: 'psql' tool not found: set PGSQL variable to correct tool path" exit fi PGSQL="$TOOLPATH" fi # input: sql query, optional pgsql command-line params pgsql_query() { # if password not yet queried, query it now prompt_pw "PgSQL password for user '$DBRWUSER@$DBHOST'" mecho "pgsql_query: $PGSQL $2 -A -q -t -P fieldsep=' ' -h $DBHOST -U $DBRWUSER $DBNAME -c '$1'" PGPASSWORD="$DBRWPW" $PGSQL $2 \ -A -q -t \ -P fieldsep=" " \ -h $DBHOST \ -U $DBRWUSER \ $DBNAME \ -c "$1" } # input: sql query, optional pgsql command-line params pgsql_ro_query() { mdbg "pgsql_ro_query: $PGSQL $2 -h $DBHOST -U $DBROUSER $DBNAME -c '$1'" PGPASSWORD="$DBROPW" $PGSQL $2 \ -h $DBHOST \ -U $DBROUSER \ $DBNAME \ -c "$1" } DBCMD=pgsql_query DBROCMD=pgsql_ro_query DBRAWPARAMS="-A -q -t" kamailio-4.0.4/utils/kamctl/xhttp_pi/0000755000000000000000000000000012223032461016250 5ustar rootrootkamailio-4.0.4/utils/kamctl/xhttp_pi/htable-mod0000644000000000000000000000263212223032460020211 0ustar rootroot htable show htable DB1_QUERY id key_name key_type value_type key_value expires add htable DB1_INSERT key_name key_type value_type key_value expires update htable DB1_UPDATE id= key_name key_type value_type key_value expires delete htable DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/uid_auth_db-table0000644000000000000000000000130312223032460021523 0ustar rootroot uid_credentials mysql idDB1_INT auth_usernameDB1_STR didDB1_STR realmDB1_STR passwordDB1_STR flagsDB1_INT ha1DB1_STR ha1bDB1_STR uidDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/pipelimit-table0000644000000000000000000000060412223032460021253 0ustar rootroot pl_pipes mysql idDB1_INT pipeidDB1_STR algorithmDB1_STR plimitDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/domainpolicy-mod0000644000000000000000000000261312223032460021440 0ustar rootroot domainpolicy show domainpolicy DB1_QUERY id rule type att val description add domainpolicy DB1_INSERT rule type att val description update domainpolicy DB1_UPDATE id= rule type att val description delete domainpolicy DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/sca-table0000644000000000000000000000173112223032460020027 0ustar rootroot sca_subscriptions mysql idDB1_INT subscriberDB1_STR aorDB1_STR eventDB1_INT expiresDB1_INT stateDB1_INT app_idxDB1_INT call_idDB1_STR from_tagDB1_STR to_tagDB1_STR record_routeDB1_BLOB notify_cseqDB1_INT subscribe_cseqDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/presence-mod0000644000000000000000000002725512223032460020566 0ustar rootroot presentity show presentity DB1_QUERY id username domain event etag expires received_time body sender add presentity DB1_INSERT username domain event etag expires received_time body sender update presentity DB1_UPDATE id= username domain event etag expires received_time body sender delete presentity DB1_DELETE id= active_watchers show active_watchers DB1_QUERY id presentity_uri watcher_username watcher_domain to_user to_domain event event_id to_tag from_tag callid local_cseq remote_cseq contact record_route expires status reason version socket_info local_contact from_user from_domain updated updated_winfo add active_watchers DB1_INSERT presentity_uri watcher_username watcher_domain to_user to_domain event event_id to_tag from_tag callid local_cseq remote_cseq contact record_route expires status reason version socket_info local_contact from_user from_domain updated updated_winfo update active_watchers DB1_UPDATE id= presentity_uri watcher_username watcher_domain to_user to_domain event event_id to_tag from_tag callid local_cseq remote_cseq contact record_route expires status reason version socket_info local_contact from_user from_domain updated updated_winfo delete active_watchers DB1_DELETE id= watchers show watchers DB1_QUERY id presentity_uri watcher_username watcher_domain event status reason inserted_time add watchers DB1_INSERT presentity_uri watcher_username watcher_domain event status reason inserted_time update watchers DB1_UPDATE id= presentity_uri watcher_username watcher_domain event status reason inserted_time delete watchers DB1_DELETE id= xcap show xcap DB1_QUERY id username domain doc doc_type etag source doc_uri port add xcap DB1_INSERT username domain doc doc_type etag source doc_uri port update xcap DB1_UPDATE id= username domain doc doc_type etag source doc_uri port delete xcap DB1_DELETE id= pua show pua DB1_QUERY id pres_uri pres_id event expires desired_expires flag etag tuple_id watcher_uri call_id to_tag from_tag cseq record_route contact remote_contact version extra_headers add pua DB1_INSERT pres_uri pres_id event expires desired_expires flag etag tuple_id watcher_uri call_id to_tag from_tag cseq record_route contact remote_contact version extra_headers update pua DB1_UPDATE id= pres_uri pres_id event expires desired_expires flag etag tuple_id watcher_uri call_id to_tag from_tag cseq record_route contact remote_contact version extra_headers delete pua DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/siptrace-table0000644000000000000000000000157312223032460021077 0ustar rootroot sip_trace mysql idDB1_INT time_stampDB1_DATETIME time_usDB1_INT callidDB1_STR traced_userDB1_STR msgDB1_BLOB methodDB1_STR statusDB1_STR fromipDB1_STR toipDB1_STR fromtagDB1_STR directionDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/cpl-mod0000644000000000000000000000240112223032460017522 0ustar rootroot cpl show cpl DB1_QUERY id username domain cpl_xml cpl_bin add cpl DB1_INSERT username domain cpl_xml cpl_bin update cpl DB1_UPDATE id= username domain cpl_xml cpl_bin delete cpl DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/avpops-mod0000644000000000000000000000323712223032460020264 0ustar rootroot usr_preferences show usr_preferences DB1_QUERY id uuid username domain attribute type value last_modified add usr_preferences DB1_INSERT uuid username domain attribute type value last_modified update usr_preferences DB1_UPDATE id= uuid username domain attribute type value last_modified delete usr_preferences DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/siptrace-mod0000644000000000000000000000407512223032460020567 0ustar rootroot sip_trace show sip_trace DB1_QUERY id time_stamp time_us callid traced_user msg method status fromip toip fromtag direction add sip_trace DB1_INSERT time_stamp time_us callid traced_user msg method status fromip toip fromtag direction update sip_trace DB1_UPDATE id= time_stamp time_us callid traced_user msg method status fromip toip fromtag direction delete sip_trace DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/usrloc-mod0000644000000000000000000001072012223032460020256 0ustar rootroot location show location DB1_QUERY id ruid username domain contact received path expires q callid cseq last_modified flags cflags user_agent socket methods instance reg_id add location DB1_INSERT ruid username domain contact received path expires q callid cseq last_modified flags cflags user_agent socket methods instance reg_id update location DB1_UPDATE id= ruid username domain contact received path expires q callid cseq last_modified flags cflags user_agent socket methods instance reg_id delete location DB1_DELETE id= location_attrs show location_attrs DB1_QUERY id ruid username domain aname atype avalue last_modified add location_attrs DB1_INSERT ruid username domain aname atype avalue last_modified update location_attrs DB1_UPDATE id= ruid username domain aname atype avalue last_modified delete location_attrs DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/imc-table0000644000000000000000000000150512223032460020030 0ustar rootroot imc_rooms mysql idDB1_INT nameDB1_STR domainDB1_STR flagDB1_INT imc_members mysql idDB1_INT usernameDB1_STR domainDB1_STR roomDB1_STR flagDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/matrix-table0000644000000000000000000000047612223032460020572 0ustar rootroot matrix mysql firstDB1_INT secondDB1_INT resDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/uri_db-mod0000644000000000000000000000242612223032460020217 0ustar rootroot uri show uri DB1_QUERY id username domain uri_user last_modified add uri DB1_INSERT username domain uri_user last_modified update uri DB1_UPDATE id= username domain uri_user last_modified delete uri DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/uid_gflags-mod0000644000000000000000000000246112223032460021056 0ustar rootroot uid_global_attrs show uid_global_attrs DB1_QUERY id name type value flags add uid_global_attrs DB1_INSERT name type value flags update uid_global_attrs DB1_UPDATE id= name type value flags delete uid_global_attrs DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/uid_uri_db-table0000644000000000000000000000216012223032460021363 0ustar rootroot uid_uri mysql idDB1_INT uidDB1_STR didDB1_STR usernameDB1_STR flagsDB1_INT schemeDB1_STR uid_uri_attrs mysql idDB1_INT usernameDB1_STR didDB1_STR nameDB1_STR valueDB1_STR typeDB1_INT flagsDB1_INT schemeDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/msilo-mod0000644000000000000000000000423212223032460020073 0ustar rootroot silo show silo DB1_QUERY id src_addr dst_addr username domain inc_time exp_time snd_time ctype body extra_hdrs callid status add silo DB1_INSERT src_addr dst_addr username domain inc_time exp_time snd_time ctype body extra_hdrs callid status update silo DB1_UPDATE id= src_addr dst_addr username domain inc_time exp_time snd_time ctype body extra_hdrs callid status delete silo DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/uac-mod0000644000000000000000000000377312223032460017531 0ustar rootroot uacreg show uacreg DB1_QUERY id l_uuid l_username l_domain r_username r_domain realm auth_username auth_password auth_proxy expires add uacreg DB1_INSERT l_uuid l_username l_domain r_username r_domain realm auth_username auth_password auth_proxy expires update uacreg DB1_UPDATE id= l_uuid l_username l_domain r_username r_domain realm auth_username auth_password auth_proxy expires delete uacreg DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/presence-table0000644000000000000000000001201512223032460021062 0ustar rootroot presentity mysql idDB1_INT usernameDB1_STR domainDB1_STR eventDB1_STR etagDB1_STR expiresDB1_INT received_timeDB1_INT bodyDB1_BLOB senderDB1_STR active_watchers mysql idDB1_INT presentity_uriDB1_STR watcher_usernameDB1_STR watcher_domainDB1_STR to_userDB1_STR to_domainDB1_STR eventDB1_STR event_idDB1_STR to_tagDB1_STR from_tagDB1_STR callidDB1_STR local_cseqDB1_INT remote_cseqDB1_INT contactDB1_STR record_routeDB1_BLOB expiresDB1_INT statusDB1_INT reasonDB1_STR versionDB1_INT socket_infoDB1_STR local_contactDB1_STR from_userDB1_STR from_domainDB1_STR updatedDB1_INT updated_winfoDB1_INT watchers mysql idDB1_INT presentity_uriDB1_STR watcher_usernameDB1_STR watcher_domainDB1_STR eventDB1_STR statusDB1_INT reasonDB1_STR inserted_timeDB1_INT xcap mysql idDB1_INT usernameDB1_STR domainDB1_STR docDB1_BLOB doc_typeDB1_INT etagDB1_STR sourceDB1_INT doc_uriDB1_STR portDB1_INT pua mysql idDB1_INT pres_uriDB1_STR pres_idDB1_STR eventDB1_INT expiresDB1_INT desired_expiresDB1_INT flagDB1_INT etagDB1_STR tuple_idDB1_STR watcher_uriDB1_STR call_idDB1_STR to_tagDB1_STR from_tagDB1_STR cseqDB1_INT record_routeDB1_BLOB contactDB1_STR remote_contactDB1_STR versionDB1_INT extra_headersDB1_BLOB kamailio-4.0.4/utils/kamctl/xhttp_pi/uid_gflags-table0000644000000000000000000000072012223032460021362 0ustar rootroot uid_global_attrs mysql idDB1_INT nameDB1_STR typeDB1_INT valueDB1_STR flagsDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/carrierroute-mod0000644000000000000000000001377212223032460021467 0ustar rootroot carrierroute show carrierroute DB1_QUERY id carrier domain scan_prefix flags mask prob strip rewrite_host rewrite_prefix rewrite_suffix description add carrierroute DB1_INSERT carrier domain scan_prefix flags mask prob strip rewrite_host rewrite_prefix rewrite_suffix description update carrierroute DB1_UPDATE id= carrier domain scan_prefix flags mask prob strip rewrite_host rewrite_prefix rewrite_suffix description delete carrierroute DB1_DELETE id= carrierfailureroute show carrierfailureroute DB1_QUERY id carrier domain scan_prefix host_name reply_code flags mask next_domain description add carrierfailureroute DB1_INSERT carrier domain scan_prefix host_name reply_code flags mask next_domain description update carrierfailureroute DB1_UPDATE id= carrier domain scan_prefix host_name reply_code flags mask next_domain description delete carrierfailureroute DB1_DELETE id= carrier_name show carrier_name DB1_QUERY id carrier add carrier_name DB1_INSERT carrier update carrier_name DB1_UPDATE id= carrier delete carrier_name DB1_DELETE id= domain_name show domain_name DB1_QUERY id domain add domain_name DB1_INSERT domain update domain_name DB1_UPDATE id= domain delete domain_name DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/msilo-table0000644000000000000000000000165112223032460020405 0ustar rootroot silo mysql idDB1_INT src_addrDB1_STR dst_addrDB1_STR usernameDB1_STR domainDB1_STR inc_timeDB1_INT exp_timeDB1_INT snd_timeDB1_INT ctypeDB1_STR bodyDB1_BLOB extra_hdrsDB1_BLOB callidDB1_STR statusDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/pdt-table0000644000000000000000000000056312223032460020052 0ustar rootroot pdt mysql idDB1_INT sdomainDB1_STR prefixDB1_STR domainDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/domain-table0000644000000000000000000000161712223032460020533 0ustar rootroot domain mysql idDB1_INT domainDB1_STR didDB1_STR last_modifiedDB1_DATETIME domain_attrs mysql idDB1_INT didDB1_STR nameDB1_STR typeDB1_INT valueDB1_STR last_modifiedDB1_DATETIME kamailio-4.0.4/utils/kamctl/xhttp_pi/uac-table0000644000000000000000000000150412223032460020027 0ustar rootroot uacreg mysql idDB1_INT l_uuidDB1_STR l_usernameDB1_STR l_domainDB1_STR r_usernameDB1_STR r_domainDB1_STR realmDB1_STR auth_usernameDB1_STR auth_passwordDB1_STR auth_proxyDB1_STR expiresDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/uid_avp_db-table0000644000000000000000000000100412223032460021346 0ustar rootroot uid_user_attrs mysql idDB1_INT uidDB1_STR nameDB1_STR valueDB1_STR typeDB1_INT flagsDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/acc-table0000644000000000000000000000237512223032461020015 0ustar rootroot acc mysql idDB1_INT methodDB1_STR from_tagDB1_STR to_tagDB1_STR callidDB1_STR sip_codeDB1_STR sip_reasonDB1_STR timeDB1_DATETIME missed_calls mysql idDB1_INT methodDB1_STR from_tagDB1_STR to_tagDB1_STR callidDB1_STR sip_codeDB1_STR sip_reasonDB1_STR timeDB1_DATETIME kamailio-4.0.4/utils/kamctl/xhttp_pi/mtree-table0000644000000000000000000000126712223032460020401 0ustar rootroot mtree mysql idDB1_INT tprefixDB1_STR tvalueDB1_STR mtrees mysql idDB1_INT tnameDB1_STR tprefixDB1_STR tvalueDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/standard-table0000644000000000000000000000042312223032460021056 0ustar rootroot version mysql table_nameDB1_STR table_versionDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/uid_domain-table0000644000000000000000000000161512223032460021372 0ustar rootroot uid_domain mysql idDB1_INT didDB1_STR domainDB1_STR flagsDB1_INT uid_domain_attrs mysql idDB1_INT didDB1_STR nameDB1_STR typeDB1_INT valueDB1_STR flagsDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/usrloc-table0000644000000000000000000000367212223032460020576 0ustar rootroot location mysql idDB1_INT ruidDB1_STR usernameDB1_STR domainDB1_STR contactDB1_STR receivedDB1_STR pathDB1_STR expiresDB1_DATETIME qDB1_DOUBLE callidDB1_STR cseqDB1_INT last_modifiedDB1_DATETIME flagsDB1_INT cflagsDB1_INT user_agentDB1_STR socketDB1_STR methodsDB1_INT instanceDB1_STR reg_idDB1_INT location_attrs mysql idDB1_INT ruidDB1_STR usernameDB1_STR domainDB1_STR anameDB1_STR atypeDB1_INT avalueDB1_STR last_modifiedDB1_DATETIME kamailio-4.0.4/utils/kamctl/xhttp_pi/mtree-mod0000644000000000000000000000427612223032460020074 0ustar rootroot mtree show mtree DB1_QUERY id tprefix tvalue add mtree DB1_INSERT tprefix tvalue update mtree DB1_UPDATE id= tprefix tvalue delete mtree DB1_DELETE id= mtrees show mtrees DB1_QUERY id tname tprefix tvalue add mtrees DB1_INSERT tname tprefix tvalue update mtrees DB1_UPDATE id= tname tprefix tvalue delete mtrees DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/alias_db-table0000644000000000000000000000072112223032460021015 0ustar rootroot dbaliases mysql idDB1_INT alias_usernameDB1_STR alias_domainDB1_STR usernameDB1_STR domainDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/dispatcher-table0000644000000000000000000000110712223032460021404 0ustar rootroot dispatcher mysql idDB1_INT setidDB1_INT destinationDB1_STR flagsDB1_INT priorityDB1_INT attrsDB1_STR descriptionDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/auth_db-mod0000644000000000000000000000317012223032460020356 0ustar rootroot subscriber show subscriber DB1_QUERY id username domain password email_address ha1 ha1b rpid add subscriber DB1_INSERT username domain password email_address ha1 ha1b rpid update subscriber DB1_UPDATE id= username domain password email_address ha1 ha1b rpid delete subscriber DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/speeddial-mod0000644000000000000000000000340512223032460020703 0ustar rootroot speed_dial show speed_dial DB1_QUERY id username domain sd_username sd_domain new_uri fname lname description add speed_dial DB1_INSERT username domain sd_username sd_domain new_uri fname lname description update speed_dial DB1_UPDATE id= username domain sd_username sd_domain new_uri fname lname description delete speed_dial DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/uid_auth_db-mod0000644000000000000000000000335512223032460021224 0ustar rootroot uid_credentials show uid_credentials DB1_QUERY id auth_username did realm password flags ha1 ha1b uid add uid_credentials DB1_INSERT auth_username did realm password flags ha1 ha1b uid update uid_credentials DB1_UPDATE id= auth_username did realm password flags ha1 ha1b uid delete uid_credentials DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/permissions-table0000644000000000000000000000165412223032460021640 0ustar rootroot trusted mysql idDB1_INT src_ipDB1_STR protoDB1_STR from_patternDB1_STR tagDB1_STR address mysql idDB1_INT grpDB1_INT ip_addrDB1_STR maskDB1_INT portDB1_INT tagDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/purple-mod0000644000000000000000000000246112223032460020261 0ustar rootroot purplemap show purplemap DB1_QUERY id sip_user ext_user ext_prot ext_pass add purplemap DB1_INSERT sip_user ext_user ext_prot ext_pass update purplemap DB1_UPDATE id= sip_user ext_user ext_prot ext_pass delete purplemap DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/speeddial-table0000644000000000000000000000130612223032460021211 0ustar rootroot speed_dial mysql idDB1_INT usernameDB1_STR domainDB1_STR sd_usernameDB1_STR sd_domainDB1_STR new_uriDB1_STR fnameDB1_STR lnameDB1_STR descriptionDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/registrar-mod0000644000000000000000000000546712223032460020765 0ustar rootroot aliases show aliases DB1_QUERY id ruid username domain contact received path expires q callid cseq last_modified flags cflags user_agent socket methods instance reg_id add aliases DB1_INSERT ruid username domain contact received path expires q callid cseq last_modified flags cflags user_agent socket methods instance reg_id update aliases DB1_UPDATE id= ruid username domain contact received path expires q callid cseq last_modified flags cflags user_agent socket methods instance reg_id delete aliases DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/group-table0000644000000000000000000000137312223032460020417 0ustar rootroot grp mysql idDB1_INT usernameDB1_STR domainDB1_STR grpDB1_STR last_modifiedDB1_DATETIME re_grp mysql idDB1_INT reg_expDB1_STR group_idDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/pdt-mod0000644000000000000000000000221112223032460017532 0ustar rootroot pdt show pdt DB1_QUERY id sdomain prefix domain add pdt DB1_INSERT sdomain prefix domain update pdt DB1_UPDATE id= sdomain prefix domain delete pdt DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/group-mod0000644000000000000000000000447112223032460020111 0ustar rootroot grp show grp DB1_QUERY id username domain grp last_modified add grp DB1_INSERT username domain grp last_modified update grp DB1_UPDATE id= username domain grp last_modified delete grp DB1_DELETE id= re_grp show re_grp DB1_QUERY id reg_exp group_id add re_grp DB1_INSERT reg_exp group_id update re_grp DB1_UPDATE id= reg_exp group_id delete re_grp DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/registrar-table0000644000000000000000000000244612223032460021267 0ustar rootroot aliases mysql idDB1_INT ruidDB1_STR usernameDB1_STR domainDB1_STR contactDB1_STR receivedDB1_STR pathDB1_STR expiresDB1_DATETIME qDB1_DOUBLE callidDB1_STR cseqDB1_INT last_modifiedDB1_DATETIME flagsDB1_INT cflagsDB1_INT user_agentDB1_STR socketDB1_STR methodsDB1_INT instanceDB1_STR reg_idDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/domain-mod0000644000000000000000000000507312223032460020223 0ustar rootroot domain show domain DB1_QUERY id domain did last_modified add domain DB1_INSERT domain did last_modified update domain DB1_UPDATE id= domain did last_modified delete domain DB1_DELETE id= domain_attrs show domain_attrs DB1_QUERY id did name type value last_modified add domain_attrs DB1_INSERT did name type value last_modified update domain_attrs DB1_UPDATE id= did name type value last_modified delete domain_attrs DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/alias_db-mod0000644000000000000000000000251112223032460020504 0ustar rootroot dbaliases show dbaliases DB1_QUERY id alias_username alias_domain username domain add dbaliases DB1_INSERT alias_username alias_domain username domain update dbaliases DB1_UPDATE id= alias_username alias_domain username domain delete dbaliases DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/dialplan-table0000644000000000000000000000127012223032460021043 0ustar rootroot dialplan mysql idDB1_INT dpidDB1_INT prDB1_INT match_opDB1_INT match_expDB1_STR match_lenDB1_INT subst_expDB1_STR repl_expDB1_STR attrsDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/dialog-mod0000644000000000000000000001156712223032460020220 0ustar rootroot dialog show dialog DB1_QUERY id hash_entry hash_id callid from_uri from_tag to_uri to_tag caller_cseq callee_cseq caller_route_set callee_route_set caller_contact callee_contact caller_sock callee_sock state start_time timeout sflags iflags toroute_name req_uri xdata add dialog DB1_INSERT hash_entry hash_id callid from_uri from_tag to_uri to_tag caller_cseq callee_cseq caller_route_set callee_route_set caller_contact callee_contact caller_sock callee_sock state start_time timeout sflags iflags toroute_name req_uri xdata update dialog DB1_UPDATE id= hash_entry hash_id callid from_uri from_tag to_uri to_tag caller_cseq callee_cseq caller_route_set callee_route_set caller_contact callee_contact caller_sock callee_sock state start_time timeout sflags iflags toroute_name req_uri xdata delete dialog DB1_DELETE id= dialog_vars show dialog_vars DB1_QUERY id hash_entry hash_id dialog_key dialog_value add dialog_vars DB1_INSERT hash_entry hash_id dialog_key dialog_value update dialog_vars DB1_UPDATE id= hash_entry hash_id dialog_key dialog_value delete dialog_vars DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/pi_framework-000000644000000000000000000000257012223032460021100 0ustar rootroot mysql://kamailio:kamailiorw@localhost/kamailio kamailio-4.0.4/utils/kamctl/xhttp_pi/permissions-mod0000644000000000000000000000516412223032460021330 0ustar rootroot trusted show trusted DB1_QUERY id src_ip proto from_pattern tag add trusted DB1_INSERT src_ip proto from_pattern tag update trusted DB1_UPDATE id= src_ip proto from_pattern tag delete trusted DB1_DELETE id= address show address DB1_QUERY id grp ip_addr mask port tag add address DB1_INSERT grp ip_addr mask port tag update address DB1_UPDATE id= grp ip_addr mask port tag delete address DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/uid_domain-mod0000644000000000000000000000507312223032460021064 0ustar rootroot uid_domain show uid_domain DB1_QUERY id did domain flags add uid_domain DB1_INSERT did domain flags update uid_domain DB1_UPDATE id= did domain flags delete uid_domain DB1_DELETE id= uid_domain_attrs show uid_domain_attrs DB1_QUERY id did name type value flags add uid_domain_attrs DB1_INSERT did name type value flags update uid_domain_attrs DB1_UPDATE id= did name type value flags delete uid_domain_attrs DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/dispatcher-mod0000644000000000000000000000303012223032460021071 0ustar rootroot dispatcher show dispatcher DB1_QUERY id setid destination flags priority attrs description add dispatcher DB1_INSERT setid destination flags priority attrs description update dispatcher DB1_UPDATE id= setid destination flags priority attrs description delete dispatcher DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/auth_db-table0000644000000000000000000000117712223032460020673 0ustar rootroot subscriber mysql idDB1_INT usernameDB1_STR domainDB1_STR passwordDB1_STR email_addressDB1_STR ha1DB1_STR ha1bDB1_STR rpidDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/drouting-mod0000644000000000000000000001305412223032460020605 0ustar rootroot dr_gateways show dr_gateways DB1_QUERY gwid type address strip pri_prefix attrs description add dr_gateways DB1_INSERT type address strip pri_prefix attrs description update dr_gateways DB1_UPDATE gwid= type address strip pri_prefix attrs description delete dr_gateways DB1_DELETE gwid= dr_rules show dr_rules DB1_QUERY ruleid groupid prefix timerec priority routeid gwlist description add dr_rules DB1_INSERT groupid prefix timerec priority routeid gwlist description update dr_rules DB1_UPDATE ruleid= groupid prefix timerec priority routeid gwlist description delete dr_rules DB1_DELETE ruleid= dr_gw_lists show dr_gw_lists DB1_QUERY id gwlist description add dr_gw_lists DB1_INSERT gwlist description update dr_gw_lists DB1_UPDATE id= gwlist description delete dr_gw_lists DB1_DELETE id= dr_groups show dr_groups DB1_QUERY id username domain groupid description add dr_groups DB1_INSERT username domain groupid description update dr_groups DB1_UPDATE id= username domain groupid description delete dr_groups DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/imc-mod0000644000000000000000000000467512223032460017533 0ustar rootroot imc_rooms show imc_rooms DB1_QUERY id name domain flag add imc_rooms DB1_INSERT name domain flag update imc_rooms DB1_UPDATE id= name domain flag delete imc_rooms DB1_DELETE id= imc_members show imc_members DB1_QUERY id username domain room flag add imc_members DB1_INSERT username domain room flag update imc_members DB1_UPDATE id= username domain room flag delete imc_members DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/carrierroute-table0000644000000000000000000000433312223032460021770 0ustar rootroot carrierroute mysql idDB1_INT carrierDB1_INT domainDB1_INT scan_prefixDB1_STR flagsDB1_INT maskDB1_INT probDB1_DOUBLE stripDB1_INT rewrite_hostDB1_STR rewrite_prefixDB1_STR rewrite_suffixDB1_STR descriptionDB1_STR carrierfailureroute mysql idDB1_INT carrierDB1_INT domainDB1_INT scan_prefixDB1_STR host_nameDB1_STR reply_codeDB1_STR flagsDB1_INT maskDB1_INT next_domainDB1_INT descriptionDB1_STR carrier_name mysql idDB1_INT carrierDB1_STR domain_name mysql idDB1_INT domainDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/dialplan-mod0000644000000000000000000000334112223032460020534 0ustar rootroot dialplan show dialplan DB1_QUERY id dpid pr match_op match_exp match_len subst_exp repl_exp attrs add dialplan DB1_INSERT dpid pr match_op match_exp match_len subst_exp repl_exp attrs update dialplan DB1_UPDATE id= dpid pr match_op match_exp match_len subst_exp repl_exp attrs delete dialplan DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/lcr-table0000644000000000000000000000406512223032460020044 0ustar rootroot lcr_gw mysql idDB1_INT lcr_idDB1_INT gw_nameDB1_STR ip_addrDB1_STR hostnameDB1_STR portDB1_INT paramsDB1_STR uri_schemeDB1_INT transportDB1_INT stripDB1_INT prefixDB1_STR tagDB1_STR flagsDB1_INT defunctDB1_INT lcr_rule_target mysql idDB1_INT lcr_idDB1_INT rule_idDB1_INT gw_idDB1_INT priorityDB1_INT weightDB1_INT lcr_rule mysql idDB1_INT lcr_idDB1_INT prefixDB1_STR from_uriDB1_STR request_uriDB1_STR stopperDB1_INT enabledDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/cpl-table0000644000000000000000000000066512223032460020044 0ustar rootroot cpl mysql idDB1_INT usernameDB1_STR domainDB1_STR cpl_xmlDB1_BLOB cpl_binDB1_BLOB kamailio-4.0.4/utils/kamctl/xhttp_pi/lcr-mod0000644000000000000000000001227312223032460017534 0ustar rootroot lcr_gw show lcr_gw DB1_QUERY id lcr_id gw_name ip_addr hostname port params uri_scheme transport strip prefix tag flags defunct add lcr_gw DB1_INSERT lcr_id gw_name ip_addr hostname port params uri_scheme transport strip prefix tag flags defunct update lcr_gw DB1_UPDATE id= lcr_id gw_name ip_addr hostname port params uri_scheme transport strip prefix tag flags defunct delete lcr_gw DB1_DELETE id= lcr_rule_target show lcr_rule_target DB1_QUERY id lcr_id rule_id gw_id priority weight add lcr_rule_target DB1_INSERT lcr_id rule_id gw_id priority weight update lcr_rule_target DB1_UPDATE id= lcr_id rule_id gw_id priority weight delete lcr_rule_target DB1_DELETE id= lcr_rule show lcr_rule DB1_QUERY id lcr_id prefix from_uri request_uri stopper enabled add lcr_rule DB1_INSERT lcr_id prefix from_uri request_uri stopper enabled update lcr_rule DB1_UPDATE id= lcr_id prefix from_uri request_uri stopper enabled delete lcr_rule DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/rls-table0000644000000000000000000000457512223032460020072 0ustar rootroot rls_presentity mysql idDB1_INT rlsubs_didDB1_STR resource_uriDB1_STR content_typeDB1_STR presence_stateDB1_BLOB expiresDB1_INT updatedDB1_INT auth_stateDB1_INT reasonDB1_STR rls_watchers mysql idDB1_INT presentity_uriDB1_STR to_userDB1_STR to_domainDB1_STR watcher_usernameDB1_STR watcher_domainDB1_STR eventDB1_STR event_idDB1_STR to_tagDB1_STR from_tagDB1_STR callidDB1_STR local_cseqDB1_INT remote_cseqDB1_INT contactDB1_STR record_routeDB1_BLOB expiresDB1_INT statusDB1_INT reasonDB1_STR versionDB1_INT socket_infoDB1_STR local_contactDB1_STR from_userDB1_STR from_domainDB1_STR updatedDB1_INT kamailio-4.0.4/utils/kamctl/xhttp_pi/domainpolicy-table0000644000000000000000000000100212223032460021737 0ustar rootroot domainpolicy mysql idDB1_INT ruleDB1_STR typeDB1_STR attDB1_STR valDB1_STR descriptionDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/uri_db-table0000644000000000000000000000067712223032460020535 0ustar rootroot uri mysql idDB1_INT usernameDB1_STR domainDB1_STR uri_userDB1_STR last_modifiedDB1_DATETIME kamailio-4.0.4/utils/kamctl/xhttp_pi/standard-mod0000644000000000000000000000103312223032460020544 0ustar rootroot version show version DB1_QUERY table_name table_version add version DB1_INSERT table_name table_version kamailio-4.0.4/utils/kamctl/xhttp_pi/rls-mod0000644000000000000000000001261512223032460017554 0ustar rootroot rls_presentity show rls_presentity DB1_QUERY id rlsubs_did resource_uri content_type presence_state expires updated auth_state reason add rls_presentity DB1_INSERT rlsubs_did resource_uri content_type presence_state expires updated auth_state reason update rls_presentity DB1_UPDATE id= rlsubs_did resource_uri content_type presence_state expires updated auth_state reason delete rls_presentity DB1_DELETE id= rls_watchers show rls_watchers DB1_QUERY id presentity_uri to_user to_domain watcher_username watcher_domain event event_id to_tag from_tag callid local_cseq remote_cseq contact record_route expires status reason version socket_info local_contact from_user from_domain updated add rls_watchers DB1_INSERT presentity_uri to_user to_domain watcher_username watcher_domain event event_id to_tag from_tag callid local_cseq remote_cseq contact record_route expires status reason version socket_info local_contact from_user from_domain updated update rls_watchers DB1_UPDATE id= presentity_uri to_user to_domain watcher_username watcher_domain event event_id to_tag from_tag callid local_cseq remote_cseq contact record_route expires status reason version socket_info local_contact from_user from_domain updated delete rls_watchers DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/sca-mod0000644000000000000000000000440612223032460017521 0ustar rootroot sca_subscriptions show sca_subscriptions DB1_QUERY id subscriber aor event expires state app_idx call_id from_tag to_tag record_route notify_cseq subscribe_cseq add sca_subscriptions DB1_INSERT subscriber aor event expires state app_idx call_id from_tag to_tag record_route notify_cseq subscribe_cseq update sca_subscriptions DB1_UPDATE id= subscriber aor event expires state app_idx call_id from_tag to_tag record_route notify_cseq subscribe_cseq delete sca_subscriptions DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/acc-mod0000644000000000000000000000633612223032461017506 0ustar rootroot acc show acc DB1_QUERY id method from_tag to_tag callid sip_code sip_reason time add acc DB1_INSERT method from_tag to_tag callid sip_code sip_reason time update acc DB1_UPDATE id= method from_tag to_tag callid sip_code sip_reason time delete acc DB1_DELETE id= missed_calls show missed_calls DB1_QUERY id method from_tag to_tag callid sip_code sip_reason time add missed_calls DB1_INSERT method from_tag to_tag callid sip_code sip_reason time update missed_calls DB1_UPDATE id= method from_tag to_tag callid sip_code sip_reason time delete missed_calls DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/dialog-table0000644000000000000000000000414012223032460020515 0ustar rootroot dialog mysql idDB1_INT hash_entryDB1_INT hash_idDB1_INT callidDB1_STR from_uriDB1_STR from_tagDB1_STR to_uriDB1_STR to_tagDB1_STR caller_cseqDB1_STR callee_cseqDB1_STR caller_route_setDB1_STR callee_route_setDB1_STR caller_contactDB1_STR callee_contactDB1_STR caller_sockDB1_STR callee_sockDB1_STR stateDB1_INT start_timeDB1_INT timeoutDB1_INT sflagsDB1_INT iflagsDB1_INT toroute_nameDB1_STR req_uriDB1_STR xdataDB1_STR dialog_vars mysql idDB1_INT hash_entryDB1_INT hash_idDB1_INT dialog_keyDB1_STR dialog_valueDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/pipelimit-mod0000644000000000000000000000225512223032460020747 0ustar rootroot pl_pipes show pl_pipes DB1_QUERY id pipeid algorithm plimit add pl_pipes DB1_INSERT pipeid algorithm plimit update pl_pipes DB1_UPDATE id= pipeid algorithm plimit delete pl_pipes DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/matrix-mod0000644000000000000000000000110312223032460020246 0ustar rootroot matrix show matrix DB1_QUERY first second res add matrix DB1_INSERT first second res kamailio-4.0.4/utils/kamctl/xhttp_pi/drouting-table0000644000000000000000000000374712223032460021125 0ustar rootroot dr_gateways mysql gwidDB1_INT typeDB1_INT addressDB1_STR stripDB1_INT pri_prefixDB1_STR attrsDB1_STR descriptionDB1_STR dr_rules mysql ruleidDB1_INT groupidDB1_STR prefixDB1_STR timerecDB1_STR priorityDB1_INT routeidDB1_STR gwlistDB1_STR descriptionDB1_STR dr_gw_lists mysql idDB1_INT gwlistDB1_STR descriptionDB1_STR dr_groups mysql idDB1_INT usernameDB1_STR domainDB1_STR groupidDB1_INT descriptionDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/pi_framework.xml0000644000000000000000000044003112223032461021461 0ustar rootroot mysql://kamailio:kamailiorw@localhost/kamailio acc mysql idDB1_INT methodDB1_STR from_tagDB1_STR to_tagDB1_STR callidDB1_STR sip_codeDB1_STR sip_reasonDB1_STR timeDB1_DATETIME missed_calls mysql idDB1_INT methodDB1_STR from_tagDB1_STR to_tagDB1_STR callidDB1_STR sip_codeDB1_STR sip_reasonDB1_STR timeDB1_DATETIME dbaliases mysql idDB1_INT alias_usernameDB1_STR alias_domainDB1_STR usernameDB1_STR domainDB1_STR subscriber mysql idDB1_INT usernameDB1_STR domainDB1_STR passwordDB1_STR email_addressDB1_STR ha1DB1_STR ha1bDB1_STR rpidDB1_STR usr_preferences mysql idDB1_INT uuidDB1_STR usernameDB1_STR domainDB1_STR attributeDB1_STR typeDB1_INT valueDB1_STR last_modifiedDB1_DATETIME carrierroute mysql idDB1_INT carrierDB1_INT domainDB1_INT scan_prefixDB1_STR flagsDB1_INT maskDB1_INT probDB1_DOUBLE stripDB1_INT rewrite_hostDB1_STR rewrite_prefixDB1_STR rewrite_suffixDB1_STR descriptionDB1_STR carrierfailureroute mysql idDB1_INT carrierDB1_INT domainDB1_INT scan_prefixDB1_STR host_nameDB1_STR reply_codeDB1_STR flagsDB1_INT maskDB1_INT next_domainDB1_INT descriptionDB1_STR carrier_name mysql idDB1_INT carrierDB1_STR domain_name mysql idDB1_INT domainDB1_STR cpl mysql idDB1_INT usernameDB1_STR domainDB1_STR cpl_xmlDB1_BLOB cpl_binDB1_BLOB dialog mysql idDB1_INT hash_entryDB1_INT hash_idDB1_INT callidDB1_STR from_uriDB1_STR from_tagDB1_STR to_uriDB1_STR to_tagDB1_STR caller_cseqDB1_STR callee_cseqDB1_STR caller_route_setDB1_STR callee_route_setDB1_STR caller_contactDB1_STR callee_contactDB1_STR caller_sockDB1_STR callee_sockDB1_STR stateDB1_INT start_timeDB1_INT timeoutDB1_INT sflagsDB1_INT iflagsDB1_INT toroute_nameDB1_STR req_uriDB1_STR xdataDB1_STR dialog_vars mysql idDB1_INT hash_entryDB1_INT hash_idDB1_INT dialog_keyDB1_STR dialog_valueDB1_STR dialplan mysql idDB1_INT dpidDB1_INT prDB1_INT match_opDB1_INT match_expDB1_STR match_lenDB1_INT subst_expDB1_STR repl_expDB1_STR attrsDB1_STR dispatcher mysql idDB1_INT setidDB1_INT destinationDB1_STR flagsDB1_INT priorityDB1_INT attrsDB1_STR descriptionDB1_STR domainpolicy mysql idDB1_INT ruleDB1_STR typeDB1_STR attDB1_STR valDB1_STR descriptionDB1_STR domain mysql idDB1_INT domainDB1_STR didDB1_STR last_modifiedDB1_DATETIME domain_attrs mysql idDB1_INT didDB1_STR nameDB1_STR typeDB1_INT valueDB1_STR last_modifiedDB1_DATETIME dr_gateways mysql gwidDB1_INT typeDB1_INT addressDB1_STR stripDB1_INT pri_prefixDB1_STR attrsDB1_STR descriptionDB1_STR dr_rules mysql ruleidDB1_INT groupidDB1_STR prefixDB1_STR timerecDB1_STR priorityDB1_INT routeidDB1_STR gwlistDB1_STR descriptionDB1_STR dr_gw_lists mysql idDB1_INT gwlistDB1_STR descriptionDB1_STR dr_groups mysql idDB1_INT usernameDB1_STR domainDB1_STR groupidDB1_INT descriptionDB1_STR grp mysql idDB1_INT usernameDB1_STR domainDB1_STR grpDB1_STR last_modifiedDB1_DATETIME re_grp mysql idDB1_INT reg_expDB1_STR group_idDB1_INT htable mysql idDB1_INT key_nameDB1_STR key_typeDB1_INT value_typeDB1_INT key_valueDB1_STR expiresDB1_INT imc_rooms mysql idDB1_INT nameDB1_STR domainDB1_STR flagDB1_INT imc_members mysql idDB1_INT usernameDB1_STR domainDB1_STR roomDB1_STR flagDB1_INT lcr_gw mysql idDB1_INT lcr_idDB1_INT gw_nameDB1_STR ip_addrDB1_STR hostnameDB1_STR portDB1_INT paramsDB1_STR uri_schemeDB1_INT transportDB1_INT stripDB1_INT prefixDB1_STR tagDB1_STR flagsDB1_INT defunctDB1_INT lcr_rule_target mysql idDB1_INT lcr_idDB1_INT rule_idDB1_INT gw_idDB1_INT priorityDB1_INT weightDB1_INT lcr_rule mysql idDB1_INT lcr_idDB1_INT prefixDB1_STR from_uriDB1_STR request_uriDB1_STR stopperDB1_INT enabledDB1_INT matrix mysql firstDB1_INT secondDB1_INT resDB1_INT silo mysql idDB1_INT src_addrDB1_STR dst_addrDB1_STR usernameDB1_STR domainDB1_STR inc_timeDB1_INT exp_timeDB1_INT snd_timeDB1_INT ctypeDB1_STR bodyDB1_BLOB extra_hdrsDB1_BLOB callidDB1_STR statusDB1_INT mtree mysql idDB1_INT tprefixDB1_STR tvalueDB1_STR mtrees mysql idDB1_INT tnameDB1_STR tprefixDB1_STR tvalueDB1_STR pdt mysql idDB1_INT sdomainDB1_STR prefixDB1_STR domainDB1_STR trusted mysql idDB1_INT src_ipDB1_STR protoDB1_STR from_patternDB1_STR tagDB1_STR address mysql idDB1_INT grpDB1_INT ip_addrDB1_STR maskDB1_INT portDB1_INT tagDB1_STR pl_pipes mysql idDB1_INT pipeidDB1_STR algorithmDB1_STR plimitDB1_INT presentity mysql idDB1_INT usernameDB1_STR domainDB1_STR eventDB1_STR etagDB1_STR expiresDB1_INT received_timeDB1_INT bodyDB1_BLOB senderDB1_STR active_watchers mysql idDB1_INT presentity_uriDB1_STR watcher_usernameDB1_STR watcher_domainDB1_STR to_userDB1_STR to_domainDB1_STR eventDB1_STR event_idDB1_STR to_tagDB1_STR from_tagDB1_STR callidDB1_STR local_cseqDB1_INT remote_cseqDB1_INT contactDB1_STR record_routeDB1_BLOB expiresDB1_INT statusDB1_INT reasonDB1_STR versionDB1_INT socket_infoDB1_STR local_contactDB1_STR from_userDB1_STR from_domainDB1_STR updatedDB1_INT updated_winfoDB1_INT watchers mysql idDB1_INT presentity_uriDB1_STR watcher_usernameDB1_STR watcher_domainDB1_STR eventDB1_STR statusDB1_INT reasonDB1_STR inserted_timeDB1_INT xcap mysql idDB1_INT usernameDB1_STR domainDB1_STR docDB1_BLOB doc_typeDB1_INT etagDB1_STR sourceDB1_INT doc_uriDB1_STR portDB1_INT pua mysql idDB1_INT pres_uriDB1_STR pres_idDB1_STR eventDB1_INT expiresDB1_INT desired_expiresDB1_INT flagDB1_INT etagDB1_STR tuple_idDB1_STR watcher_uriDB1_STR call_idDB1_STR to_tagDB1_STR from_tagDB1_STR cseqDB1_INT record_routeDB1_BLOB contactDB1_STR remote_contactDB1_STR versionDB1_INT extra_headersDB1_BLOB purplemap mysql idDB1_INT sip_userDB1_STR ext_userDB1_STR ext_protDB1_STR ext_passDB1_STR aliases mysql idDB1_INT ruidDB1_STR usernameDB1_STR domainDB1_STR contactDB1_STR receivedDB1_STR pathDB1_STR expiresDB1_DATETIME qDB1_DOUBLE callidDB1_STR cseqDB1_INT last_modifiedDB1_DATETIME flagsDB1_INT cflagsDB1_INT user_agentDB1_STR socketDB1_STR methodsDB1_INT instanceDB1_STR reg_idDB1_INT rls_presentity mysql idDB1_INT rlsubs_didDB1_STR resource_uriDB1_STR content_typeDB1_STR presence_stateDB1_BLOB expiresDB1_INT updatedDB1_INT auth_stateDB1_INT reasonDB1_STR rls_watchers mysql idDB1_INT presentity_uriDB1_STR to_userDB1_STR to_domainDB1_STR watcher_usernameDB1_STR watcher_domainDB1_STR eventDB1_STR event_idDB1_STR to_tagDB1_STR from_tagDB1_STR callidDB1_STR local_cseqDB1_INT remote_cseqDB1_INT contactDB1_STR record_routeDB1_BLOB expiresDB1_INT statusDB1_INT reasonDB1_STR versionDB1_INT socket_infoDB1_STR local_contactDB1_STR from_userDB1_STR from_domainDB1_STR updatedDB1_INT sca_subscriptions mysql idDB1_INT subscriberDB1_STR aorDB1_STR eventDB1_INT expiresDB1_INT stateDB1_INT app_idxDB1_INT call_idDB1_STR from_tagDB1_STR to_tagDB1_STR record_routeDB1_BLOB notify_cseqDB1_INT subscribe_cseqDB1_INT sip_trace mysql idDB1_INT time_stampDB1_DATETIME time_usDB1_INT callidDB1_STR traced_userDB1_STR msgDB1_BLOB methodDB1_STR statusDB1_STR fromipDB1_STR toipDB1_STR fromtagDB1_STR directionDB1_STR speed_dial mysql idDB1_INT usernameDB1_STR domainDB1_STR sd_usernameDB1_STR sd_domainDB1_STR new_uriDB1_STR fnameDB1_STR lnameDB1_STR descriptionDB1_STR version mysql table_nameDB1_STR table_versionDB1_INT uacreg mysql idDB1_INT l_uuidDB1_STR l_usernameDB1_STR l_domainDB1_STR r_usernameDB1_STR r_domainDB1_STR realmDB1_STR auth_usernameDB1_STR auth_passwordDB1_STR auth_proxyDB1_STR expiresDB1_INT uid_credentials mysql idDB1_INT auth_usernameDB1_STR didDB1_STR realmDB1_STR passwordDB1_STR flagsDB1_INT ha1DB1_STR ha1bDB1_STR uidDB1_STR uid_user_attrs mysql idDB1_INT uidDB1_STR nameDB1_STR valueDB1_STR typeDB1_INT flagsDB1_INT uid_domain mysql idDB1_INT didDB1_STR domainDB1_STR flagsDB1_INT uid_domain_attrs mysql idDB1_INT didDB1_STR nameDB1_STR typeDB1_INT valueDB1_STR flagsDB1_INT uid_global_attrs mysql idDB1_INT nameDB1_STR typeDB1_INT valueDB1_STR flagsDB1_INT uid_uri mysql idDB1_INT uidDB1_STR didDB1_STR usernameDB1_STR flagsDB1_INT schemeDB1_STR uid_uri_attrs mysql idDB1_INT usernameDB1_STR didDB1_STR nameDB1_STR valueDB1_STR typeDB1_INT flagsDB1_INT schemeDB1_STR uri mysql idDB1_INT usernameDB1_STR domainDB1_STR uri_userDB1_STR last_modifiedDB1_DATETIME userblacklist mysql idDB1_INT usernameDB1_STR domainDB1_STR prefixDB1_STR whitelistDB1_INT globalblacklist mysql idDB1_INT prefixDB1_STR whitelistDB1_INT descriptionDB1_STR location mysql idDB1_INT ruidDB1_STR usernameDB1_STR domainDB1_STR contactDB1_STR receivedDB1_STR pathDB1_STR expiresDB1_DATETIME qDB1_DOUBLE callidDB1_STR cseqDB1_INT last_modifiedDB1_DATETIME flagsDB1_INT cflagsDB1_INT user_agentDB1_STR socketDB1_STR methodsDB1_INT instanceDB1_STR reg_idDB1_INT location_attrs mysql idDB1_INT ruidDB1_STR usernameDB1_STR domainDB1_STR anameDB1_STR atypeDB1_INT avalueDB1_STR last_modifiedDB1_DATETIME acc show acc DB1_QUERY id method from_tag to_tag callid sip_code sip_reason time add acc DB1_INSERT method from_tag to_tag callid sip_code sip_reason time update acc DB1_UPDATE id= method from_tag to_tag callid sip_code sip_reason time delete acc DB1_DELETE id= missed_calls show missed_calls DB1_QUERY id method from_tag to_tag callid sip_code sip_reason time add missed_calls DB1_INSERT method from_tag to_tag callid sip_code sip_reason time update missed_calls DB1_UPDATE id= method from_tag to_tag callid sip_code sip_reason time delete missed_calls DB1_DELETE id= dbaliases show dbaliases DB1_QUERY id alias_username alias_domain username domain add dbaliases DB1_INSERT alias_username alias_domain username domain update dbaliases DB1_UPDATE id= alias_username alias_domain username domain delete dbaliases DB1_DELETE id= subscriber show subscriber DB1_QUERY id username domain password email_address ha1 ha1b rpid add subscriber DB1_INSERT username domain password email_address ha1 ha1b rpid update subscriber DB1_UPDATE id= username domain password email_address ha1 ha1b rpid delete subscriber DB1_DELETE id= usr_preferences show usr_preferences DB1_QUERY id uuid username domain attribute type value last_modified add usr_preferences DB1_INSERT uuid username domain attribute type value last_modified update usr_preferences DB1_UPDATE id= uuid username domain attribute type value last_modified delete usr_preferences DB1_DELETE id= carrierroute show carrierroute DB1_QUERY id carrier domain scan_prefix flags mask prob strip rewrite_host rewrite_prefix rewrite_suffix description add carrierroute DB1_INSERT carrier domain scan_prefix flags mask prob strip rewrite_host rewrite_prefix rewrite_suffix description update carrierroute DB1_UPDATE id= carrier domain scan_prefix flags mask prob strip rewrite_host rewrite_prefix rewrite_suffix description delete carrierroute DB1_DELETE id= carrierfailureroute show carrierfailureroute DB1_QUERY id carrier domain scan_prefix host_name reply_code flags mask next_domain description add carrierfailureroute DB1_INSERT carrier domain scan_prefix host_name reply_code flags mask next_domain description update carrierfailureroute DB1_UPDATE id= carrier domain scan_prefix host_name reply_code flags mask next_domain description delete carrierfailureroute DB1_DELETE id= carrier_name show carrier_name DB1_QUERY id carrier add carrier_name DB1_INSERT carrier update carrier_name DB1_UPDATE id= carrier delete carrier_name DB1_DELETE id= domain_name show domain_name DB1_QUERY id domain add domain_name DB1_INSERT domain update domain_name DB1_UPDATE id= domain delete domain_name DB1_DELETE id= cpl show cpl DB1_QUERY id username domain cpl_xml cpl_bin add cpl DB1_INSERT username domain cpl_xml cpl_bin update cpl DB1_UPDATE id= username domain cpl_xml cpl_bin delete cpl DB1_DELETE id= dialog show dialog DB1_QUERY id hash_entry hash_id callid from_uri from_tag to_uri to_tag caller_cseq callee_cseq caller_route_set callee_route_set caller_contact callee_contact caller_sock callee_sock state start_time timeout sflags iflags toroute_name req_uri xdata add dialog DB1_INSERT hash_entry hash_id callid from_uri from_tag to_uri to_tag caller_cseq callee_cseq caller_route_set callee_route_set caller_contact callee_contact caller_sock callee_sock state start_time timeout sflags iflags toroute_name req_uri xdata update dialog DB1_UPDATE id= hash_entry hash_id callid from_uri from_tag to_uri to_tag caller_cseq callee_cseq caller_route_set callee_route_set caller_contact callee_contact caller_sock callee_sock state start_time timeout sflags iflags toroute_name req_uri xdata delete dialog DB1_DELETE id= dialog_vars show dialog_vars DB1_QUERY id hash_entry hash_id dialog_key dialog_value add dialog_vars DB1_INSERT hash_entry hash_id dialog_key dialog_value update dialog_vars DB1_UPDATE id= hash_entry hash_id dialog_key dialog_value delete dialog_vars DB1_DELETE id= dialplan show dialplan DB1_QUERY id dpid pr match_op match_exp match_len subst_exp repl_exp attrs add dialplan DB1_INSERT dpid pr match_op match_exp match_len subst_exp repl_exp attrs update dialplan DB1_UPDATE id= dpid pr match_op match_exp match_len subst_exp repl_exp attrs delete dialplan DB1_DELETE id= dispatcher show dispatcher DB1_QUERY id setid destination flags priority attrs description add dispatcher DB1_INSERT setid destination flags priority attrs description update dispatcher DB1_UPDATE id= setid destination flags priority attrs description delete dispatcher DB1_DELETE id= domain show domain DB1_QUERY id domain did last_modified add domain DB1_INSERT domain did last_modified update domain DB1_UPDATE id= domain did last_modified delete domain DB1_DELETE id= domain_attrs show domain_attrs DB1_QUERY id did name type value last_modified add domain_attrs DB1_INSERT did name type value last_modified update domain_attrs DB1_UPDATE id= did name type value last_modified delete domain_attrs DB1_DELETE id= domainpolicy show domainpolicy DB1_QUERY id rule type att val description add domainpolicy DB1_INSERT rule type att val description update domainpolicy DB1_UPDATE id= rule type att val description delete domainpolicy DB1_DELETE id= dr_gateways show dr_gateways DB1_QUERY gwid type address strip pri_prefix attrs description add dr_gateways DB1_INSERT type address strip pri_prefix attrs description update dr_gateways DB1_UPDATE gwid= type address strip pri_prefix attrs description delete dr_gateways DB1_DELETE gwid= dr_rules show dr_rules DB1_QUERY ruleid groupid prefix timerec priority routeid gwlist description add dr_rules DB1_INSERT groupid prefix timerec priority routeid gwlist description update dr_rules DB1_UPDATE ruleid= groupid prefix timerec priority routeid gwlist description delete dr_rules DB1_DELETE ruleid= dr_gw_lists show dr_gw_lists DB1_QUERY id gwlist description add dr_gw_lists DB1_INSERT gwlist description update dr_gw_lists DB1_UPDATE id= gwlist description delete dr_gw_lists DB1_DELETE id= dr_groups show dr_groups DB1_QUERY id username domain groupid description add dr_groups DB1_INSERT username domain groupid description update dr_groups DB1_UPDATE id= username domain groupid description delete dr_groups DB1_DELETE id= grp show grp DB1_QUERY id username domain grp last_modified add grp DB1_INSERT username domain grp last_modified update grp DB1_UPDATE id= username domain grp last_modified delete grp DB1_DELETE id= re_grp show re_grp DB1_QUERY id reg_exp group_id add re_grp DB1_INSERT reg_exp group_id update re_grp DB1_UPDATE id= reg_exp group_id delete re_grp DB1_DELETE id= htable show htable DB1_QUERY id key_name key_type value_type key_value expires add htable DB1_INSERT key_name key_type value_type key_value expires update htable DB1_UPDATE id= key_name key_type value_type key_value expires delete htable DB1_DELETE id= imc_rooms show imc_rooms DB1_QUERY id name domain flag add imc_rooms DB1_INSERT name domain flag update imc_rooms DB1_UPDATE id= name domain flag delete imc_rooms DB1_DELETE id= imc_members show imc_members DB1_QUERY id username domain room flag add imc_members DB1_INSERT username domain room flag update imc_members DB1_UPDATE id= username domain room flag delete imc_members DB1_DELETE id= lcr_gw show lcr_gw DB1_QUERY id lcr_id gw_name ip_addr hostname port params uri_scheme transport strip prefix tag flags defunct add lcr_gw DB1_INSERT lcr_id gw_name ip_addr hostname port params uri_scheme transport strip prefix tag flags defunct update lcr_gw DB1_UPDATE id= lcr_id gw_name ip_addr hostname port params uri_scheme transport strip prefix tag flags defunct delete lcr_gw DB1_DELETE id= lcr_rule_target show lcr_rule_target DB1_QUERY id lcr_id rule_id gw_id priority weight add lcr_rule_target DB1_INSERT lcr_id rule_id gw_id priority weight update lcr_rule_target DB1_UPDATE id= lcr_id rule_id gw_id priority weight delete lcr_rule_target DB1_DELETE id= lcr_rule show lcr_rule DB1_QUERY id lcr_id prefix from_uri request_uri stopper enabled add lcr_rule DB1_INSERT lcr_id prefix from_uri request_uri stopper enabled update lcr_rule DB1_UPDATE id= lcr_id prefix from_uri request_uri stopper enabled delete lcr_rule DB1_DELETE id= matrix show matrix DB1_QUERY first second res add matrix DB1_INSERT first second res silo show silo DB1_QUERY id src_addr dst_addr username domain inc_time exp_time snd_time ctype body extra_hdrs callid status add silo DB1_INSERT src_addr dst_addr username domain inc_time exp_time snd_time ctype body extra_hdrs callid status update silo DB1_UPDATE id= src_addr dst_addr username domain inc_time exp_time snd_time ctype body extra_hdrs callid status delete silo DB1_DELETE id= mtree show mtree DB1_QUERY id tprefix tvalue add mtree DB1_INSERT tprefix tvalue update mtree DB1_UPDATE id= tprefix tvalue delete mtree DB1_DELETE id= mtrees show mtrees DB1_QUERY id tname tprefix tvalue add mtrees DB1_INSERT tname tprefix tvalue update mtrees DB1_UPDATE id= tname tprefix tvalue delete mtrees DB1_DELETE id= pdt show pdt DB1_QUERY id sdomain prefix domain add pdt DB1_INSERT sdomain prefix domain update pdt DB1_UPDATE id= sdomain prefix domain delete pdt DB1_DELETE id= trusted show trusted DB1_QUERY id src_ip proto from_pattern tag add trusted DB1_INSERT src_ip proto from_pattern tag update trusted DB1_UPDATE id= src_ip proto from_pattern tag delete trusted DB1_DELETE id= address show address DB1_QUERY id grp ip_addr mask port tag add address DB1_INSERT grp ip_addr mask port tag update address DB1_UPDATE id= grp ip_addr mask port tag delete address DB1_DELETE id= pl_pipes show pl_pipes DB1_QUERY id pipeid algorithm plimit add pl_pipes DB1_INSERT pipeid algorithm plimit update pl_pipes DB1_UPDATE id= pipeid algorithm plimit delete pl_pipes DB1_DELETE id= presentity show presentity DB1_QUERY id username domain event etag expires received_time body sender add presentity DB1_INSERT username domain event etag expires received_time body sender update presentity DB1_UPDATE id= username domain event etag expires received_time body sender delete presentity DB1_DELETE id= active_watchers show active_watchers DB1_QUERY id presentity_uri watcher_username watcher_domain to_user to_domain event event_id to_tag from_tag callid local_cseq remote_cseq contact record_route expires status reason version socket_info local_contact from_user from_domain updated updated_winfo add active_watchers DB1_INSERT presentity_uri watcher_username watcher_domain to_user to_domain event event_id to_tag from_tag callid local_cseq remote_cseq contact record_route expires status reason version socket_info local_contact from_user from_domain updated updated_winfo update active_watchers DB1_UPDATE id= presentity_uri watcher_username watcher_domain to_user to_domain event event_id to_tag from_tag callid local_cseq remote_cseq contact record_route expires status reason version socket_info local_contact from_user from_domain updated updated_winfo delete active_watchers DB1_DELETE id= watchers show watchers DB1_QUERY id presentity_uri watcher_username watcher_domain event status reason inserted_time add watchers DB1_INSERT presentity_uri watcher_username watcher_domain event status reason inserted_time update watchers DB1_UPDATE id= presentity_uri watcher_username watcher_domain event status reason inserted_time delete watchers DB1_DELETE id= xcap show xcap DB1_QUERY id username domain doc doc_type etag source doc_uri port add xcap DB1_INSERT username domain doc doc_type etag source doc_uri port update xcap DB1_UPDATE id= username domain doc doc_type etag source doc_uri port delete xcap DB1_DELETE id= pua show pua DB1_QUERY id pres_uri pres_id event expires desired_expires flag etag tuple_id watcher_uri call_id to_tag from_tag cseq record_route contact remote_contact version extra_headers add pua DB1_INSERT pres_uri pres_id event expires desired_expires flag etag tuple_id watcher_uri call_id to_tag from_tag cseq record_route contact remote_contact version extra_headers update pua DB1_UPDATE id= pres_uri pres_id event expires desired_expires flag etag tuple_id watcher_uri call_id to_tag from_tag cseq record_route contact remote_contact version extra_headers delete pua DB1_DELETE id= purplemap show purplemap DB1_QUERY id sip_user ext_user ext_prot ext_pass add purplemap DB1_INSERT sip_user ext_user ext_prot ext_pass update purplemap DB1_UPDATE id= sip_user ext_user ext_prot ext_pass delete purplemap DB1_DELETE id= aliases show aliases DB1_QUERY id ruid username domain contact received path expires q callid cseq last_modified flags cflags user_agent socket methods instance reg_id add aliases DB1_INSERT ruid username domain contact received path expires q callid cseq last_modified flags cflags user_agent socket methods instance reg_id update aliases DB1_UPDATE id= ruid username domain contact received path expires q callid cseq last_modified flags cflags user_agent socket methods instance reg_id delete aliases DB1_DELETE id= rls_presentity show rls_presentity DB1_QUERY id rlsubs_did resource_uri content_type presence_state expires updated auth_state reason add rls_presentity DB1_INSERT rlsubs_did resource_uri content_type presence_state expires updated auth_state reason update rls_presentity DB1_UPDATE id= rlsubs_did resource_uri content_type presence_state expires updated auth_state reason delete rls_presentity DB1_DELETE id= rls_watchers show rls_watchers DB1_QUERY id presentity_uri to_user to_domain watcher_username watcher_domain event event_id to_tag from_tag callid local_cseq remote_cseq contact record_route expires status reason version socket_info local_contact from_user from_domain updated add rls_watchers DB1_INSERT presentity_uri to_user to_domain watcher_username watcher_domain event event_id to_tag from_tag callid local_cseq remote_cseq contact record_route expires status reason version socket_info local_contact from_user from_domain updated update rls_watchers DB1_UPDATE id= presentity_uri to_user to_domain watcher_username watcher_domain event event_id to_tag from_tag callid local_cseq remote_cseq contact record_route expires status reason version socket_info local_contact from_user from_domain updated delete rls_watchers DB1_DELETE id= sca_subscriptions show sca_subscriptions DB1_QUERY id subscriber aor event expires state app_idx call_id from_tag to_tag record_route notify_cseq subscribe_cseq add sca_subscriptions DB1_INSERT subscriber aor event expires state app_idx call_id from_tag to_tag record_route notify_cseq subscribe_cseq update sca_subscriptions DB1_UPDATE id= subscriber aor event expires state app_idx call_id from_tag to_tag record_route notify_cseq subscribe_cseq delete sca_subscriptions DB1_DELETE id= sip_trace show sip_trace DB1_QUERY id time_stamp time_us callid traced_user msg method status fromip toip fromtag direction add sip_trace DB1_INSERT time_stamp time_us callid traced_user msg method status fromip toip fromtag direction update sip_trace DB1_UPDATE id= time_stamp time_us callid traced_user msg method status fromip toip fromtag direction delete sip_trace DB1_DELETE id= speed_dial show speed_dial DB1_QUERY id username domain sd_username sd_domain new_uri fname lname description add speed_dial DB1_INSERT username domain sd_username sd_domain new_uri fname lname description update speed_dial DB1_UPDATE id= username domain sd_username sd_domain new_uri fname lname description delete speed_dial DB1_DELETE id= version show version DB1_QUERY table_name table_version add version DB1_INSERT table_name table_version uacreg show uacreg DB1_QUERY id l_uuid l_username l_domain r_username r_domain realm auth_username auth_password auth_proxy expires add uacreg DB1_INSERT l_uuid l_username l_domain r_username r_domain realm auth_username auth_password auth_proxy expires update uacreg DB1_UPDATE id= l_uuid l_username l_domain r_username r_domain realm auth_username auth_password auth_proxy expires delete uacreg DB1_DELETE id= uid_credentials show uid_credentials DB1_QUERY id auth_username did realm password flags ha1 ha1b uid add uid_credentials DB1_INSERT auth_username did realm password flags ha1 ha1b uid update uid_credentials DB1_UPDATE id= auth_username did realm password flags ha1 ha1b uid delete uid_credentials DB1_DELETE id= uid_user_attrs show uid_user_attrs DB1_QUERY id uid name value type flags add uid_user_attrs DB1_INSERT uid name value type flags update uid_user_attrs DB1_UPDATE id= uid name value type flags delete uid_user_attrs DB1_DELETE id= uid_domain show uid_domain DB1_QUERY id did domain flags add uid_domain DB1_INSERT did domain flags update uid_domain DB1_UPDATE id= did domain flags delete uid_domain DB1_DELETE id= uid_domain_attrs show uid_domain_attrs DB1_QUERY id did name type value flags add uid_domain_attrs DB1_INSERT did name type value flags update uid_domain_attrs DB1_UPDATE id= did name type value flags delete uid_domain_attrs DB1_DELETE id= uid_global_attrs show uid_global_attrs DB1_QUERY id name type value flags add uid_global_attrs DB1_INSERT name type value flags update uid_global_attrs DB1_UPDATE id= name type value flags delete uid_global_attrs DB1_DELETE id= uid_uri show uid_uri DB1_QUERY id uid did username flags scheme add uid_uri DB1_INSERT uid did username flags scheme update uid_uri DB1_UPDATE id= uid did username flags scheme delete uid_uri DB1_DELETE id= uid_uri_attrs show uid_uri_attrs DB1_QUERY id username did name value type flags scheme add uid_uri_attrs DB1_INSERT username did name value type flags scheme update uid_uri_attrs DB1_UPDATE id= username did name value type flags scheme delete uid_uri_attrs DB1_DELETE id= uri show uri DB1_QUERY id username domain uri_user last_modified add uri DB1_INSERT username domain uri_user last_modified update uri DB1_UPDATE id= username domain uri_user last_modified delete uri DB1_DELETE id= userblacklist show userblacklist DB1_QUERY id username domain prefix whitelist add userblacklist DB1_INSERT username domain prefix whitelist update userblacklist DB1_UPDATE id= username domain prefix whitelist delete userblacklist DB1_DELETE id= globalblacklist show globalblacklist DB1_QUERY id prefix whitelist description add globalblacklist DB1_INSERT prefix whitelist description update globalblacklist DB1_UPDATE id= prefix whitelist description delete globalblacklist DB1_DELETE id= location show location DB1_QUERY id ruid username domain contact received path expires q callid cseq last_modified flags cflags user_agent socket methods instance reg_id add location DB1_INSERT ruid username domain contact received path expires q callid cseq last_modified flags cflags user_agent socket methods instance reg_id update location DB1_UPDATE id= ruid username domain contact received path expires q callid cseq last_modified flags cflags user_agent socket methods instance reg_id delete location DB1_DELETE id= location_attrs show location_attrs DB1_QUERY id ruid username domain aname atype avalue last_modified add location_attrs DB1_INSERT ruid username domain aname atype avalue last_modified update location_attrs DB1_UPDATE id= ruid username domain aname atype avalue last_modified delete location_attrs DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/uid_avp_db-mod0000644000000000000000000000261312223032460021045 0ustar rootroot uid_user_attrs show uid_user_attrs DB1_QUERY id uid name value type flags add uid_user_attrs DB1_INSERT uid name value type flags update uid_user_attrs DB1_UPDATE id= uid name value type flags delete uid_user_attrs DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/pi_framework-010000644000000000000000000000247312223032460021103 0ustar rootroot kamailio-4.0.4/utils/kamctl/xhttp_pi/purple-table0000644000000000000000000000071112223032460020565 0ustar rootroot purplemap mysql idDB1_INT sip_userDB1_STR ext_userDB1_STR ext_protDB1_STR ext_passDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/userblacklist-mod0000644000000000000000000000504612223032460021623 0ustar rootroot userblacklist show userblacklist DB1_QUERY id username domain prefix whitelist add userblacklist DB1_INSERT username domain prefix whitelist update userblacklist DB1_UPDATE id= username domain prefix whitelist delete userblacklist DB1_DELETE id= globalblacklist show globalblacklist DB1_QUERY id prefix whitelist description add globalblacklist DB1_INSERT prefix whitelist description update globalblacklist DB1_UPDATE id= prefix whitelist description delete globalblacklist DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/uid_uri_db-mod0000644000000000000000000000572612223032460021066 0ustar rootroot uid_uri show uid_uri DB1_QUERY id uid did username flags scheme add uid_uri DB1_INSERT uid did username flags scheme update uid_uri DB1_UPDATE id= uid did username flags scheme delete uid_uri DB1_DELETE id= uid_uri_attrs show uid_uri_attrs DB1_QUERY id username did name value type flags scheme add uid_uri_attrs DB1_INSERT username did name value type flags scheme update uid_uri_attrs DB1_UPDATE id= username did name value type flags scheme delete uid_uri_attrs DB1_DELETE id= kamailio-4.0.4/utils/kamctl/xhttp_pi/pi_framework-020000644000000000000000000000001512223032460021072 0ustar rootroot kamailio-4.0.4/utils/kamctl/xhttp_pi/avpops-table0000644000000000000000000000122612223032460020570 0ustar rootroot usr_preferences mysql idDB1_INT uuidDB1_STR usernameDB1_STR domainDB1_STR attributeDB1_STR typeDB1_INT valueDB1_STR last_modifiedDB1_DATETIME kamailio-4.0.4/utils/kamctl/xhttp_pi/userblacklist-table0000644000000000000000000000156012223032460022130 0ustar rootroot userblacklist mysql idDB1_INT usernameDB1_STR domainDB1_STR prefixDB1_STR whitelistDB1_INT globalblacklist mysql idDB1_INT prefixDB1_STR whitelistDB1_INT descriptionDB1_STR kamailio-4.0.4/utils/kamctl/xhttp_pi/htable-table0000644000000000000000000000100112223032460020506 0ustar rootroot htable mysql idDB1_INT key_nameDB1_STR key_typeDB1_INT value_typeDB1_INT key_valueDB1_STR expiresDB1_INT kamailio-4.0.4/utils/kamctl/kamctl.sqlbase0000644000000000000000000001111412223032461017236 0ustar rootroot# # $Id$ # # control tool for maintaining Kamailio # #=================================================================== ##### ----------------------------------------------- ##### ### common variables and functions for SQL engines if [ -z "$DBNAME" ] ; then DBNAME=kamailio fi if [ -z "$DBHOST" ] ; then DBHOST=localhost fi if [ -z "$DBRWUSER" ] ; then DBRWUSER=kamailio fi # the read-only user for whom password may be stored here if [ -z "$DBROUSER" ] ; then DBROUSER=kamailioro fi if [ -z "$DBROPW" ] ; then DBROPW=kamailioro fi # full privileges SQL user if [ -z "$DBROOTUSER" ]; then DBROOTUSER="root" fi #params: none # output: DBRWPW prompt_pw() { if [ -z "$DBRWPW" ] ; then savetty=`stty -g` if [ -z "$1" ] ; then printf "Password: " > /dev/stderr else printf "$1: " > /dev/stderr fi stty -echo read DBRWPW stty $savetty echo fi } # dbtext don't support db_ops usage_db_ops() { echo mecho " -- command 'db' - database operations" echo cat < ..................... execute SQL query db roexec ................. execute read-only SQL query db run ......................... execute SQL query from \$id variable db rorun ....................... execute read-only SQL query from \$id variable db show ..................... display table content db showg
.................... display formatted table content EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_db_ops" # speeddial not implemented for dbtext usage_speeddial() { echo mecho " -- command 'speeddial' - manage speed dials (short numbers)" echo cat < ....... show speeddial details speeddial list ............. list speeddial for uri speeddial add [] ... ........................... add a speedial (*) speeddial rm ....... remove a speeddial (*) speeddial help ...................... help message - , must be an AoR (username@domain) - must be an AoR (username@domain) - must be a SIP AoR (sip:username@domain) - a description for speeddial EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_speeddial" # avp operations not implemented for dbtext usage_avp() { echo mecho " -- command 'avp' - manage AVPs" echo cat <] [-a attribute] [-v value] [-t type] ... list AVPs avp add [-T table] ............ add AVP (*) avp rm [-T table] [-u ] [-a attribute] [-v value] [-t type] ... remove AVP (*) avp help .................................. help message - -T - table name - -u - SIP id or unique id - -a - AVP name - -v - AVP value - -t - AVP name and type (0 (str:str), 1 (str:int), 2 (int:str), 3 (int:int)) - must be an AoR (username@domain) - must be a string but not AoR EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_avp" # alias_db not implemented for dbtext usage_alias_db() { echo mecho " -- command 'alias_db' - manage database aliases" echo cat < .............. show alias details alias_db list ............. list aliases for uri alias_db add ...... add an alias (*) alias_db rm ................ remove an alias (*) alias_db help ...................... help message - must be an AoR (username@domain)" - must be an AoR (username@domain)" EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_alias_db" usage_domain() { echo mecho " -- command 'domain' - manage local domains" echo cat < ................. add the domain to the database domain rm .................. delete the domain from the database EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_domain" uid_usage_domain() { echo mecho " -- command 'uid_domain' - manage local domains" echo cat < [did] [flags].... add the domain to the database uid_domain rm .................. delete the domain from the database EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS uid_usage_domain" kamailio-4.0.4/utils/kamctl/kamctlrc0000644000000000000000000000723512223032461016143 0ustar rootroot# $Id$ # # The Kamailio configuration file for the control tools. # # Here you can set variables used in the kamctl and kamdbctl setup # scripts. Per default all variables here are commented out, the control tools # will use their internal default values. ## your SIP domain # SIP_DOMAIN=kamailio.org ## chrooted directory # $CHROOT_DIR="/path/to/chrooted/directory" ## database type: MYSQL, PGSQL, ORACLE, DB_BERKELEY, DBTEXT, or SQLITE # by default none is loaded # # If you want to setup a database with kamdbctl, you must at least specify # this parameter. # DBENGINE=MYSQL ## database host # DBHOST=localhost ## database name (for ORACLE this is TNS name) # DBNAME=kamailio # database path used by dbtext, db_berkeley or sqlite # DB_PATH="/usr/local/etc/kamailio/dbtext" ## database read/write user # DBRWUSER="kamailio" ## password for database read/write user # DBRWPW="kamailiorw" ## database read only user # DBROUSER="kamailioro" ## password for database read only user # DBROPW="kamailioro" ## database access host (from where is kamctl used) # DBACCESSHOST=192.168.0.1 ## database super user (for ORACLE this is 'scheme-creator' user) # DBROOTUSER="root" # user name column # USERCOL="username" # SQL definitions # If you change this definitions here, then you must change them # in db/schema/entities.xml too. # FIXME # FOREVER="2030-05-28 21:32:15" # DEFAULT_ALIASES_EXPIRES=$FOREVER # DEFAULT_Q="1.0" # DEFAULT_CALLID="Default-Call-ID" # DEFAULT_CSEQ="13" # DEFAULT_LOCATION_EXPIRES=$FOREVER # Program to calculate a message-digest fingerprint # MD5="md5sum" # awk tool # AWK="awk" # If you use a system with a grep and egrep that is not 100% gnu grep compatible, # e.g. solaris, install the gnu grep (ggrep) and specify this below. # # grep tool # GREP="grep" # egrep tool # EGREP="egrep" # sed tool # SED="sed" # tail tool # LAST_LINE="tail -n 1" # expr tool # EXPR="expr" # Describe what additional tables to install. Valid values for the variables # below are yes/no/ask. With ask (default) it will interactively ask the user # for an answer, while yes/no allow for automated, unassisted installs. # # If to install tables for the modules in the EXTRA_MODULES variable. # INSTALL_EXTRA_TABLES=ask # If to install presence related tables. # INSTALL_PRESENCE_TABLES=ask # Define what module tables should be installed. # If you use the postgres database and want to change the installed tables, then you # must also adjust the STANDARD_TABLES or EXTRA_TABLES variable accordingly in the # kamdbctl.base script. # Kamailio standard modules # STANDARD_MODULES="standard acc lcr domain group permissions registrar usrloc msilo # alias_db uri_db speeddial avpops auth_db pdt dialog dispatcher # dialplan" # Kamailio extra modules # EXTRA_MODULES="imc cpl siptrace domainpolicy carrierroute userblacklist htable purple sca" ## type of aliases used: DB - database aliases; UL - usrloc aliases ## - default: none # ALIASES_TYPE="DB" ## control engine: FIFO or UNIXSOCK ## - default FIFO # CTLENGINE="FIFO" ## path to FIFO file # OSER_FIFO="FIFO" ## check ACL names; default on (1); off (0) # VERIFY_ACL=1 ## ACL names - if VERIFY_ACL is set, only the ACL names from below list ## are accepted # ACL_GROUPS="local ld int voicemail free-pstn" ## verbose - debug purposes - default '0' # VERBOSE=1 ## do (1) or don't (0) store plaintext passwords ## in the subscriber table - default '1' # STORE_PLAINTEXT_PW=0 ## Kamailio START Options ## PID file path - default is: /var/run/kamailio.pid # PID_FILE=/var/run/kamailio.pid ## Extra start options - default is: not set # example: start Kamailio with 64MB share memory: STARTOPTIONS="-m 64" # STARTOPTIONS= kamailio-4.0.4/utils/kamctl/kamctl.db_berkeley0000644000000000000000000000242212223032460020054 0ustar rootroot# $Id$ # # control tool for maintaining Kamailio # # History: # -------- # 2007-11-05 genesis (wiquan) #=================================================================== # path to the db_berkeley directory if [ -z "$DB_PATH" ] ; then DB_PATH="/usr/local/share/kamailio/db_berkeley/kamailio" fi #=================================================================== kamailio_bdb() { case $1 in reload) shift if [ "$#" -lt 1 ] ; then merr "reload - too few parameters" exit 1 fi $CTLCMD bdb_reload $1 exit $? ;; *) usage exit 1 ;; esac } # domain don't support reload at the moment usage_domain() { echo mecho " -- command 'domain' - manage domains" echo cat < ............ add a new served domain domain rm ............. remove a served domain EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_domain" # showdb is not implemented for SQL databases usage_showdb() { echo mecho " -- command 'showdb|userdb' - dump offline users" echo cat <] .......... creates new rootCA tls userCERT [] ... creates user certificate default is $ETCDIR/tls EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_base" usage_acl() { echo mecho " -- command 'acl' - manage access control lists (acl)" echo cat <] .............. show user membership acl grant ....... grant user membership (*) acl revoke [] .... grant user membership(s) (*) EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_acl" usage_lcr() { echo mecho " -- command 'lcr' - manage least cost routes (lcr)" echo cat < .......................... add a carrier name cr rmcn ......................................... rm a carrier name cr adddn ............................ add a domain name cr rmdn .......................................... rm a domain name cr addcarrier ................ ............... .........................add a carrier (prob, strip, rewrite_prefix, rewrite_suffix,................... flags, mask and comment are optional arguments) ............... cr rmcarrier ................ rm a carrier EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_cr" usage_rpid() { echo mecho " -- command 'rpid' - manage Remote-Party-ID (RPID)" echo cat < ......... add rpid for a user (*) rpid rm ................. set rpid to NULL for a user (*) rpid show ............... show rpid of a user EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_rpid" usage_subscriber() { echo mecho " -- command 'add|passwd|rm' - manage subscribers" echo cat < .......... add a new subscriber (*) passwd ......... change user's password (*) rm ...................... delete a user (*) EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_subscriber" usage_trusted() { echo mecho " -- command 'add|dump|reload|rm|show' - manage trusted" echo cat < ....................... add a new entry ....................... (from_pattern and tag are optional arguments) trusted rm ............... remove all entries for the given src_ip EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_trusted" usage_address() { echo mecho " -- command 'add|dump|reload|rm|show' - manage address" echo cat < ....................... add a new entry ....................... (mask, port and tag are optional arguments) address rm ......... remove entries for given grp and ipaddr EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_address" usage_dispatcher() { echo mecho " -- command 'dispatcher' - manage dispatcher" echo cat < .......................... add gateway dispatcher rmgw ................ delete gateway EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_dispatcher" usage_dialplan() { echo mecho " -- command 'dialplan' - manage dialplans" echo cat < .............. show dialplan tables dialplan reload ................... reload dialplan tables dialplan addrule .................... add a rule dialplan rm ....................... removes the entire dialplan table dialplan rmdpid ............ removes all the gived dpid entries dialplan rmrule ..... removes all the gived dpid/prio entries EOF } ##### ----------------------------------------------- ##### #### Common functions mdbg() { if [ "0$VERBOSE" -ne 0 ] ; then if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e "\033[1m$1\033[0m" else echo "$1" fi fi } mwarn() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e '\E[37;32m'"\033[1mWARNING: $1\033[0m" else echo "** WARNING: $1" fi } minfo() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e '\E[37;33m'"\033[1mINFO: $1\033[0m" else echo "** INFO: $1" fi } mecho() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e "\033[1m$1\033[0m" else echo "$1" fi } merr() { if [ -t 1 -a -z "$NOHLPRINT" ] ; then echo -e '\E[37;31m'"\033[1mERROR: $1\033[0m" else echo "** ERROR: $1" fi } # determine host name, typically for use in printing UAC # messages; we use today a simplistic but portable uname -n way -- # no domain name is displayed ; fifo_uac expands !! to host # address only for optional header fields; uname output without # domain is sufficient for informational header fields such as # From # get_my_host() { if [ -z "$SIP_DOMAIN" ]; then uname -n else echo "$SIP_DOMAIN" fi } # calculate name and domain of current user set_user() { OSERUSER=`echo $1|$AWK -F@ '{print $1}'` OSERDOMAIN=`echo $1|$AWK -F@ '{print $2}'` if [ -z "$OSERDOMAIN" ] ; then OSERDOMAIN="$SIP_DOMAIN" fi if [ -z "$OSERDOMAIN" ] ; then merr "domain unknown: use usernames with domain or set default domain \ in SIP_DOMAIN" exit 1 fi } # check the parameter if it is a valid address of record (user@domain) check_aor() { echo "$1" | $EGREP "^$USERNAME_RE@.*\..*" >/dev/null if [ $? -ne 0 ] ; then echo "error: invalid AoR: $1" > /dev/stderr exit 1 fi } # check the parameter if it is a valid address of record (user@domain) is_aor() { echo "$1" | $EGREP "^$USERNAME_RE@.*\..*" >/dev/null if [ $? -ne 0 ] ; then false else true fi } # check the parameter if it is a valid SIP address of record (sip:user@domain) check_sipaor() { echo "$1" | $EGREP "^sip(s)?:$USERNAME_RE@.*\..*" >/dev/null if [ $? -ne 0 ] ; then echo "error: invalid SIP AoR: $1" > /dev/stderr exit 1 fi } # check the parameter if it is a valid SIP URI # quite simplified now -- it captures just very basic # errors check_uri() { echo "$1" | $EGREP "^sip(s)?:($USERNAME_RE@)?.*\..*" > /dev/null if [ $? -ne 0 ] ; then echo "error: invalid SIP URI: $1" > /dev/stderr exit 1 fi } print_status() { echo $1 | $EGREP "^[1-6][0-9][0-9]" > /dev/null if [ "$?" -eq 0 ] ; then echo $1 else echo "200 OK" fi } # process output from FIFO/Unixsock server; if everything is ok # skip the first "ok" line and proceed to returned # parameters filter_fl() { # tail +2 $AWK 'BEGIN {line=0;IGNORECASE=1;} {line++} NR == 1 && /^200 OK/ { next } /^$/ { next } { print }' } # params: user, realm, password # output: HA1 _gen_ha1() { HA1=`echo -n "$1:$2:$3" | $MD5 | $AWK '{ print $1 }'` if [ $? -ne 0 ] ; then echo "HA1 calculation failed" exit 1 fi } # params: user, realm, password # output: HA1B _gen_ha1b() { HA1B=`echo -n "$1@$2:$2:$3" | $MD5 | $AWK '{ print $1 }'` if [ $? -ne 0 ] ; then echo "HA1B calculation failed" exit 1 fi } # params: user, realm, password # output: PHPLIB_ID _gen_phplib_id() { NOW=`date`; PHPLIB_ID=`echo -n "$1$2:$3:$NOW" | $MD5 | $AWK '{ print $1 }'` } # params: user, password # output: HA1, HA1B credentials() { set_user $1 _gen_ha1 "$OSERUSER" "$OSERDOMAIN" "$2" _gen_ha1b "$OSERUSER" "$OSERDOMAIN" "$2" } kamailio-4.0.4/utils/kamctl/kamctl.ser_mi0000644000000000000000000000343312223032460017066 0ustar rootroot# # $Id$ # # control tool for maintaining Kamailio # #=================================================================== ##### ----------------------------------------------- ##### ### FIFO specific variables and functions # ##### ----------------------------------------------- ##### ### load CTL base # if [ -f "$MYLIBDIR/kamctl.ctlbase" ]; then . "$MYLIBDIR/kamctl.ctlbase" else mwarn "Cannot load CTL core functions '$MYLIBDIR/kamctl.ctlbase' ..." # exit -1 fi # ##### ----------------------------------------------- ##### ### parameters # # ##### ----------------------------------------------- ##### ### functions # usage_ser_mi() { echo mecho " -- command 'kamcmd_mi'" echo cat < 2 and col_split[1] != 'AS': raise ParseError('multiple columns must be separated by a comma') elif len(col_split) == 3: if col_split[1] != 'AS': raise ParseError('Invalid column alias, use AS') my_key = self._ReplaceStringLiterals(col_split[2], quotes=True) my_val = self._ReplaceStringLiterals(col_split[0], quotes=True) self.aliases[my_key] = [my_val] self.columns.append(my_key) elif len(col_split) > 3: raise ParseError('multiple columns must be separated by a comma') elif len(col_split) == 2: # alias my_key = self._ReplaceStringLiterals(col_split[1], quotes=True) my_val = self._ReplaceStringLiterals(col_split[0], quotes=True) self.aliases[my_key] = [my_val] self.columns.append(my_key) else: col = self._ReplaceStringLiterals(col, quotes=True).strip() if not col: # col == '' raise ParseError('empty column name not allowed') self.columns.append(col) # pop off all the columns related junk self.tokens = self.tokens[col_end + 1:] Debug('Columns: %s' % self.columns) Debug('Aliases: %s' % self.aliases) Debug('self.tokens: %s' % self.tokens) def _ParseColumnsConcatHelper(self, col_split): """Handles the columns being CONCAT'd together. Args: col_split: ['column', 'column'] Raises: ParseError: invalid CONCAT() """ concat_placeholder = '_' split_len = len(col_split) if split_len == 1: raise ParseError('CONCAT() must be followed by column name[s]') if not col_split[1].startswith(self._paren_placeholder): raise ParseError('CONCAT must be followed by ()') if split_len > 2: if split_len == 4 and col_split[2] != 'AS': raise ParseError('CONCAT() must be followed by an AS clause') if split_len > 5: raise ParseError('CONCAT() AS clause takes exactly 1 arg. ' 'Extra args: [%s]' % (col_split[4:])) else: concat_placeholder = self._ReplaceStringLiterals(col_split[-1], quotes=True) # make sure this place hodler is unique while concat_placeholder in self.aliases: concat_placeholder += '_' concat_cols_str = self._ReplaceParens(col_split[1]) concat_cols = concat_cols_str.split(',') concat_col_list = [] for concat_col in concat_cols: if ' ' in concat_col.strip(): raise ParseError('multiple columns must be separated by a' ' comma inside CONCAT()') concat_col = self._ReplaceStringLiterals(concat_col, quotes=True).strip() if not concat_col: raise ParseError('Attempting to CONCAT empty set') concat_col_list.append(concat_col) self.aliases[concat_placeholder] = concat_col_list self.columns.append(concat_placeholder) def _ParseTable(self): """Parse out the table name (multiple table names not supported). Raises: ParseError: Unable to parse table name """ table_name = '' if (not self.tokens or # len == 0 (self.tokens[0] in self.RESERVED_WORDS and self.tokens[0] not in ['FROM', 'INTO'])): raise ParseError('Missing table name') # SELECT if self.command == 'SELECT': table_name = self.tokens.pop(0) # INSERT elif self.command == 'INSERT': table_name = self.tokens.pop(0) if table_name == 'INTO': table_name = self.tokens.pop(0) # DELETE elif self.command == 'DELETE': if self.tokens[0] != 'FROM': raise ParseError('DELETE command must be followed by FROM') self.tokens.pop(0) # FROM table_name = self.tokens.pop(0) # UPDATE elif self.command == 'UPDATE': table_name = self.tokens.pop(0) if not self.table: self.table = table_name else: # multiple queries detected, make sure they're against same table if self.table != table_name: raise ParseError('Table changed between queries! %s -> %s' % (self.table, table_name)) Debug('Table is [%s]' % self.table) Debug('self.tokens is %s' % self.tokens) def _ParseTargets(self): """Parse out name value pairs of columns and their values. Raises: ParseError: Unable to parse targets """ self.targets = {} # UPDATE if self.command == 'UPDATE': if self.tokens.pop(0) != 'SET': raise ParseError('UPDATE command must be followed by SET') self.targets = self._ParsePairs(' '.join(self.tokens), ',') # INSERT if self.command == 'INSERT': if self.tokens[0] == 'SET': self.targets = self._ParsePairs(' '.join(self.tokens[1:]), ',') elif len(self.tokens) == 3 and self.tokens[1] == 'VALUES': if not self.tokens[0].startswith(self._paren_placeholder): raise ParseError('INSERT column names must be inside parens') if not self.tokens[2].startswith(self._paren_placeholder): raise ParseError('INSERT values must be inside parens') cols = self._ReplaceParens(self.tokens[0]).split(',') vals = self._ReplaceParens(self.tokens[2]).split(',') if len(cols) != len(vals): raise ParseError('INSERT column and value numbers must match') if not cols: # len == 0 raise ParseError('INSERT column number must be greater than 0') i = 0 while i < len(cols): val = vals[i].strip() if not val: # val == '' raise ParseError('INSERT values cannot be empty') if ' ' in val: raise ParseError('INSERT values must be comma separated') self.targets[cols[i].strip()] = self._ReplaceStringLiterals(val) i += 1 else: raise ParseError('Unable to parse INSERT targets') for target in self.targets: self.targets[target] = self._EscapeChars(self.targets[target]) Debug('Targets are [%s]' % self.targets) def _EscapeChars(self, value): """Escape necessary chars before inserting into dbtext. Args: value: 'string' Returns: escaped: 'string' with chars escaped appropriately """ # test that the value is string, if not return it as is try: value.find('a') except: return value escaped = value escaped = escaped.replace('\\', '\\\\').replace('\0', '\\0') escaped = escaped.replace(':', '\\:').replace('\n', '\\n') escaped = escaped.replace('\r', '\\r').replace('\t', '\\t') return escaped def _UnEscapeChars(self, value): """Un-escape necessary chars before returning to user. Args: value: 'string' Returns: escaped: 'string' with chars escaped appropriately """ # test that the value is string, if not return it as is try: value.find('a') except: return value escaped = value escaped = escaped.replace('\\:', ':').replace('\\n', '\n') escaped = escaped.replace('\\r', '\r').replace('\\t', '\t') escaped = escaped.replace('\\0', '\0').replace('\\\\', '\\') return escaped def Execute(self, query, writethru=True): """Parse and execute the query. Args: query: e.g. 'select * from table;' writethru: bool Returns: dataset: [{col: val, col: val}, {col: val}, {col: val}] Raises: ExecuteError: unable to execute query """ # parse the query self.ParseQuery(query) # get lock and execute the query self.OpenTable() Debug('Running ' + self.command) dataset = [] if self.command == 'SELECT': dataset = self._RunSelect() elif self.command == 'UPDATE': dataset = self._RunUpdate() elif self.command == 'INSERT': dataset = self._RunInsert() elif self.command == 'DELETE': dataset = self._RunDelete() if self.command != 'SELECT' and writethru: self.WriteTempTable() self.MoveTableIntoPlace() Debug(dataset) return dataset def CleanUp(self): """Reset the internal variables (for multiple queries).""" self.tokens = [] # query broken up into tokens self.conditions = {} # args to the WHERE clause self.columns = [] # columns requested by SELECT self.table = '' # name of the table being queried self.header = {} # table header self.orig_data = [] # original table data used to diff after updates self.data = [] # table data as a list of dicts self.count = False # where or not using COUNT() self.aliases = {} # column aliases (SELECT AS) self.targets = {} # target columns-value pairs for INSERT/UPDATE self.args = '' # query arguments preceeding the ; self.command = '' # which command are we executing self.strings = [] # list of string literals parsed from the query self.parens = [] # list of parentheses parsed from the query def ParseQuery(self, query): """External wrapper for the query parsing routines. Args: query: string Raises: ParseError: Unable to parse query """ self.args = query.split(';')[0] self._Tokenize() self._ParseCommand() self._ParseOrderBy() self._ParseConditions() self._ParseColumns() self._ParseTable() self._ParseTargets() def _ParseCommand(self): """Determine the command: SELECT, UPDATE, DELETE or INSERT. Raises: ParseError: unable to parse command """ self.command = self.tokens[0] # Check that command is valid if self.command not in self.ALL_COMMANDS: raise ParseError('Unsupported command: ' + self.command) self.tokens.pop(0) Debug('Command is: %s' % self.command) Debug('self.tokens: %s' % self.tokens) def _Tokenize(self): """Turn the string query into a list of tokens. Split on '(', ')', ' ', ';', '=' and ','. In addition capitalize any SQL keywords found. """ # horrible hack to handle now() time_now = '%s' % int(time.time()) time_now = time_now[0:-2] + '00' # round off the seconds for unittesting while 'now()' in self.args.lower(): start = self.args.lower().find('now()') self.args = ('%s%s%s' % (self.args[0:start], time_now, self.args[start + 5:])) # pad token separators with spaces pad = self.args.replace('(', ' ( ').replace(')', ' ) ') pad = pad.replace(',', ' , ').replace(';', ' ; ').replace('=', ' = ') self.args = pad # parse out all the blocks (string literals and parens) self._ParseOutBlocks() # split remaining into tokens self.tokens = self.args.split() # now capitalize i = 0 while i < len(self.tokens): if self.tokens[i].upper() in self.RESERVED_WORDS: self.tokens[i] = self.tokens[i].upper() i += 1 Debug('Tokens: %s' % self.tokens) def _ParseOutBlocks(self): """Parse out string literals and parenthesized values.""" self.strings = [] self.parens = [] # set str placeholder to a value that's not present in the string while self._str_placeholder in self.args: self._str_placeholder = '%s_' % self._str_placeholder # set paren placeholder to a value that's not present in the string while self._paren_placeholder in self.args: self._paren_placeholder = '%s_' % self._paren_placeholder self.strings = self._ParseOutHelper(self._str_placeholder, ["'", '"'], 'quotes') self.parens = self._ParseOutHelper(self._paren_placeholder, ['(', ')'], 'parens') Debug('Strings: %s' % self.strings) Debug('Parens: %s' % self.parens) def _ParseOutHelper(self, placeholder, delims, mode): """Replace all text within delims with placeholders. Args: placeholder: string delims: list of strings mode: string 'parens': if there are 2 delims treat the first as opening and second as closing, such as with ( and ) 'quotes': treat each delim as either opening or closing and require the same one to terminate the block, such as with ' and " Returns: list: [value1, value2, ...] Raises: ParseError: unable to parse out delims ExecuteError: Invalid usage """ if mode not in ['quotes', 'parens']: raise ExecuteError('_ParseOutHelper: invalid mode ' + mode) if mode == 'parens' and len(delims) != 2: raise ExecuteError('_ParseOutHelper: delims must have 2 values ' 'in "parens" mode') values = [] started = 0 new_args = '' string = '' my_id = 0 delim = '' for c in self.args: if c in delims: if not started: if mode == 'parens' and c != delims[0]: raise ParseError('Found closing delimeter %s before ' 'corresponding %s' % (c, delims[0])) started += 1 delim = c else: if ((mode == 'parens' and c == delim) or (mode == 'quotes' and c != delim)): string = '%s%s' % (string, c) continue # wait for matching delim started -= 1 if not started: values.append(string) new_args = '%s %s' % (new_args, '%s%d' % (placeholder, my_id)) my_id += 1 string = '' else: if not started: new_args = '%s%s' % (new_args, c) else: string = '%s%s' % (string, c) if started: if mode == 'parens': waiting_for = delims[1] else: waiting_for = delim raise ParseError('Unterminated block, waiting for ' + waiting_for) self.args = new_args Debug('Values: %s' % values) return values def _ReplaceStringLiterals(self, s, quotes=False): """Replaces string placeholders with real values. If quotes is set to True surround the returned value with single quotes Args: s: string quotes: bool Returns: s: string """ if s.strip().startswith(self._str_placeholder): str_index = int(s.split(self._str_placeholder)[1]) s = self.strings[str_index] if quotes: s = "'" + s + "'" return s def _ReplaceParens(self, s): """Replaces paren placeholders with real values. Args: s: string Returns: s: string """ if s.strip().startswith(self._paren_placeholder): str_index = int(s.split(self._paren_placeholder)[1]) s = self.parens[str_index].strip() return s def _RunDelete(self): """Run the DELETE command. Go through the rows in self.data matching them against the conditions, if they fit delete the row leaving a placeholder value (in order to keep the iteration process sane). Afterward clean up any empty values. Returns: dataset: [number of affected rows] """ i = 0 length = len(self.data) affected = 0 while i < length: if self._MatchRow(self.data[i]): self.data[i] = None affected += 1 i += 1 # clean out the placeholders while None in self.data: self.data.remove(None) return [affected] def _RunUpdate(self): """Run the UPDATE command. Find the matching rows and update based on self.targets Returns: affected: [int] Raises: ExecuteError: failed to run UPDATE """ i = 0 length = len(self.data) affected = 0 while i < length: if self._MatchRow(self.data[i]): for target in self.targets: if target not in self.header: raise ExecuteError(target + ' is an invalid column name') if self.header[target]['auto']: raise ExecuteError(target + ' is type auto and connot be updated') self.data[i][target] = self._TypeCheck(self.targets[target], target) affected += 1 i += 1 return [affected] def _RunInsert(self): """Run the INSERT command. Build up the row based on self.targets and table defaults, then append to self.data Returns: affected: [int] Raises: ExecuteError: failed to run INSERT """ new_row = {} cols = self._SortHeaderColumns() for col in cols: if col in self.targets: if self.header[col]['auto']: raise ExecuteError(col + ' is type auto: cannot be modified') new_row[col] = self.targets[col] elif self.header[col]['null']: new_row[col] = '' elif self.header[col]['auto']: new_row[col] = self._GetNextAuto(col) else: raise ExecuteError(col + ' cannot be empty or null') self.data.append(new_row) return [1] def _GetNextAuto(self, col): """Figure out the next value for col based on existing values. Scan all the current values and return the highest one + 1. Args: col: string Returns: next: int Raises: ExecuteError: Failed to get auto inc """ highest = 0 seen = [] for row in self.data: if row[col] > highest: highest = row[col] if row[col] not in seen: seen.append(row[col]) else: raise ExecuteError('duplicate value %s in %s' % (row[col], col)) return highest + 1 def _RunSelect(self): """Run the SELECT command. Returns: dataset: [] Raises: ExecuteError: failed to run SELECT """ dataset = [] if ['*'] == self.columns: self.columns = self._SortHeaderColumns() for row in self.data: if self._MatchRow(row): match = [] for col in self.columns: if col in self.aliases: concat = '' for concat_col in self.aliases[col]: if concat_col.startswith("'") and concat_col.endswith("'"): concat += concat_col.strip("'") elif concat_col not in self.header.keys(): raise ExecuteError('Table %s does not have a column %s' % (self.table, concat_col)) else: concat = '%s%s' % (concat, row[concat_col]) if not concat.strip(): raise ExecuteError('Empty CONCAT statement') my_match = concat elif col.startswith("'") and col.endswith("'"): my_match = col.strip("'") elif col not in self.header.keys(): raise ExecuteError('Table %s does not have a column %s' % (self.table, col)) else: my_match = row[col] match.append(self._UnEscapeChars(my_match)) dataset.append(match) if self.count: Debug('Dataset: %s' % dataset) dataset = [len(dataset)] if self.order_by: if self.order_by not in self.header.keys(): raise ExecuteError('Unknown column %s in ORDER BY clause' % self.order_by) pos = self._PositionByCol(self.order_by) dataset = self._SortMatrixByCol(dataset, pos) return dataset def _SortMatrixByCol(self, dataset, pos): """Sorts the matrix (array or arrays) based on a given column value. That is, if given matrix that looks like: [[1, 2, 3], [6, 5, 4], [3, 2, 1]] given pos = 0 produce: [[1, 2, 3], [3, 2, 1], [6, 5, 4]] given pos = 1 produce: [[1, 2, 3], [3, 2, 1], [6, 5, 4]] given pos = 2 produce: [[3, 2, 1], [1, 2, 3], [6, 5, 4]] Works for both integer and string values of column. Args: dataset: [[], [], ...] pos: int Returns: sorted: [[], [], ...] """ # prepend value in pos to the beginning of every row i = 0 while i < len(dataset): dataset[i].insert(0, dataset[i][pos]) i += 1 # sort the matrix, which is done on the row we just prepended dataset.sort() # strip away the first value i = 0 while i < len(dataset): dataset[i].pop(0) i += 1 return dataset def _MatchRow(self, row): """Matches the row against self.conditions. Args: row: ['val', 'val'] Returns: Bool """ match = True # when there are no conditions we match everything if not self.conditions: return match for condition in self.conditions: cond_val = self.conditions[condition] if condition not in self.header.keys(): match = False break else: if cond_val != row[condition]: match = False break return match def _ProcessHeader(self): """Parse out the header information. Returns: {col_name: {'type': string, 'null': string, 'auto': string, 'pos': int}} """ header = self.fd.readline().strip() cols = {} pos = 0 for col in header.split(): col_name = col.split('(')[0] col_type = col.split('(')[1].split(')')[0].split(',')[0] col_null = False col_auto = False if ',' in col.split('(')[1].split(')')[0]: if col.split('(')[1].split(')')[0].split(',')[1].lower() == 'null': col_null = True if col.split('(')[1].split(')')[0].split(',')[1].lower() == 'auto': col_auto = True cols[col_name] = {} cols[col_name]['type'] = col_type cols[col_name]['null'] = col_null cols[col_name]['auto'] = col_auto cols[col_name]['pos'] = pos pos += 1 return cols def _GetData(self): """Reads table data into memory as a list of dicts keyed on column names. Returns: data: [{row}, {row}, ...] Raises: ExecuteError: failed to get data """ data = [] row_num = 0 for row in self.fd: row = row.rstrip('\n') row_dict = {} i = 0 field_start = 0 field_num = 0 while i < len(row): if row[i] == ':': # the following block is executed again after the while is done val = row[field_start:i] col = self._ColByPosition(field_num) val = self._TypeCheck(val, col) row_dict[col] = val field_start = i + 1 # skip the colon itself field_num += 1 if row[i] == '\\': i += 2 # skip the next char since it's escaped else: i += 1 # handle the last field since we won't hit a : at the end # sucks to duplicate the code outside the loop but I can't think # of a better way :( val = row[field_start:i] col = self._ColByPosition(field_num) val = self._TypeCheck(val, col) row_dict[col] = val # verify that all columns were created for col in self.header: if col not in row_dict: raise ExecuteError('%s is missing from row %d in %s' % (col, row_num, self.table)) row_num += 1 data.append(row_dict) return data def _TypeCheck(self, val, col): """Verify type of val based on the header. Make sure the value is returned in quotes if it's a string and as '' when it's empty and Null Args: val: string col: string Returns: val: string Raises: ExecuteError: invalid value or column """ if not val and not self.header[col]['null']: raise ExecuteError(col + ' cannot be empty or null') if (self.header[col]['type'].lower() == 'int' or self.header[col]['type'].lower() == 'double'): try: if val: val = eval(val) except NameError, e: raise ExecuteError('Failed to parse %s in %s ' '(unable to convert to type %s): %s' % (col, self.table, self.header[col]['type'], e)) except SyntaxError, e: raise ExecuteError('Failed to parse %s in %s ' '(unable to convert to type %s): %s' % (col, self.table, self.header[col]['type'], e)) return val def _ColByPosition(self, pos): """Returns column name based on position. Args: pos: int Returns: column: string Raises: ExecuteError: invalid column """ for col in self.header: if self.header[col]['pos'] == pos: return col raise ExecuteError('Header does not contain column %d' % pos) def _PositionByCol(self, col): """Returns position of the column based on the name. Args: col: string Returns: pos: int Raises: ExecuteError: invalid column """ if col not in self.header.keys(): raise ExecuteError(col + ' is not a valid column name') return self.header[col]['pos'] def _SortHeaderColumns(self): """Sort column names by position. Returns: sorted: [col1, col2, ...] Raises: ExecuteError: unable to sort header """ cols = self.header.keys() sorted_cols = [''] * len(cols) for col in cols: pos = self.header[col]['pos'] sorted_cols[pos] = col if '' in sorted_cols: raise ExecuteError('Unable to sort header columns: %s' % cols) return sorted_cols def OpenTable(self): """Opens the table file and places its content into memory. Raises: ExecuteError: unable to open table """ # if we already have a header assume multiple queries on same table # (can't use self.data in case the table was empty to begin with) if self.header: return try: self.fd = open(os.path.join(self.location, self.table), 'r') self.header = self._ProcessHeader() if self.command in ['INSERT', 'DELETE', 'UPDATE']: fcntl.flock(self.fd, fcntl.LOCK_EX) self.data = self._GetData() self.orig_data = self.data[:] # save a copy of the data before modifying except IOError, e: raise ExecuteError('Unable to open table %s: %s' % (self.table, e)) Debug('Header is: %s' % self.header) # type check the conditions for cond in self.conditions: if cond not in self.header.keys(): raise ExecuteError('unknown column %s in WHERE clause' % cond) self.conditions[cond] = self._TypeCheck(self.conditions[cond], cond) # type check the targets for target in self.targets: if target not in self.header.keys(): raise ExecuteError('unknown column in targets: %s' % target) self.targets[target] = self._TypeCheck(self.targets[target], target) Debug('Type checked conditions: %s' % self.conditions) Debug('Data is:') for row in self.data: Debug('=======================') Debug(row) Debug('=======================') def WriteTempTable(self): """Write table header and data. First write header and data to a temp file, then move the tmp file to replace the original table file. """ self.temp_file = tempfile.NamedTemporaryFile() Debug('temp_file: ' + self.temp_file.name) # write header columns = self._SortHeaderColumns() header = '' for col in columns: header = '%s %s' % (header, col) header = '%s(%s' % (header, self.header[col]['type']) if self.header[col]['null']: header = '%s,null)' % header elif self.header[col]['auto']: header = '%s,auto)' % header else: header = '%s)' % header self.temp_file.write(header.strip() + '\n') # write data for row in self.data: row_str = '' for col in columns: row_str = '%s:%s' % (row_str, row[col]) self.temp_file.write(row_str[1:] + '\n') self.temp_file.flush() def MoveTableIntoPlace(self): """Replace the real table with the temp one. Diff the new data against the original and replace the table when they are different. """ if self.data != self.orig_data: temp_file = self.temp_file.name table_file = os.path.join(self.location, self.table) Debug('Copying %s to %s' % (temp_file, table_file)) shutil.copy(self.temp_file.name, self.location + '/' + self.table) def _ParsePairs(self, s, delimeter): """Parses out name value pairs from a string. String contains name=value pairs separated by a delimiter (such as "and" or ",") Args: s: string delimeter: string Returns: my_dict: dictionary Raises: ParseError: unable to parse pairs """ my_dict = {} Debug('parse pairs: [%s]' % s) pairs = s.split(delimeter) for pair in pairs: if '=' not in pair: raise ParseError('Invalid condition pair: ' + pair) split = pair.split('=') Debug('split: %s' % split) if len(split) != 2: raise ParseError('Invalid condition pair: ' + pair) col = split[0].strip() if not col or not split[1].strip() or ' ' in col: raise ParseError('Invalid condition pair: ' + pair) val = self._ReplaceStringLiterals(split[1].strip()) my_dict[col] = val return my_dict class Error(Exception): """DBText error.""" class ParseError(Error): """Parse error.""" class NotSupportedError(Error): """Not Supported error.""" class ExecuteError(Error): """Execute error.""" def main(argv): if len(argv) < 2: print 'Usage %s query' % argv[0] sys.exit(1) if 'DBTEXT_PATH' not in os.environ or not os.environ['DBTEXT_PATH']: print 'DBTEXT_PATH must be set' sys.exit(1) else: location = os.environ['DBTEXT_PATH'] try: conn = DBText(location) dataset = conn.Execute(' '.join(argv[1:])) if dataset: for row in dataset: if conn.command != 'SELECT': print 'Updated %s, rows affected: %d' % (conn.table, row) else: print row except Error, e: print e sys.exit(1) if __name__ == '__main__': main(sys.argv) kamailio-4.0.4/utils/kamctl/dbtextdb/tests/0000755000000000000000000000000012223032460017352 5ustar rootrootkamailio-4.0.4/utils/kamctl/dbtextdb/tests/bad_table_null0000644000000000000000000000015512223032460022225 0ustar rootrootcol1(string) id(int) col2(string,null) col3(string) item1\::1:item2:asdf it\:em1:2:\:: \:item3:3:asdf\::asdf kamailio-4.0.4/utils/kamctl/dbtextdb/tests/unsorted_table20000644000000000000000000000025112223032460022367 0ustar rootrootid(int,auto) user(string) domain(string,null) number(int) 1:fred:test.com:2125551234 4:alex:test1.com:2125551237 2:james:test4.com:2125551231 5:john:test.com:2125551240 kamailio-4.0.4/utils/kamctl/dbtextdb/tests/bad_table_wrong_type0000644000000000000000000000012412223032460023444 0ustar rootrootcol1(string) id(int) col2(string,null) item1\::1:item2 it\:em1:2a: \:item3:3:asdf\: kamailio-4.0.4/utils/kamctl/dbtextdb/tests/bad_table_long_row0000644000000000000000000000016012223032460023075 0ustar rootrootcol1(string) id(int) col2(string,null) col3(string) item1\::1:item2:asdf it\:em1:2:\::lk: \:item3:3:asdf\::asdf kamailio-4.0.4/utils/kamctl/dbtextdb/tests/subscriber0000644000000000000000000000172512223032460021445 0ustar rootrootid(int,auto) username(string) domain(string) password(string) first_name(string,null) last_name(string,null) email_address(string) datetime_created(int,null) ha1(string) ha1b(string) timezone(string,null) rpid(string,null) 1:monitor:test.com:monitor:::test-team@test.com:1202336326:88bb9163d2b15a3c62def3975fdca72d:27b0e1a4da287171989894532c9f783e:: 2:test1:test.com:password:::test-team@test.com:1202336326:fb43223b02f6331f917cc2491235aad8:9b090c888afe748e3bf5b8681e71f29e:: 3:test2:test.com:password:::test-team@test.com:1202336327:9fe9bfa1315b8202838838c3807a0a32:fac1f260ebda200719de4aa29880ee05:: 4:monitor:test.com:monitor:::test-team@test.com:1202336326:88bb9163d2b15a3c62def3975fdca72d:27b0e1a4da287171989894532c9f783e:: 5:test1:test.com:password:::test-team@test.com:1202336375:fb43223b02f6331f917cc2491235aad8:9b090c888afe748e3bf5b8681e71f29e:: 6:test1:test.com:password:::test-team@test.com:1202336375:fb43223b02f6331f917cc2491235aad8:9b090c888afe748e3bf5b8681e71f29e:: kamailio-4.0.4/utils/kamctl/dbtextdb/tests/test0000644000000000000000000000013212223032460020250 0ustar rootrootcol1(string) id(int,auto) col2(string,null) item1\::1:item2 it\:em1\\:2: \:item3:3:asdf\: kamailio-4.0.4/utils/kamctl/dbtextdb/tests/bad_table_int0000644000000000000000000000016212223032460022043 0ustar rootrootcol1(string) id(int) col2(string,null) col3(string) item1\::string:item2:asdf it\:em1:2:\:: \:item3:3:asdf\::asdf kamailio-4.0.4/utils/kamctl/dbtextdb/tests/bad_table_auto_dupe0000644000000000000000000000015712223032460023242 0ustar rootrootcol1(string) id(int) col2(string,null) col3(string) item1\::1:item2:asdf it\:em1:2:\::lk \:item3:2:asdf\::asdf kamailio-4.0.4/utils/kamctl/dbtextdb/tests/unsorted_table0000644000000000000000000000030512223032460022305 0ustar rootrootid(int,auto) user(string) domain(string,null) number(int) 1:fred:test.com:2125551234 4:alex:test1.com:2125551237 2:james:test4.com:2125551231 3:mike:test2.com:2125551239 5:john:test.com:2125551240 kamailio-4.0.4/utils/kamctl/dbtextdb/tests/bad_table_short_row0000644000000000000000000000015412223032460023300 0ustar rootrootcol1(string) id(int) col2(string,null) col3(string) item1\::1:item2:asdf it\:em1:2:\: \:item3:3:asdf\::asdf kamailio-4.0.4/utils/kamctl/dbtextdb/__init__.py0000644000000000000000000000035212223032460020321 0ustar rootroot#!/usr/bin/python # # Copyright 2008 Google Inc. All Rights Reserved. """Empty __init__ for dbtextdb module. This file is empty, it only exists to enable importing dbtextdb """ __author__ = 'herman@google.com (Herman Sheremetyev)' kamailio-4.0.4/utils/kamctl/postgres/0000755000000000000000000000000012223032461016257 5ustar rootrootkamailio-4.0.4/utils/kamctl/postgres/sca-create.sql0000644000000000000000000000152412223032460021010 0ustar rootrootINSERT INTO version (table_name, table_version) values ('sca_subscriptions','1'); CREATE TABLE sca_subscriptions ( id SERIAL PRIMARY KEY NOT NULL, subscriber VARCHAR(255) NOT NULL, aor VARCHAR(255) NOT NULL, event INTEGER DEFAULT 0 NOT NULL, expires INTEGER DEFAULT 0 NOT NULL, state INTEGER DEFAULT 0 NOT NULL, app_idx INTEGER DEFAULT 0 NOT NULL, call_id VARCHAR(255) NOT NULL, from_tag VARCHAR(64) NOT NULL, to_tag VARCHAR(64) NOT NULL, record_route TEXT, notify_cseq INTEGER NOT NULL, subscribe_cseq INTEGER NOT NULL, CONSTRAINT sca_subscriptions_sca_subscriptions_idx UNIQUE (subscriber, call_id, from_tag, to_tag) ); CREATE INDEX sca_subscriptions_sca_expires_idx ON sca_subscriptions (expires); CREATE INDEX sca_subscriptions_sca_subscribers_idx ON sca_subscriptions (subscriber, event); kamailio-4.0.4/utils/kamctl/postgres/registrar-create.sql0000644000000000000000000000206012223032460022240 0ustar rootrootINSERT INTO version (table_name, table_version) values ('aliases','6'); CREATE TABLE aliases ( id SERIAL PRIMARY KEY NOT NULL, ruid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT NULL, contact VARCHAR(255) DEFAULT '' NOT NULL, received VARCHAR(128) DEFAULT NULL, path VARCHAR(128) DEFAULT NULL, expires TIMESTAMP WITHOUT TIME ZONE DEFAULT '2030-05-28 21:32:15' NOT NULL, q REAL DEFAULT 1.0 NOT NULL, callid VARCHAR(255) DEFAULT 'Default-Call-ID' NOT NULL, cseq INTEGER DEFAULT 1 NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, cflags INTEGER DEFAULT 0 NOT NULL, user_agent VARCHAR(255) DEFAULT '' NOT NULL, socket VARCHAR(64) DEFAULT NULL, methods INTEGER DEFAULT NULL, instance VARCHAR(255) DEFAULT NULL, reg_id INTEGER DEFAULT 0 NOT NULL, CONSTRAINT aliases_ruid_idx UNIQUE (ruid) ); CREATE INDEX aliases_alias_idx ON aliases (username, domain, contact); kamailio-4.0.4/utils/kamctl/postgres/uri_db-create.sql0000644000000000000000000000064012223032460021504 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uri','1'); CREATE TABLE uri ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, uri_user VARCHAR(64) DEFAULT '' NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT uri_account_idx UNIQUE (username, domain, uri_user) ); kamailio-4.0.4/utils/kamctl/postgres/purple-create.sql0000644000000000000000000000041712223032460021551 0ustar rootrootINSERT INTO version (table_name, table_version) values ('purplemap','1'); CREATE TABLE purplemap ( id SERIAL PRIMARY KEY NOT NULL, sip_user VARCHAR(128) NOT NULL, ext_user VARCHAR(128) NOT NULL, ext_prot VARCHAR(16) NOT NULL, ext_pass VARCHAR(64) ); kamailio-4.0.4/utils/kamctl/postgres/avpops-create.sql0000644000000000000000000000122212223032460021545 0ustar rootrootINSERT INTO version (table_name, table_version) values ('usr_preferences','2'); CREATE TABLE usr_preferences ( id SERIAL PRIMARY KEY NOT NULL, uuid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(128) DEFAULT 0 NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, attribute VARCHAR(32) DEFAULT '' NOT NULL, type INTEGER DEFAULT 0 NOT NULL, value VARCHAR(128) DEFAULT '' NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL ); CREATE INDEX usr_preferences_ua_idx ON usr_preferences (uuid, attribute); CREATE INDEX usr_preferences_uda_idx ON usr_preferences (username, domain, attribute); kamailio-4.0.4/utils/kamctl/postgres/usrloc-create.sql0000644000000000000000000000341212223032460021547 0ustar rootrootINSERT INTO version (table_name, table_version) values ('location','6'); CREATE TABLE location ( id SERIAL PRIMARY KEY NOT NULL, ruid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT NULL, contact VARCHAR(255) DEFAULT '' NOT NULL, received VARCHAR(128) DEFAULT NULL, path VARCHAR(512) DEFAULT NULL, expires TIMESTAMP WITHOUT TIME ZONE DEFAULT '2030-05-28 21:32:15' NOT NULL, q REAL DEFAULT 1.0 NOT NULL, callid VARCHAR(255) DEFAULT 'Default-Call-ID' NOT NULL, cseq INTEGER DEFAULT 1 NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, cflags INTEGER DEFAULT 0 NOT NULL, user_agent VARCHAR(255) DEFAULT '' NOT NULL, socket VARCHAR(64) DEFAULT NULL, methods INTEGER DEFAULT NULL, instance VARCHAR(255) DEFAULT NULL, reg_id INTEGER DEFAULT 0 NOT NULL, CONSTRAINT location_ruid_idx UNIQUE (ruid) ); CREATE INDEX location_account_contact_idx ON location (username, domain, contact); CREATE INDEX location_expires_idx ON location (expires); INSERT INTO version (table_name, table_version) values ('location_attrs','1'); CREATE TABLE location_attrs ( id SERIAL PRIMARY KEY NOT NULL, ruid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT NULL, aname VARCHAR(64) DEFAULT '' NOT NULL, atype INTEGER DEFAULT 0 NOT NULL, avalue VARCHAR(255) DEFAULT '' NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL ); CREATE INDEX location_attrs_account_record_idx ON location_attrs (username, domain, ruid); CREATE INDEX location_attrs_last_modified_idx ON location_attrs (last_modified); kamailio-4.0.4/utils/kamctl/postgres/standard-create.sql0000644000000000000000000000025012223032460022035 0ustar rootrootCREATE TABLE version ( table_name VARCHAR(32) NOT NULL, table_version INTEGER DEFAULT 0 NOT NULL, CONSTRAINT version_table_name_idx UNIQUE (table_name) ); kamailio-4.0.4/utils/kamctl/postgres/domainpolicy-create.sql0000644000000000000000000000063412223032460022732 0ustar rootrootINSERT INTO version (table_name, table_version) values ('domainpolicy','2'); CREATE TABLE domainpolicy ( id SERIAL PRIMARY KEY NOT NULL, rule VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, att VARCHAR(255), val VARCHAR(128), description VARCHAR(255) NOT NULL, CONSTRAINT domainpolicy_rav_idx UNIQUE (rule, att, val) ); CREATE INDEX domainpolicy_rule_idx ON domainpolicy (rule); kamailio-4.0.4/utils/kamctl/postgres/carrierroute-create.sql0000644000000000000000000000276112223032460022754 0ustar rootrootINSERT INTO version (table_name, table_version) values ('carrierroute','3'); CREATE TABLE carrierroute ( id SERIAL PRIMARY KEY NOT NULL, carrier INTEGER DEFAULT 0 NOT NULL, domain INTEGER DEFAULT 0 NOT NULL, scan_prefix VARCHAR(64) DEFAULT '' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, mask INTEGER DEFAULT 0 NOT NULL, prob REAL DEFAULT 0 NOT NULL, strip INTEGER DEFAULT 0 NOT NULL, rewrite_host VARCHAR(128) DEFAULT '' NOT NULL, rewrite_prefix VARCHAR(64) DEFAULT '' NOT NULL, rewrite_suffix VARCHAR(64) DEFAULT '' NOT NULL, description VARCHAR(255) DEFAULT NULL ); INSERT INTO version (table_name, table_version) values ('carrierfailureroute','2'); CREATE TABLE carrierfailureroute ( id SERIAL PRIMARY KEY NOT NULL, carrier INTEGER DEFAULT 0 NOT NULL, domain INTEGER DEFAULT 0 NOT NULL, scan_prefix VARCHAR(64) DEFAULT '' NOT NULL, host_name VARCHAR(128) DEFAULT '' NOT NULL, reply_code VARCHAR(3) DEFAULT '' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, mask INTEGER DEFAULT 0 NOT NULL, next_domain INTEGER DEFAULT 0 NOT NULL, description VARCHAR(255) DEFAULT NULL ); INSERT INTO version (table_name, table_version) values ('carrier_name','1'); CREATE TABLE carrier_name ( id SERIAL PRIMARY KEY NOT NULL, carrier VARCHAR(64) DEFAULT NULL ); INSERT INTO version (table_name, table_version) values ('domain_name','1'); CREATE TABLE domain_name ( id SERIAL PRIMARY KEY NOT NULL, domain VARCHAR(64) DEFAULT NULL ); kamailio-4.0.4/utils/kamctl/postgres/domain-create.sql0000644000000000000000000000136612223032460021515 0ustar rootrootINSERT INTO version (table_name, table_version) values ('domain','2'); CREATE TABLE domain ( id SERIAL PRIMARY KEY NOT NULL, domain VARCHAR(64) NOT NULL, did VARCHAR(64) DEFAULT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT domain_domain_idx UNIQUE (domain) ); INSERT INTO version (table_name, table_version) values ('domain_attrs','1'); CREATE TABLE domain_attrs ( id SERIAL PRIMARY KEY NOT NULL, did VARCHAR(64) NOT NULL, name VARCHAR(32) NOT NULL, type INTEGER NOT NULL, value VARCHAR(255) NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT domain_attrs_domain_attrs_idx UNIQUE (did, name, value) ); kamailio-4.0.4/utils/kamctl/postgres/permissions-create.sql0000644000000000000000000000114712223032460022616 0ustar rootrootINSERT INTO version (table_name, table_version) values ('trusted','5'); CREATE TABLE trusted ( id SERIAL PRIMARY KEY NOT NULL, src_ip VARCHAR(50) NOT NULL, proto VARCHAR(4) NOT NULL, from_pattern VARCHAR(64) DEFAULT NULL, tag VARCHAR(64) ); CREATE INDEX trusted_peer_idx ON trusted (src_ip); INSERT INTO version (table_name, table_version) values ('address','6'); CREATE TABLE address ( id SERIAL PRIMARY KEY NOT NULL, grp INTEGER DEFAULT 1 NOT NULL, ip_addr VARCHAR(50) NOT NULL, mask INTEGER DEFAULT 32 NOT NULL, port SMALLINT DEFAULT 0 NOT NULL, tag VARCHAR(64) ); kamailio-4.0.4/utils/kamctl/postgres/htable-create.sql0000644000000000000000000000053612223032460021503 0ustar rootrootINSERT INTO version (table_name, table_version) values ('htable','2'); CREATE TABLE htable ( id SERIAL PRIMARY KEY NOT NULL, key_name VARCHAR(64) DEFAULT '' NOT NULL, key_type INTEGER DEFAULT 0 NOT NULL, value_type INTEGER DEFAULT 0 NOT NULL, key_value VARCHAR(128) DEFAULT '' NOT NULL, expires INTEGER DEFAULT 0 NOT NULL ); kamailio-4.0.4/utils/kamctl/postgres/matrix-create.sql0000644000000000000000000000035712223032460021551 0ustar rootrootINSERT INTO version (table_name, table_version) values ('matrix','1'); CREATE TABLE matrix ( first INTEGER NOT NULL, second SMALLINT NOT NULL, res INTEGER NOT NULL ); CREATE INDEX matrix_matrix_idx ON matrix (first, second); kamailio-4.0.4/utils/kamctl/postgres/pdt-create.sql0000644000000000000000000000045712223032460021035 0ustar rootrootINSERT INTO version (table_name, table_version) values ('pdt','1'); CREATE TABLE pdt ( id SERIAL PRIMARY KEY NOT NULL, sdomain VARCHAR(128) NOT NULL, prefix VARCHAR(32) NOT NULL, domain VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT pdt_sdomain_prefix_idx UNIQUE (sdomain, prefix) ); kamailio-4.0.4/utils/kamctl/postgres/uid_uri_db-create.sql0000644000000000000000000000160512223032460022347 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_uri','3'); CREATE TABLE uid_uri ( id SERIAL PRIMARY KEY NOT NULL, uid VARCHAR(64) NOT NULL, did VARCHAR(64) NOT NULL, username VARCHAR(64) NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, scheme VARCHAR(8) DEFAULT 'sip' NOT NULL ); CREATE INDEX uid_uri_uri_idx1 ON uid_uri (username, did, scheme); CREATE INDEX uid_uri_uri_uid ON uid_uri (uid); INSERT INTO version (table_name, table_version) values ('uid_uri_attrs','2'); CREATE TABLE uid_uri_attrs ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, did VARCHAR(64) NOT NULL, name VARCHAR(32) NOT NULL, value VARCHAR(128), type INTEGER DEFAULT 0 NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, scheme VARCHAR(8) DEFAULT 'sip' NOT NULL, CONSTRAINT uid_uri_attrs_uriattrs_idx UNIQUE (username, did, name, value, scheme) ); kamailio-4.0.4/utils/kamctl/postgres/uid_gflags-create.sql0000644000000000000000000000054112223032460022344 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_global_attrs','1'); CREATE TABLE uid_global_attrs ( id SERIAL PRIMARY KEY NOT NULL, name VARCHAR(32) NOT NULL, type INTEGER DEFAULT 0 NOT NULL, value VARCHAR(128), flags INTEGER DEFAULT 0 NOT NULL, CONSTRAINT uid_global_attrs_global_attrs_idx UNIQUE (name, value) ); kamailio-4.0.4/utils/kamctl/postgres/drouting-create.sql0000644000000000000000000000244612223032460022101 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dr_gateways','3'); CREATE TABLE dr_gateways ( gwid SERIAL PRIMARY KEY NOT NULL, type INTEGER DEFAULT 0 NOT NULL, address VARCHAR(128) NOT NULL, strip INTEGER DEFAULT 0 NOT NULL, pri_prefix VARCHAR(64) DEFAULT NULL, attrs VARCHAR(255) DEFAULT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ); INSERT INTO version (table_name, table_version) values ('dr_rules','3'); CREATE TABLE dr_rules ( ruleid SERIAL PRIMARY KEY NOT NULL, groupid VARCHAR(255) NOT NULL, prefix VARCHAR(64) NOT NULL, timerec VARCHAR(255) NOT NULL, priority INTEGER DEFAULT 0 NOT NULL, routeid VARCHAR(64) NOT NULL, gwlist VARCHAR(255) NOT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ); INSERT INTO version (table_name, table_version) values ('dr_gw_lists','1'); CREATE TABLE dr_gw_lists ( id SERIAL PRIMARY KEY NOT NULL, gwlist VARCHAR(255) NOT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ); INSERT INTO version (table_name, table_version) values ('dr_groups','2'); CREATE TABLE dr_groups ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(128) DEFAULT '' NOT NULL, groupid INTEGER DEFAULT 0 NOT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ); kamailio-4.0.4/utils/kamctl/postgres/msilo-create.sql0000644000000000000000000000130412223032460021361 0ustar rootrootINSERT INTO version (table_name, table_version) values ('silo','7'); CREATE TABLE silo ( id SERIAL PRIMARY KEY NOT NULL, src_addr VARCHAR(128) DEFAULT '' NOT NULL, dst_addr VARCHAR(128) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, inc_time INTEGER DEFAULT 0 NOT NULL, exp_time INTEGER DEFAULT 0 NOT NULL, snd_time INTEGER DEFAULT 0 NOT NULL, ctype VARCHAR(32) DEFAULT 'text/plain' NOT NULL, body BYTEA DEFAULT '' NOT NULL, extra_hdrs TEXT DEFAULT '' NOT NULL, callid VARCHAR(128) DEFAULT '' NOT NULL, status INTEGER DEFAULT 0 NOT NULL ); CREATE INDEX silo_account_idx ON silo (username, domain); kamailio-4.0.4/utils/kamctl/postgres/dialog-create.sql0000644000000000000000000000252012223032460021476 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dialog','7'); CREATE TABLE dialog ( id SERIAL PRIMARY KEY NOT NULL, hash_entry INTEGER NOT NULL, hash_id INTEGER NOT NULL, callid VARCHAR(255) NOT NULL, from_uri VARCHAR(128) NOT NULL, from_tag VARCHAR(64) NOT NULL, to_uri VARCHAR(128) NOT NULL, to_tag VARCHAR(64) NOT NULL, caller_cseq VARCHAR(20) NOT NULL, callee_cseq VARCHAR(20) NOT NULL, caller_route_set VARCHAR(512), callee_route_set VARCHAR(512), caller_contact VARCHAR(128) NOT NULL, callee_contact VARCHAR(128) NOT NULL, caller_sock VARCHAR(64) NOT NULL, callee_sock VARCHAR(64) NOT NULL, state INTEGER NOT NULL, start_time INTEGER NOT NULL, timeout INTEGER DEFAULT 0 NOT NULL, sflags INTEGER DEFAULT 0 NOT NULL, iflags INTEGER DEFAULT 0 NOT NULL, toroute_name VARCHAR(32), req_uri VARCHAR(128) NOT NULL, xdata VARCHAR(512) ); CREATE INDEX dialog_hash_idx ON dialog (hash_entry, hash_id); INSERT INTO version (table_name, table_version) values ('dialog_vars','1'); CREATE TABLE dialog_vars ( id SERIAL PRIMARY KEY NOT NULL, hash_entry INTEGER NOT NULL, hash_id INTEGER NOT NULL, dialog_key VARCHAR(128) NOT NULL, dialog_value VARCHAR(512) NOT NULL ); CREATE INDEX dialog_vars_hash_idx ON dialog_vars (hash_entry, hash_id); kamailio-4.0.4/utils/kamctl/postgres/acc-create.sql0000644000000000000000000000177212223032461020776 0ustar rootrootINSERT INTO version (table_name, table_version) values ('acc','4'); CREATE TABLE acc ( id SERIAL PRIMARY KEY NOT NULL, method VARCHAR(16) DEFAULT '' NOT NULL, from_tag VARCHAR(64) DEFAULT '' NOT NULL, to_tag VARCHAR(64) DEFAULT '' NOT NULL, callid VARCHAR(255) DEFAULT '' NOT NULL, sip_code VARCHAR(3) DEFAULT '' NOT NULL, sip_reason VARCHAR(32) DEFAULT '' NOT NULL, time TIMESTAMP WITHOUT TIME ZONE NOT NULL ); CREATE INDEX acc_callid_idx ON acc (callid); INSERT INTO version (table_name, table_version) values ('missed_calls','3'); CREATE TABLE missed_calls ( id SERIAL PRIMARY KEY NOT NULL, method VARCHAR(16) DEFAULT '' NOT NULL, from_tag VARCHAR(64) DEFAULT '' NOT NULL, to_tag VARCHAR(64) DEFAULT '' NOT NULL, callid VARCHAR(255) DEFAULT '' NOT NULL, sip_code VARCHAR(3) DEFAULT '' NOT NULL, sip_reason VARCHAR(32) DEFAULT '' NOT NULL, time TIMESTAMP WITHOUT TIME ZONE NOT NULL ); CREATE INDEX missed_calls_callid_idx ON missed_calls (callid); kamailio-4.0.4/utils/kamctl/postgres/pipelimit-create.sql0000644000000000000000000000041212223032460022231 0ustar rootrootINSERT INTO version (table_name, table_version) values ('pl_pipes','1'); CREATE TABLE pl_pipes ( id SERIAL PRIMARY KEY NOT NULL, pipeid VARCHAR(64) DEFAULT '' NOT NULL, algorithm VARCHAR(32) DEFAULT '' NOT NULL, plimit INTEGER DEFAULT 0 NOT NULL ); kamailio-4.0.4/utils/kamctl/postgres/uid_auth_db-create.sql0000644000000000000000000000132612223032460022511 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_credentials','7'); CREATE TABLE uid_credentials ( id SERIAL PRIMARY KEY NOT NULL, auth_username VARCHAR(64) NOT NULL, did VARCHAR(64) DEFAULT '_default' NOT NULL, realm VARCHAR(64) NOT NULL, password VARCHAR(28) DEFAULT '' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, ha1 VARCHAR(32) NOT NULL, ha1b VARCHAR(32) DEFAULT '' NOT NULL, uid VARCHAR(64) NOT NULL ); CREATE INDEX uid_credentials_cred_idx ON uid_credentials (auth_username, did); CREATE INDEX uid_credentials_uid ON uid_credentials (uid); CREATE INDEX uid_credentials_did_idx ON uid_credentials (did); CREATE INDEX uid_credentials_realm_idx ON uid_credentials (realm); kamailio-4.0.4/utils/kamctl/postgres/mtree-create.sql0000644000000000000000000000115112223032460021352 0ustar rootrootINSERT INTO version (table_name, table_version) values ('mtree','1'); CREATE TABLE mtree ( id SERIAL PRIMARY KEY NOT NULL, tprefix VARCHAR(32) DEFAULT '' NOT NULL, tvalue VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT mtree_tprefix_idx UNIQUE (tprefix) ); INSERT INTO version (table_name, table_version) values ('mtrees','2'); CREATE TABLE mtrees ( id SERIAL PRIMARY KEY NOT NULL, tname VARCHAR(128) DEFAULT '' NOT NULL, tprefix VARCHAR(32) DEFAULT '' NOT NULL, tvalue VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT mtrees_tname_tprefix_tvalue_idx UNIQUE (tname, tprefix, tvalue) ); kamailio-4.0.4/utils/kamctl/postgres/imc-create.sql0000644000000000000000000000117612223032460021015 0ustar rootrootINSERT INTO version (table_name, table_version) values ('imc_rooms','1'); CREATE TABLE imc_rooms ( id SERIAL PRIMARY KEY NOT NULL, name VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, flag INTEGER NOT NULL, CONSTRAINT imc_rooms_name_domain_idx UNIQUE (name, domain) ); INSERT INTO version (table_name, table_version) values ('imc_members','1'); CREATE TABLE imc_members ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, room VARCHAR(64) NOT NULL, flag INTEGER NOT NULL, CONSTRAINT imc_members_account_room_idx UNIQUE (username, domain, room) ); kamailio-4.0.4/utils/kamctl/postgres/userblacklist-create.sql0000644000000000000000000000140512223032460023107 0ustar rootrootINSERT INTO version (table_name, table_version) values ('userblacklist','1'); CREATE TABLE userblacklist ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, prefix VARCHAR(64) DEFAULT '' NOT NULL, whitelist SMALLINT DEFAULT 0 NOT NULL ); CREATE INDEX userblacklist_userblacklist_idx ON userblacklist (username, domain, prefix); INSERT INTO version (table_name, table_version) values ('globalblacklist','1'); CREATE TABLE globalblacklist ( id SERIAL PRIMARY KEY NOT NULL, prefix VARCHAR(64) DEFAULT '' NOT NULL, whitelist SMALLINT DEFAULT 0 NOT NULL, description VARCHAR(255) DEFAULT NULL ); CREATE INDEX globalblacklist_globalblacklist_idx ON globalblacklist (prefix); kamailio-4.0.4/utils/kamctl/postgres/alias_db-create.sql0000644000000000000000000000073012223032460021776 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dbaliases','1'); CREATE TABLE dbaliases ( id SERIAL PRIMARY KEY NOT NULL, alias_username VARCHAR(64) DEFAULT '' NOT NULL, alias_domain VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, CONSTRAINT dbaliases_alias_idx UNIQUE (alias_username, alias_domain) ); CREATE INDEX dbaliases_target_idx ON dbaliases (username, domain); kamailio-4.0.4/utils/kamctl/postgres/uac-create.sql0000644000000000000000000000121112223032460021003 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uacreg','1'); CREATE TABLE uacreg ( id SERIAL PRIMARY KEY NOT NULL, l_uuid VARCHAR(64) DEFAULT '' NOT NULL, l_username VARCHAR(64) DEFAULT '' NOT NULL, l_domain VARCHAR(128) DEFAULT '' NOT NULL, r_username VARCHAR(64) DEFAULT '' NOT NULL, r_domain VARCHAR(128) DEFAULT '' NOT NULL, realm VARCHAR(64) DEFAULT '' NOT NULL, auth_username VARCHAR(64) DEFAULT '' NOT NULL, auth_password VARCHAR(64) DEFAULT '' NOT NULL, auth_proxy VARCHAR(64) DEFAULT '' NOT NULL, expires INTEGER DEFAULT 0 NOT NULL, CONSTRAINT uacreg_l_uuid_idx UNIQUE (l_uuid) ); kamailio-4.0.4/utils/kamctl/postgres/dispatcher-create.sql0000644000000000000000000000062012223032460022364 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dispatcher','4'); CREATE TABLE dispatcher ( id SERIAL PRIMARY KEY NOT NULL, setid INTEGER DEFAULT 0 NOT NULL, destination VARCHAR(192) DEFAULT '' NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, priority INTEGER DEFAULT 0 NOT NULL, attrs VARCHAR(128) DEFAULT '' NOT NULL, description VARCHAR(64) DEFAULT '' NOT NULL ); kamailio-4.0.4/utils/kamctl/postgres/speeddial-create.sql0000644000000000000000000000112712223032460022173 0ustar rootrootINSERT INTO version (table_name, table_version) values ('speed_dial','2'); CREATE TABLE speed_dial ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, sd_username VARCHAR(64) DEFAULT '' NOT NULL, sd_domain VARCHAR(64) DEFAULT '' NOT NULL, new_uri VARCHAR(128) DEFAULT '' NOT NULL, fname VARCHAR(64) DEFAULT '' NOT NULL, lname VARCHAR(64) DEFAULT '' NOT NULL, description VARCHAR(64) DEFAULT '' NOT NULL, CONSTRAINT speed_dial_speed_dial_idx UNIQUE (username, domain, sd_domain, sd_username) ); kamailio-4.0.4/utils/kamctl/postgres/siptrace-create.sql0000644000000000000000000000156612223032460022062 0ustar rootrootINSERT INTO version (table_name, table_version) values ('sip_trace','3'); CREATE TABLE sip_trace ( id SERIAL PRIMARY KEY NOT NULL, time_stamp TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, time_us INTEGER DEFAULT 0 NOT NULL, callid VARCHAR(255) DEFAULT '' NOT NULL, traced_user VARCHAR(128) DEFAULT '' NOT NULL, msg TEXT NOT NULL, method VARCHAR(50) DEFAULT '' NOT NULL, status VARCHAR(128) DEFAULT '' NOT NULL, fromip VARCHAR(50) DEFAULT '' NOT NULL, toip VARCHAR(50) DEFAULT '' NOT NULL, fromtag VARCHAR(64) DEFAULT '' NOT NULL, direction VARCHAR(4) DEFAULT '' NOT NULL ); CREATE INDEX sip_trace_traced_user_idx ON sip_trace (traced_user); CREATE INDEX sip_trace_date_idx ON sip_trace (time_stamp); CREATE INDEX sip_trace_fromip_idx ON sip_trace (fromip); CREATE INDEX sip_trace_callid_idx ON sip_trace (callid); kamailio-4.0.4/utils/kamctl/postgres/dialplan-create.sql0000644000000000000000000000060612223032460022026 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dialplan','1'); CREATE TABLE dialplan ( id SERIAL PRIMARY KEY NOT NULL, dpid INTEGER NOT NULL, pr INTEGER NOT NULL, match_op INTEGER NOT NULL, match_exp VARCHAR(64) NOT NULL, match_len INTEGER NOT NULL, subst_exp VARCHAR(64) NOT NULL, repl_exp VARCHAR(32) NOT NULL, attrs VARCHAR(32) NOT NULL ); kamailio-4.0.4/utils/kamctl/postgres/group-create.sql0000644000000000000000000000125412223032460021376 0ustar rootrootINSERT INTO version (table_name, table_version) values ('grp','2'); CREATE TABLE grp ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, grp VARCHAR(64) DEFAULT '' NOT NULL, last_modified TIMESTAMP WITHOUT TIME ZONE DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT grp_account_group_idx UNIQUE (username, domain, grp) ); INSERT INTO version (table_name, table_version) values ('re_grp','1'); CREATE TABLE re_grp ( id SERIAL PRIMARY KEY NOT NULL, reg_exp VARCHAR(128) DEFAULT '' NOT NULL, group_id INTEGER DEFAULT 0 NOT NULL ); CREATE INDEX re_grp_group_idx ON re_grp (group_id); kamailio-4.0.4/utils/kamctl/postgres/lcr-create.sql0000644000000000000000000000265112223032460021024 0ustar rootrootINSERT INTO version (table_name, table_version) values ('lcr_gw','3'); CREATE TABLE lcr_gw ( id SERIAL PRIMARY KEY NOT NULL, lcr_id SMALLINT NOT NULL, gw_name VARCHAR(128), ip_addr VARCHAR(50), hostname VARCHAR(64), port SMALLINT, params VARCHAR(64), uri_scheme SMALLINT, transport SMALLINT, strip SMALLINT, prefix VARCHAR(16) DEFAULT NULL, tag VARCHAR(64) DEFAULT NULL, flags INTEGER DEFAULT 0 NOT NULL, defunct INTEGER DEFAULT NULL ); CREATE INDEX lcr_gw_lcr_id_idx ON lcr_gw (lcr_id); INSERT INTO version (table_name, table_version) values ('lcr_rule_target','1'); CREATE TABLE lcr_rule_target ( id SERIAL PRIMARY KEY NOT NULL, lcr_id SMALLINT NOT NULL, rule_id INTEGER NOT NULL, gw_id INTEGER NOT NULL, priority SMALLINT NOT NULL, weight INTEGER DEFAULT 1 NOT NULL, CONSTRAINT lcr_rule_target_rule_id_gw_id_idx UNIQUE (rule_id, gw_id) ); CREATE INDEX lcr_rule_target_lcr_id_idx ON lcr_rule_target (lcr_id); INSERT INTO version (table_name, table_version) values ('lcr_rule','2'); CREATE TABLE lcr_rule ( id SERIAL PRIMARY KEY NOT NULL, lcr_id SMALLINT NOT NULL, prefix VARCHAR(16) DEFAULT NULL, from_uri VARCHAR(64) DEFAULT NULL, request_uri VARCHAR(64) DEFAULT NULL, stopper INTEGER DEFAULT 0 NOT NULL, enabled INTEGER DEFAULT 1 NOT NULL, CONSTRAINT lcr_rule_lcr_id_prefix_from_uri_idx UNIQUE (lcr_id, prefix, from_uri) ); kamailio-4.0.4/utils/kamctl/postgres/auth_db-create.sql0000644000000000000000000000107612223032460021652 0ustar rootrootINSERT INTO version (table_name, table_version) values ('subscriber','6'); CREATE TABLE subscriber ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, password VARCHAR(25) DEFAULT '' NOT NULL, email_address VARCHAR(64) DEFAULT '' NOT NULL, ha1 VARCHAR(64) DEFAULT '' NOT NULL, ha1b VARCHAR(64) DEFAULT '' NOT NULL, rpid VARCHAR(64) DEFAULT NULL, CONSTRAINT subscriber_account_idx UNIQUE (username, domain) ); CREATE INDEX subscriber_username_idx ON subscriber (username); kamailio-4.0.4/utils/kamctl/postgres/presence-create.sql0000644000000000000000000001020312223032460022040 0ustar rootrootINSERT INTO version (table_name, table_version) values ('presentity','3'); CREATE TABLE presentity ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, event VARCHAR(64) NOT NULL, etag VARCHAR(64) NOT NULL, expires INTEGER NOT NULL, received_time INTEGER NOT NULL, body BYTEA NOT NULL, sender VARCHAR(128) NOT NULL, CONSTRAINT presentity_presentity_idx UNIQUE (username, domain, event, etag) ); CREATE INDEX presentity_presentity_expires ON presentity (expires); CREATE INDEX presentity_account_idx ON presentity (username, domain, event); INSERT INTO version (table_name, table_version) values ('active_watchers','11'); CREATE TABLE active_watchers ( id SERIAL PRIMARY KEY NOT NULL, presentity_uri VARCHAR(128) NOT NULL, watcher_username VARCHAR(64) NOT NULL, watcher_domain VARCHAR(64) NOT NULL, to_user VARCHAR(64) NOT NULL, to_domain VARCHAR(64) NOT NULL, event VARCHAR(64) DEFAULT 'presence' NOT NULL, event_id VARCHAR(64), to_tag VARCHAR(64) NOT NULL, from_tag VARCHAR(64) NOT NULL, callid VARCHAR(255) NOT NULL, local_cseq INTEGER NOT NULL, remote_cseq INTEGER NOT NULL, contact VARCHAR(128) NOT NULL, record_route TEXT, expires INTEGER NOT NULL, status INTEGER DEFAULT 2 NOT NULL, reason VARCHAR(64) NOT NULL, version INTEGER DEFAULT 0 NOT NULL, socket_info VARCHAR(64) NOT NULL, local_contact VARCHAR(128) NOT NULL, from_user VARCHAR(64) NOT NULL, from_domain VARCHAR(64) NOT NULL, updated INTEGER NOT NULL, updated_winfo INTEGER NOT NULL, CONSTRAINT active_watchers_active_watchers_idx UNIQUE (callid, to_tag, from_tag) ); CREATE INDEX active_watchers_active_watchers_expires ON active_watchers (expires); CREATE INDEX active_watchers_active_watchers_pres ON active_watchers (presentity_uri, event); CREATE INDEX active_watchers_updated_idx ON active_watchers (updated); CREATE INDEX active_watchers_updated_winfo_idx ON active_watchers (updated_winfo, presentity_uri); INSERT INTO version (table_name, table_version) values ('watchers','3'); CREATE TABLE watchers ( id SERIAL PRIMARY KEY NOT NULL, presentity_uri VARCHAR(128) NOT NULL, watcher_username VARCHAR(64) NOT NULL, watcher_domain VARCHAR(64) NOT NULL, event VARCHAR(64) DEFAULT 'presence' NOT NULL, status INTEGER NOT NULL, reason VARCHAR(64), inserted_time INTEGER NOT NULL, CONSTRAINT watchers_watcher_idx UNIQUE (presentity_uri, watcher_username, watcher_domain, event) ); INSERT INTO version (table_name, table_version) values ('xcap','4'); CREATE TABLE xcap ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, doc BYTEA NOT NULL, doc_type INTEGER NOT NULL, etag VARCHAR(64) NOT NULL, source INTEGER NOT NULL, doc_uri VARCHAR(255) NOT NULL, port INTEGER NOT NULL, CONSTRAINT xcap_doc_uri_idx UNIQUE (doc_uri) ); CREATE INDEX xcap_account_doc_type_idx ON xcap (username, domain, doc_type); CREATE INDEX xcap_account_doc_type_uri_idx ON xcap (username, domain, doc_type, doc_uri); CREATE INDEX xcap_account_doc_uri_idx ON xcap (username, domain, doc_uri); INSERT INTO version (table_name, table_version) values ('pua','7'); CREATE TABLE pua ( id SERIAL PRIMARY KEY NOT NULL, pres_uri VARCHAR(128) NOT NULL, pres_id VARCHAR(255) NOT NULL, event INTEGER NOT NULL, expires INTEGER NOT NULL, desired_expires INTEGER NOT NULL, flag INTEGER NOT NULL, etag VARCHAR(64) NOT NULL, tuple_id VARCHAR(64), watcher_uri VARCHAR(128) NOT NULL, call_id VARCHAR(255) NOT NULL, to_tag VARCHAR(64) NOT NULL, from_tag VARCHAR(64) NOT NULL, cseq INTEGER NOT NULL, record_route TEXT, contact VARCHAR(128) NOT NULL, remote_contact VARCHAR(128) NOT NULL, version INTEGER NOT NULL, extra_headers TEXT NOT NULL, CONSTRAINT pua_pua_idx UNIQUE (etag, tuple_id, call_id, from_tag) ); CREATE INDEX pua_expires_idx ON pua (expires); CREATE INDEX pua_dialog1_idx ON pua (pres_id, pres_uri); CREATE INDEX pua_dialog2_idx ON pua (call_id, from_tag); CREATE INDEX pua_record_idx ON pua (pres_id); kamailio-4.0.4/utils/kamctl/postgres/uid_avp_db-create.sql0000644000000000000000000000057312223032460022341 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_user_attrs','3'); CREATE TABLE uid_user_attrs ( id SERIAL PRIMARY KEY NOT NULL, uid VARCHAR(64) NOT NULL, name VARCHAR(32) NOT NULL, value VARCHAR(128), type INTEGER DEFAULT 0 NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, CONSTRAINT uid_user_attrs_userattrs_idx UNIQUE (uid, name, value) ); kamailio-4.0.4/utils/kamctl/postgres/rls-create.sql0000644000000000000000000000373612223032460021051 0ustar rootrootINSERT INTO version (table_name, table_version) values ('rls_presentity','1'); CREATE TABLE rls_presentity ( id SERIAL PRIMARY KEY NOT NULL, rlsubs_did VARCHAR(255) NOT NULL, resource_uri VARCHAR(128) NOT NULL, content_type VARCHAR(255) NOT NULL, presence_state BYTEA NOT NULL, expires INTEGER NOT NULL, updated INTEGER NOT NULL, auth_state INTEGER NOT NULL, reason VARCHAR(64) NOT NULL, CONSTRAINT rls_presentity_rls_presentity_idx UNIQUE (rlsubs_did, resource_uri) ); CREATE INDEX rls_presentity_rlsubs_idx ON rls_presentity (rlsubs_did); CREATE INDEX rls_presentity_updated_idx ON rls_presentity (updated); CREATE INDEX rls_presentity_expires_idx ON rls_presentity (expires); INSERT INTO version (table_name, table_version) values ('rls_watchers','3'); CREATE TABLE rls_watchers ( id SERIAL PRIMARY KEY NOT NULL, presentity_uri VARCHAR(128) NOT NULL, to_user VARCHAR(64) NOT NULL, to_domain VARCHAR(64) NOT NULL, watcher_username VARCHAR(64) NOT NULL, watcher_domain VARCHAR(64) NOT NULL, event VARCHAR(64) DEFAULT 'presence' NOT NULL, event_id VARCHAR(64), to_tag VARCHAR(64) NOT NULL, from_tag VARCHAR(64) NOT NULL, callid VARCHAR(255) NOT NULL, local_cseq INTEGER NOT NULL, remote_cseq INTEGER NOT NULL, contact VARCHAR(128) NOT NULL, record_route TEXT, expires INTEGER NOT NULL, status INTEGER DEFAULT 2 NOT NULL, reason VARCHAR(64) NOT NULL, version INTEGER DEFAULT 0 NOT NULL, socket_info VARCHAR(64) NOT NULL, local_contact VARCHAR(128) NOT NULL, from_user VARCHAR(64) NOT NULL, from_domain VARCHAR(64) NOT NULL, updated INTEGER NOT NULL, CONSTRAINT rls_watchers_rls_watcher_idx UNIQUE (callid, to_tag, from_tag) ); CREATE INDEX rls_watchers_rls_watchers_update ON rls_watchers (watcher_username, watcher_domain, event); CREATE INDEX rls_watchers_rls_watchers_expires ON rls_watchers (expires); CREATE INDEX rls_watchers_updated_idx ON rls_watchers (updated); kamailio-4.0.4/utils/kamctl/postgres/uid_domain-create.sql0000644000000000000000000000144312223032460022352 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_domain','2'); CREATE TABLE uid_domain ( id SERIAL PRIMARY KEY NOT NULL, did VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, flags INTEGER DEFAULT 0 NOT NULL, CONSTRAINT uid_domain_domain_idx UNIQUE (domain) ); CREATE INDEX uid_domain_did_idx ON uid_domain (did); INSERT INTO version (table_name, table_version) values ('uid_domain_attrs','1'); CREATE TABLE uid_domain_attrs ( id SERIAL PRIMARY KEY NOT NULL, did VARCHAR(64), name VARCHAR(32) NOT NULL, type INTEGER DEFAULT 0 NOT NULL, value VARCHAR(128), flags INTEGER DEFAULT 0 NOT NULL, CONSTRAINT uid_domain_attrs_domain_attr_idx UNIQUE (did, name, value) ); CREATE INDEX uid_domain_attrs_domain_did ON uid_domain_attrs (did, flags); kamailio-4.0.4/utils/kamctl/postgres/cpl-create.sql0000644000000000000000000000045312223032460021020 0ustar rootrootINSERT INTO version (table_name, table_version) values ('cpl','1'); CREATE TABLE cpl ( id SERIAL PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, cpl_xml TEXT, cpl_bin TEXT, CONSTRAINT cpl_account_idx UNIQUE (username, domain) ); kamailio-4.0.4/utils/kamctl/mysql/0000755000000000000000000000000012223032461015556 5ustar rootrootkamailio-4.0.4/utils/kamctl/mysql/sca-create.sql0000644000000000000000000000147112223032461020311 0ustar rootrootINSERT INTO version (table_name, table_version) values ('sca_subscriptions','1'); CREATE TABLE sca_subscriptions ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, subscriber VARCHAR(255) NOT NULL, aor VARCHAR(255) NOT NULL, event INT DEFAULT 0 NOT NULL, expires INT(11) DEFAULT 0 NOT NULL, state INT DEFAULT 0 NOT NULL, app_idx INT DEFAULT 0 NOT NULL, call_id VARCHAR(255) NOT NULL, from_tag VARCHAR(64) NOT NULL, to_tag VARCHAR(64) NOT NULL, record_route TEXT, notify_cseq INT(11) NOT NULL, subscribe_cseq INT(11) NOT NULL, CONSTRAINT sca_subscriptions_idx UNIQUE (subscriber, call_id, from_tag, to_tag) ) ENGINE=MyISAM; CREATE INDEX sca_expires_idx ON sca_subscriptions (expires); CREATE INDEX sca_subscribers_idx ON sca_subscriptions (subscriber, event); kamailio-4.0.4/utils/kamctl/mysql/registrar-create.sql0000644000000000000000000000205012223032461021537 0ustar rootrootINSERT INTO version (table_name, table_version) values ('aliases','6'); CREATE TABLE aliases ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, ruid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT NULL, contact VARCHAR(255) DEFAULT '' NOT NULL, received VARCHAR(128) DEFAULT NULL, path VARCHAR(128) DEFAULT NULL, expires DATETIME DEFAULT '2030-05-28 21:32:15' NOT NULL, q FLOAT(10,2) DEFAULT 1.0 NOT NULL, callid VARCHAR(255) DEFAULT 'Default-Call-ID' NOT NULL, cseq INT(11) DEFAULT 1 NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, flags INT(11) DEFAULT 0 NOT NULL, cflags INT(11) DEFAULT 0 NOT NULL, user_agent VARCHAR(255) DEFAULT '' NOT NULL, socket VARCHAR(64) DEFAULT NULL, methods INT(11) DEFAULT NULL, instance VARCHAR(255) DEFAULT NULL, reg_id INT(11) DEFAULT 0 NOT NULL, CONSTRAINT ruid_idx UNIQUE (ruid) ) ENGINE=MyISAM; CREATE INDEX alias_idx ON aliases (username, domain, contact); kamailio-4.0.4/utils/kamctl/mysql/uri_db-create.sql0000644000000000000000000000066012223032461021006 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uri','1'); CREATE TABLE uri ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, uri_user VARCHAR(64) DEFAULT '' NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT account_idx UNIQUE (username, domain, uri_user) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/purple-create.sql0000644000000000000000000000046612223032461021055 0ustar rootrootINSERT INTO version (table_name, table_version) values ('purplemap','1'); CREATE TABLE purplemap ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, sip_user VARCHAR(128) NOT NULL, ext_user VARCHAR(128) NOT NULL, ext_prot VARCHAR(16) NOT NULL, ext_pass VARCHAR(64) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/avpops-create.sql0000644000000000000000000000120612223032461021047 0ustar rootrootINSERT INTO version (table_name, table_version) values ('usr_preferences','2'); CREATE TABLE usr_preferences ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, uuid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(128) DEFAULT 0 NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, attribute VARCHAR(32) DEFAULT '' NOT NULL, type INT(11) DEFAULT 0 NOT NULL, value VARCHAR(128) DEFAULT '' NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL ) ENGINE=MyISAM; CREATE INDEX ua_idx ON usr_preferences (uuid, attribute); CREATE INDEX uda_idx ON usr_preferences (username, domain, attribute); kamailio-4.0.4/utils/kamctl/mysql/usrloc-create.sql0000644000000000000000000000335512223032461021055 0ustar rootrootINSERT INTO version (table_name, table_version) values ('location','6'); CREATE TABLE location ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, ruid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT NULL, contact VARCHAR(255) DEFAULT '' NOT NULL, received VARCHAR(128) DEFAULT NULL, path VARCHAR(512) DEFAULT NULL, expires DATETIME DEFAULT '2030-05-28 21:32:15' NOT NULL, q FLOAT(10,2) DEFAULT 1.0 NOT NULL, callid VARCHAR(255) DEFAULT 'Default-Call-ID' NOT NULL, cseq INT(11) DEFAULT 1 NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, flags INT(11) DEFAULT 0 NOT NULL, cflags INT(11) DEFAULT 0 NOT NULL, user_agent VARCHAR(255) DEFAULT '' NOT NULL, socket VARCHAR(64) DEFAULT NULL, methods INT(11) DEFAULT NULL, instance VARCHAR(255) DEFAULT NULL, reg_id INT(11) DEFAULT 0 NOT NULL, CONSTRAINT ruid_idx UNIQUE (ruid) ) ENGINE=MyISAM; CREATE INDEX account_contact_idx ON location (username, domain, contact); CREATE INDEX expires_idx ON location (expires); INSERT INTO version (table_name, table_version) values ('location_attrs','1'); CREATE TABLE location_attrs ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, ruid VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT NULL, aname VARCHAR(64) DEFAULT '' NOT NULL, atype INT(11) DEFAULT 0 NOT NULL, avalue VARCHAR(255) DEFAULT '' NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL ) ENGINE=MyISAM; CREATE INDEX account_record_idx ON location_attrs (username, domain, ruid); CREATE INDEX last_modified_idx ON location_attrs (last_modified); kamailio-4.0.4/utils/kamctl/mysql/standard-create.sql0000644000000000000000000000026312223032461021341 0ustar rootrootCREATE TABLE version ( table_name VARCHAR(32) NOT NULL, table_version INT UNSIGNED DEFAULT 0 NOT NULL, CONSTRAINT table_name_idx UNIQUE (table_name) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/domainpolicy-create.sql0000644000000000000000000000065112223032461022231 0ustar rootrootINSERT INTO version (table_name, table_version) values ('domainpolicy','2'); CREATE TABLE domainpolicy ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, rule VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, att VARCHAR(255), val VARCHAR(128), description VARCHAR(255) NOT NULL, CONSTRAINT rav_idx UNIQUE (rule, att, val) ) ENGINE=MyISAM; CREATE INDEX rule_idx ON domainpolicy (rule); kamailio-4.0.4/utils/kamctl/mysql/carrierroute-create.sql0000644000000000000000000000335012223032461022247 0ustar rootrootINSERT INTO version (table_name, table_version) values ('carrierroute','3'); CREATE TABLE carrierroute ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, carrier INT(10) UNSIGNED DEFAULT 0 NOT NULL, domain INT(10) UNSIGNED DEFAULT 0 NOT NULL, scan_prefix VARCHAR(64) DEFAULT '' NOT NULL, flags INT(11) UNSIGNED DEFAULT 0 NOT NULL, mask INT(11) UNSIGNED DEFAULT 0 NOT NULL, prob FLOAT DEFAULT 0 NOT NULL, strip INT(11) UNSIGNED DEFAULT 0 NOT NULL, rewrite_host VARCHAR(128) DEFAULT '' NOT NULL, rewrite_prefix VARCHAR(64) DEFAULT '' NOT NULL, rewrite_suffix VARCHAR(64) DEFAULT '' NOT NULL, description VARCHAR(255) DEFAULT NULL ) ENGINE=MyISAM; INSERT INTO version (table_name, table_version) values ('carrierfailureroute','2'); CREATE TABLE carrierfailureroute ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, carrier INT(10) UNSIGNED DEFAULT 0 NOT NULL, domain INT(10) UNSIGNED DEFAULT 0 NOT NULL, scan_prefix VARCHAR(64) DEFAULT '' NOT NULL, host_name VARCHAR(128) DEFAULT '' NOT NULL, reply_code VARCHAR(3) DEFAULT '' NOT NULL, flags INT(11) UNSIGNED DEFAULT 0 NOT NULL, mask INT(11) UNSIGNED DEFAULT 0 NOT NULL, next_domain INT(10) UNSIGNED DEFAULT 0 NOT NULL, description VARCHAR(255) DEFAULT NULL ) ENGINE=MyISAM; INSERT INTO version (table_name, table_version) values ('carrier_name','1'); CREATE TABLE carrier_name ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, carrier VARCHAR(64) DEFAULT NULL ) ENGINE=MyISAM; INSERT INTO version (table_name, table_version) values ('domain_name','1'); CREATE TABLE domain_name ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, domain VARCHAR(64) DEFAULT NULL ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/domain-create.sql0000644000000000000000000000141712223032461021012 0ustar rootrootINSERT INTO version (table_name, table_version) values ('domain','2'); CREATE TABLE domain ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, domain VARCHAR(64) NOT NULL, did VARCHAR(64) DEFAULT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT domain_idx UNIQUE (domain) ) ENGINE=MyISAM; INSERT INTO version (table_name, table_version) values ('domain_attrs','1'); CREATE TABLE domain_attrs ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, did VARCHAR(64) NOT NULL, name VARCHAR(32) NOT NULL, type INT UNSIGNED NOT NULL, value VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT domain_attrs_idx UNIQUE (did, name, value) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/permissions-create.sql0000644000000000000000000000127612223032461022121 0ustar rootrootINSERT INTO version (table_name, table_version) values ('trusted','5'); CREATE TABLE trusted ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, src_ip VARCHAR(50) NOT NULL, proto VARCHAR(4) NOT NULL, from_pattern VARCHAR(64) DEFAULT NULL, tag VARCHAR(64) ) ENGINE=MyISAM; CREATE INDEX peer_idx ON trusted (src_ip); INSERT INTO version (table_name, table_version) values ('address','6'); CREATE TABLE address ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, grp INT(11) UNSIGNED DEFAULT 1 NOT NULL, ip_addr VARCHAR(50) NOT NULL, mask INT DEFAULT 32 NOT NULL, port SMALLINT(5) UNSIGNED DEFAULT 0 NOT NULL, tag VARCHAR(64) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/htable-create.sql0000644000000000000000000000057112223032461021002 0ustar rootrootINSERT INTO version (table_name, table_version) values ('htable','2'); CREATE TABLE htable ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, key_name VARCHAR(64) DEFAULT '' NOT NULL, key_type INT DEFAULT 0 NOT NULL, value_type INT DEFAULT 0 NOT NULL, key_value VARCHAR(128) DEFAULT '' NOT NULL, expires INT DEFAULT 0 NOT NULL ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/matrix-create.sql0000644000000000000000000000037212223032461021046 0ustar rootrootINSERT INTO version (table_name, table_version) values ('matrix','1'); CREATE TABLE matrix ( first INT(10) NOT NULL, second SMALLINT(10) NOT NULL, res INT(10) NOT NULL ) ENGINE=MyISAM; CREATE INDEX matrix_idx ON matrix (first, second); kamailio-4.0.4/utils/kamctl/mysql/pdt-create.sql0000644000000000000000000000052212223032461020326 0ustar rootrootINSERT INTO version (table_name, table_version) values ('pdt','1'); CREATE TABLE pdt ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, sdomain VARCHAR(128) NOT NULL, prefix VARCHAR(32) NOT NULL, domain VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT sdomain_prefix_idx UNIQUE (sdomain, prefix) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/uid_uri_db-create.sql0000644000000000000000000000167312223032461021654 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_uri','3'); CREATE TABLE uid_uri ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, uid VARCHAR(64) NOT NULL, did VARCHAR(64) NOT NULL, username VARCHAR(64) NOT NULL, flags INT UNSIGNED DEFAULT 0 NOT NULL, scheme VARCHAR(8) DEFAULT 'sip' NOT NULL ) ENGINE=MyISAM; CREATE INDEX uri_idx1 ON uid_uri (username, did, scheme); CREATE INDEX uri_uid ON uid_uri (uid); INSERT INTO version (table_name, table_version) values ('uid_uri_attrs','2'); CREATE TABLE uid_uri_attrs ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, did VARCHAR(64) NOT NULL, name VARCHAR(32) NOT NULL, value VARCHAR(128), type INT DEFAULT 0 NOT NULL, flags INT UNSIGNED DEFAULT 0 NOT NULL, scheme VARCHAR(8) DEFAULT 'sip' NOT NULL, CONSTRAINT uriattrs_idx UNIQUE (username, did, name, value, scheme) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/uid_gflags-create.sql0000644000000000000000000000057012223032461021646 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_global_attrs','1'); CREATE TABLE uid_global_attrs ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, name VARCHAR(32) NOT NULL, type INT DEFAULT 0 NOT NULL, value VARCHAR(128), flags INT UNSIGNED DEFAULT 0 NOT NULL, CONSTRAINT global_attrs_idx UNIQUE (name, value) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/drouting-create.sql0000644000000000000000000000273512223032461021402 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dr_gateways','3'); CREATE TABLE dr_gateways ( gwid INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, type INT(11) UNSIGNED DEFAULT 0 NOT NULL, address VARCHAR(128) NOT NULL, strip INT(11) UNSIGNED DEFAULT 0 NOT NULL, pri_prefix VARCHAR(64) DEFAULT NULL, attrs VARCHAR(255) DEFAULT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ) ENGINE=MyISAM; INSERT INTO version (table_name, table_version) values ('dr_rules','3'); CREATE TABLE dr_rules ( ruleid INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, groupid VARCHAR(255) NOT NULL, prefix VARCHAR(64) NOT NULL, timerec VARCHAR(255) NOT NULL, priority INT(11) DEFAULT 0 NOT NULL, routeid VARCHAR(64) NOT NULL, gwlist VARCHAR(255) NOT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ) ENGINE=MyISAM; INSERT INTO version (table_name, table_version) values ('dr_gw_lists','1'); CREATE TABLE dr_gw_lists ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, gwlist VARCHAR(255) NOT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ) ENGINE=MyISAM; INSERT INTO version (table_name, table_version) values ('dr_groups','2'); CREATE TABLE dr_groups ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(128) DEFAULT '' NOT NULL, groupid INT(11) UNSIGNED DEFAULT 0 NOT NULL, description VARCHAR(128) DEFAULT '' NOT NULL ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/msilo-create.sql0000644000000000000000000000132512223032461020664 0ustar rootrootINSERT INTO version (table_name, table_version) values ('silo','7'); CREATE TABLE silo ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, src_addr VARCHAR(128) DEFAULT '' NOT NULL, dst_addr VARCHAR(128) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, inc_time INT DEFAULT 0 NOT NULL, exp_time INT DEFAULT 0 NOT NULL, snd_time INT DEFAULT 0 NOT NULL, ctype VARCHAR(32) DEFAULT 'text/plain' NOT NULL, body BLOB DEFAULT '' NOT NULL, extra_hdrs TEXT DEFAULT '' NOT NULL, callid VARCHAR(128) DEFAULT '' NOT NULL, status INT DEFAULT 0 NOT NULL ) ENGINE=MyISAM; CREATE INDEX account_idx ON silo (username, domain); kamailio-4.0.4/utils/kamctl/mysql/dialog-create.sql0000644000000000000000000000273412223032461021005 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dialog','7'); CREATE TABLE dialog ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, hash_entry INT(10) UNSIGNED NOT NULL, hash_id INT(10) UNSIGNED NOT NULL, callid VARCHAR(255) NOT NULL, from_uri VARCHAR(128) NOT NULL, from_tag VARCHAR(64) NOT NULL, to_uri VARCHAR(128) NOT NULL, to_tag VARCHAR(64) NOT NULL, caller_cseq VARCHAR(20) NOT NULL, callee_cseq VARCHAR(20) NOT NULL, caller_route_set VARCHAR(512), callee_route_set VARCHAR(512), caller_contact VARCHAR(128) NOT NULL, callee_contact VARCHAR(128) NOT NULL, caller_sock VARCHAR(64) NOT NULL, callee_sock VARCHAR(64) NOT NULL, state INT(10) UNSIGNED NOT NULL, start_time INT(10) UNSIGNED NOT NULL, timeout INT(10) UNSIGNED DEFAULT 0 NOT NULL, sflags INT(10) UNSIGNED DEFAULT 0 NOT NULL, iflags INT(10) UNSIGNED DEFAULT 0 NOT NULL, toroute_name VARCHAR(32), req_uri VARCHAR(128) NOT NULL, xdata VARCHAR(512) ) ENGINE=MyISAM; CREATE INDEX hash_idx ON dialog (hash_entry, hash_id); INSERT INTO version (table_name, table_version) values ('dialog_vars','1'); CREATE TABLE dialog_vars ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, hash_entry INT(10) UNSIGNED NOT NULL, hash_id INT(10) UNSIGNED NOT NULL, dialog_key VARCHAR(128) NOT NULL, dialog_value VARCHAR(512) NOT NULL ) ENGINE=MyISAM; CREATE INDEX hash_idx ON dialog_vars (hash_entry, hash_id); kamailio-4.0.4/utils/kamctl/mysql/acc-create.sql0000644000000000000000000000202112223032461020261 0ustar rootrootINSERT INTO version (table_name, table_version) values ('acc','4'); CREATE TABLE acc ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, method VARCHAR(16) DEFAULT '' NOT NULL, from_tag VARCHAR(64) DEFAULT '' NOT NULL, to_tag VARCHAR(64) DEFAULT '' NOT NULL, callid VARCHAR(255) DEFAULT '' NOT NULL, sip_code VARCHAR(3) DEFAULT '' NOT NULL, sip_reason VARCHAR(32) DEFAULT '' NOT NULL, time DATETIME NOT NULL ) ENGINE=MyISAM; CREATE INDEX callid_idx ON acc (callid); INSERT INTO version (table_name, table_version) values ('missed_calls','3'); CREATE TABLE missed_calls ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, method VARCHAR(16) DEFAULT '' NOT NULL, from_tag VARCHAR(64) DEFAULT '' NOT NULL, to_tag VARCHAR(64) DEFAULT '' NOT NULL, callid VARCHAR(255) DEFAULT '' NOT NULL, sip_code VARCHAR(3) DEFAULT '' NOT NULL, sip_reason VARCHAR(32) DEFAULT '' NOT NULL, time DATETIME NOT NULL ) ENGINE=MyISAM; CREATE INDEX callid_idx ON missed_calls (callid); kamailio-4.0.4/utils/kamctl/mysql/pipelimit-create.sql0000644000000000000000000000045512223032461021540 0ustar rootrootINSERT INTO version (table_name, table_version) values ('pl_pipes','1'); CREATE TABLE pl_pipes ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, pipeid VARCHAR(64) DEFAULT '' NOT NULL, algorithm VARCHAR(32) DEFAULT '' NOT NULL, plimit INT DEFAULT 0 NOT NULL ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/uid_auth_db-create.sql0000644000000000000000000000127112223032461022010 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_credentials','7'); CREATE TABLE uid_credentials ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, auth_username VARCHAR(64) NOT NULL, did VARCHAR(64) DEFAULT '_default' NOT NULL, realm VARCHAR(64) NOT NULL, password VARCHAR(28) DEFAULT '' NOT NULL, flags INT DEFAULT 0 NOT NULL, ha1 VARCHAR(32) NOT NULL, ha1b VARCHAR(32) DEFAULT '' NOT NULL, uid VARCHAR(64) NOT NULL ) ENGINE=MyISAM; CREATE INDEX cred_idx ON uid_credentials (auth_username, did); CREATE INDEX uid ON uid_credentials (uid); CREATE INDEX did_idx ON uid_credentials (did); CREATE INDEX realm_idx ON uid_credentials (realm); kamailio-4.0.4/utils/kamctl/mysql/mtree-create.sql0000644000000000000000000000125212223032461020654 0ustar rootrootINSERT INTO version (table_name, table_version) values ('mtree','1'); CREATE TABLE mtree ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, tprefix VARCHAR(32) DEFAULT '' NOT NULL, tvalue VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT tprefix_idx UNIQUE (tprefix) ) ENGINE=MyISAM; INSERT INTO version (table_name, table_version) values ('mtrees','2'); CREATE TABLE mtrees ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, tname VARCHAR(128) DEFAULT '' NOT NULL, tprefix VARCHAR(32) DEFAULT '' NOT NULL, tvalue VARCHAR(128) DEFAULT '' NOT NULL, CONSTRAINT tname_tprefix_tvalue_idx UNIQUE (tname, tprefix, tvalue) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/imc-create.sql0000644000000000000000000000126612223032461020315 0ustar rootrootINSERT INTO version (table_name, table_version) values ('imc_rooms','1'); CREATE TABLE imc_rooms ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, name VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, flag INT(11) NOT NULL, CONSTRAINT name_domain_idx UNIQUE (name, domain) ) ENGINE=MyISAM; INSERT INTO version (table_name, table_version) values ('imc_members','1'); CREATE TABLE imc_members ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, room VARCHAR(64) NOT NULL, flag INT(11) NOT NULL, CONSTRAINT account_room_idx UNIQUE (username, domain, room) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/userblacklist-create.sql0000644000000000000000000000147112223032461022412 0ustar rootrootINSERT INTO version (table_name, table_version) values ('userblacklist','1'); CREATE TABLE userblacklist ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, prefix VARCHAR(64) DEFAULT '' NOT NULL, whitelist TINYINT(1) DEFAULT 0 NOT NULL ) ENGINE=MyISAM; CREATE INDEX userblacklist_idx ON userblacklist (username, domain, prefix); INSERT INTO version (table_name, table_version) values ('globalblacklist','1'); CREATE TABLE globalblacklist ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, prefix VARCHAR(64) DEFAULT '' NOT NULL, whitelist TINYINT(1) DEFAULT 0 NOT NULL, description VARCHAR(255) DEFAULT NULL ) ENGINE=MyISAM; CREATE INDEX globalblacklist_idx ON globalblacklist (prefix); kamailio-4.0.4/utils/kamctl/mysql/alias_db-create.sql0000644000000000000000000000075312223032461021303 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dbaliases','1'); CREATE TABLE dbaliases ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, alias_username VARCHAR(64) DEFAULT '' NOT NULL, alias_domain VARCHAR(64) DEFAULT '' NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, CONSTRAINT alias_idx UNIQUE (alias_username, alias_domain) ) ENGINE=MyISAM; CREATE INDEX target_idx ON dbaliases (username, domain); kamailio-4.0.4/utils/kamctl/mysql/uac-create.sql0000644000000000000000000000124512223032461020312 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uacreg','1'); CREATE TABLE uacreg ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, l_uuid VARCHAR(64) DEFAULT '' NOT NULL, l_username VARCHAR(64) DEFAULT '' NOT NULL, l_domain VARCHAR(128) DEFAULT '' NOT NULL, r_username VARCHAR(64) DEFAULT '' NOT NULL, r_domain VARCHAR(128) DEFAULT '' NOT NULL, realm VARCHAR(64) DEFAULT '' NOT NULL, auth_username VARCHAR(64) DEFAULT '' NOT NULL, auth_password VARCHAR(64) DEFAULT '' NOT NULL, auth_proxy VARCHAR(64) DEFAULT '' NOT NULL, expires INT DEFAULT 0 NOT NULL, CONSTRAINT l_uuid_idx UNIQUE (l_uuid) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/dispatcher-create.sql0000644000000000000000000000065312223032461021672 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dispatcher','4'); CREATE TABLE dispatcher ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, setid INT DEFAULT 0 NOT NULL, destination VARCHAR(192) DEFAULT '' NOT NULL, flags INT DEFAULT 0 NOT NULL, priority INT DEFAULT 0 NOT NULL, attrs VARCHAR(128) DEFAULT '' NOT NULL, description VARCHAR(64) DEFAULT '' NOT NULL ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/speeddial-create.sql0000644000000000000000000000116312223032461021473 0ustar rootrootINSERT INTO version (table_name, table_version) values ('speed_dial','2'); CREATE TABLE speed_dial ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, sd_username VARCHAR(64) DEFAULT '' NOT NULL, sd_domain VARCHAR(64) DEFAULT '' NOT NULL, new_uri VARCHAR(128) DEFAULT '' NOT NULL, fname VARCHAR(64) DEFAULT '' NOT NULL, lname VARCHAR(64) DEFAULT '' NOT NULL, description VARCHAR(64) DEFAULT '' NOT NULL, CONSTRAINT speed_dial_idx UNIQUE (username, domain, sd_domain, sd_username) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/siptrace-create.sql0000644000000000000000000000155512223032461021360 0ustar rootrootINSERT INTO version (table_name, table_version) values ('sip_trace','3'); CREATE TABLE sip_trace ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, time_stamp DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, time_us INT UNSIGNED DEFAULT 0 NOT NULL, callid VARCHAR(255) DEFAULT '' NOT NULL, traced_user VARCHAR(128) DEFAULT '' NOT NULL, msg MEDIUMTEXT NOT NULL, method VARCHAR(50) DEFAULT '' NOT NULL, status VARCHAR(128) DEFAULT '' NOT NULL, fromip VARCHAR(50) DEFAULT '' NOT NULL, toip VARCHAR(50) DEFAULT '' NOT NULL, fromtag VARCHAR(64) DEFAULT '' NOT NULL, direction VARCHAR(4) DEFAULT '' NOT NULL ) ENGINE=MyISAM; CREATE INDEX traced_user_idx ON sip_trace (traced_user); CREATE INDEX date_idx ON sip_trace (time_stamp); CREATE INDEX fromip_idx ON sip_trace (fromip); CREATE INDEX callid_idx ON sip_trace (callid); kamailio-4.0.4/utils/kamctl/mysql/dialplan-create.sql0000644000000000000000000000065512223032461021332 0ustar rootrootINSERT INTO version (table_name, table_version) values ('dialplan','1'); CREATE TABLE dialplan ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, dpid INT(11) NOT NULL, pr INT(11) NOT NULL, match_op INT(11) NOT NULL, match_exp VARCHAR(64) NOT NULL, match_len INT(11) NOT NULL, subst_exp VARCHAR(64) NOT NULL, repl_exp VARCHAR(32) NOT NULL, attrs VARCHAR(32) NOT NULL ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/group-create.sql0000644000000000000000000000133412223032461020675 0ustar rootrootINSERT INTO version (table_name, table_version) values ('grp','2'); CREATE TABLE grp ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, grp VARCHAR(64) DEFAULT '' NOT NULL, last_modified DATETIME DEFAULT '1900-01-01 00:00:01' NOT NULL, CONSTRAINT account_group_idx UNIQUE (username, domain, grp) ) ENGINE=MyISAM; INSERT INTO version (table_name, table_version) values ('re_grp','1'); CREATE TABLE re_grp ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, reg_exp VARCHAR(128) DEFAULT '' NOT NULL, group_id INT(11) DEFAULT 0 NOT NULL ) ENGINE=MyISAM; CREATE INDEX group_idx ON re_grp (group_id); kamailio-4.0.4/utils/kamctl/mysql/lcr-create.sql0000644000000000000000000000312512223032461020321 0ustar rootrootINSERT INTO version (table_name, table_version) values ('lcr_gw','3'); CREATE TABLE lcr_gw ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, lcr_id SMALLINT UNSIGNED NOT NULL, gw_name VARCHAR(128), ip_addr VARCHAR(50), hostname VARCHAR(64), port SMALLINT UNSIGNED, params VARCHAR(64), uri_scheme TINYINT UNSIGNED, transport TINYINT UNSIGNED, strip TINYINT UNSIGNED, prefix VARCHAR(16) DEFAULT NULL, tag VARCHAR(64) DEFAULT NULL, flags INT UNSIGNED DEFAULT 0 NOT NULL, defunct INT UNSIGNED DEFAULT NULL ) ENGINE=MyISAM; CREATE INDEX lcr_id_idx ON lcr_gw (lcr_id); INSERT INTO version (table_name, table_version) values ('lcr_rule_target','1'); CREATE TABLE lcr_rule_target ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, lcr_id SMALLINT UNSIGNED NOT NULL, rule_id INT UNSIGNED NOT NULL, gw_id INT UNSIGNED NOT NULL, priority TINYINT UNSIGNED NOT NULL, weight INT UNSIGNED DEFAULT 1 NOT NULL, CONSTRAINT rule_id_gw_id_idx UNIQUE (rule_id, gw_id) ) ENGINE=MyISAM; CREATE INDEX lcr_id_idx ON lcr_rule_target (lcr_id); INSERT INTO version (table_name, table_version) values ('lcr_rule','2'); CREATE TABLE lcr_rule ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, lcr_id SMALLINT UNSIGNED NOT NULL, prefix VARCHAR(16) DEFAULT NULL, from_uri VARCHAR(64) DEFAULT NULL, request_uri VARCHAR(64) DEFAULT NULL, stopper INT UNSIGNED DEFAULT 0 NOT NULL, enabled INT UNSIGNED DEFAULT 1 NOT NULL, CONSTRAINT lcr_id_prefix_from_uri_idx UNIQUE (lcr_id, prefix, from_uri) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/auth_db-create.sql0000644000000000000000000000111712223032461021146 0ustar rootrootINSERT INTO version (table_name, table_version) values ('subscriber','6'); CREATE TABLE subscriber ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username VARCHAR(64) DEFAULT '' NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, password VARCHAR(25) DEFAULT '' NOT NULL, email_address VARCHAR(64) DEFAULT '' NOT NULL, ha1 VARCHAR(64) DEFAULT '' NOT NULL, ha1b VARCHAR(64) DEFAULT '' NOT NULL, rpid VARCHAR(64) DEFAULT NULL, CONSTRAINT account_idx UNIQUE (username, domain) ) ENGINE=MyISAM; CREATE INDEX username_idx ON subscriber (username); kamailio-4.0.4/utils/kamctl/mysql/presence-create.sql0000644000000000000000000001025012223032461021342 0ustar rootrootINSERT INTO version (table_name, table_version) values ('presentity','3'); CREATE TABLE presentity ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, event VARCHAR(64) NOT NULL, etag VARCHAR(64) NOT NULL, expires INT(11) NOT NULL, received_time INT(11) NOT NULL, body BLOB NOT NULL, sender VARCHAR(128) NOT NULL, CONSTRAINT presentity_idx UNIQUE (username, domain, event, etag) ) ENGINE=MyISAM; CREATE INDEX presentity_expires ON presentity (expires); CREATE INDEX account_idx ON presentity (username, domain, event); INSERT INTO version (table_name, table_version) values ('active_watchers','11'); CREATE TABLE active_watchers ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, presentity_uri VARCHAR(128) NOT NULL, watcher_username VARCHAR(64) NOT NULL, watcher_domain VARCHAR(64) NOT NULL, to_user VARCHAR(64) NOT NULL, to_domain VARCHAR(64) NOT NULL, event VARCHAR(64) DEFAULT 'presence' NOT NULL, event_id VARCHAR(64), to_tag VARCHAR(64) NOT NULL, from_tag VARCHAR(64) NOT NULL, callid VARCHAR(255) NOT NULL, local_cseq INT(11) NOT NULL, remote_cseq INT(11) NOT NULL, contact VARCHAR(128) NOT NULL, record_route TEXT, expires INT(11) NOT NULL, status INT(11) DEFAULT 2 NOT NULL, reason VARCHAR(64) NOT NULL, version INT(11) DEFAULT 0 NOT NULL, socket_info VARCHAR(64) NOT NULL, local_contact VARCHAR(128) NOT NULL, from_user VARCHAR(64) NOT NULL, from_domain VARCHAR(64) NOT NULL, updated INT(11) NOT NULL, updated_winfo INT(11) NOT NULL, CONSTRAINT active_watchers_idx UNIQUE (callid, to_tag, from_tag) ) ENGINE=MyISAM; CREATE INDEX active_watchers_expires ON active_watchers (expires); CREATE INDEX active_watchers_pres ON active_watchers (presentity_uri, event); CREATE INDEX updated_idx ON active_watchers (updated); CREATE INDEX updated_winfo_idx ON active_watchers (updated_winfo, presentity_uri); INSERT INTO version (table_name, table_version) values ('watchers','3'); CREATE TABLE watchers ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, presentity_uri VARCHAR(128) NOT NULL, watcher_username VARCHAR(64) NOT NULL, watcher_domain VARCHAR(64) NOT NULL, event VARCHAR(64) DEFAULT 'presence' NOT NULL, status INT(11) NOT NULL, reason VARCHAR(64), inserted_time INT(11) NOT NULL, CONSTRAINT watcher_idx UNIQUE (presentity_uri, watcher_username, watcher_domain, event) ) ENGINE=MyISAM; INSERT INTO version (table_name, table_version) values ('xcap','4'); CREATE TABLE xcap ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, doc MEDIUMBLOB NOT NULL, doc_type INT(11) NOT NULL, etag VARCHAR(64) NOT NULL, source INT(11) NOT NULL, doc_uri VARCHAR(255) NOT NULL, port INT(11) NOT NULL, CONSTRAINT doc_uri_idx UNIQUE (doc_uri) ) ENGINE=MyISAM; CREATE INDEX account_doc_type_idx ON xcap (username, domain, doc_type); CREATE INDEX account_doc_type_uri_idx ON xcap (username, domain, doc_type, doc_uri); CREATE INDEX account_doc_uri_idx ON xcap (username, domain, doc_uri); INSERT INTO version (table_name, table_version) values ('pua','7'); CREATE TABLE pua ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, pres_uri VARCHAR(128) NOT NULL, pres_id VARCHAR(255) NOT NULL, event INT(11) NOT NULL, expires INT(11) NOT NULL, desired_expires INT(11) NOT NULL, flag INT(11) NOT NULL, etag VARCHAR(64) NOT NULL, tuple_id VARCHAR(64), watcher_uri VARCHAR(128) NOT NULL, call_id VARCHAR(255) NOT NULL, to_tag VARCHAR(64) NOT NULL, from_tag VARCHAR(64) NOT NULL, cseq INT(11) NOT NULL, record_route TEXT, contact VARCHAR(128) NOT NULL, remote_contact VARCHAR(128) NOT NULL, version INT(11) NOT NULL, extra_headers TEXT NOT NULL, CONSTRAINT pua_idx UNIQUE (etag, tuple_id, call_id, from_tag) ) ENGINE=MyISAM; CREATE INDEX expires_idx ON pua (expires); CREATE INDEX dialog1_idx ON pua (pres_id, pres_uri); CREATE INDEX dialog2_idx ON pua (call_id, from_tag); CREATE INDEX record_idx ON pua (pres_id); kamailio-4.0.4/utils/kamctl/mysql/uid_avp_db-create.sql0000644000000000000000000000062412223032461021636 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_user_attrs','3'); CREATE TABLE uid_user_attrs ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, uid VARCHAR(64) NOT NULL, name VARCHAR(32) NOT NULL, value VARCHAR(128), type INT DEFAULT 0 NOT NULL, flags INT UNSIGNED DEFAULT 0 NOT NULL, CONSTRAINT userattrs_idx UNIQUE (uid, name, value) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/mysql/rls-create.sql0000644000000000000000000000367312223032461020351 0ustar rootrootINSERT INTO version (table_name, table_version) values ('rls_presentity','1'); CREATE TABLE rls_presentity ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, rlsubs_did VARCHAR(255) NOT NULL, resource_uri VARCHAR(128) NOT NULL, content_type VARCHAR(255) NOT NULL, presence_state BLOB NOT NULL, expires INT(11) NOT NULL, updated INT(11) NOT NULL, auth_state INT(11) NOT NULL, reason VARCHAR(64) NOT NULL, CONSTRAINT rls_presentity_idx UNIQUE (rlsubs_did, resource_uri) ) ENGINE=MyISAM; CREATE INDEX rlsubs_idx ON rls_presentity (rlsubs_did); CREATE INDEX updated_idx ON rls_presentity (updated); CREATE INDEX expires_idx ON rls_presentity (expires); INSERT INTO version (table_name, table_version) values ('rls_watchers','3'); CREATE TABLE rls_watchers ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, presentity_uri VARCHAR(128) NOT NULL, to_user VARCHAR(64) NOT NULL, to_domain VARCHAR(64) NOT NULL, watcher_username VARCHAR(64) NOT NULL, watcher_domain VARCHAR(64) NOT NULL, event VARCHAR(64) DEFAULT 'presence' NOT NULL, event_id VARCHAR(64), to_tag VARCHAR(64) NOT NULL, from_tag VARCHAR(64) NOT NULL, callid VARCHAR(255) NOT NULL, local_cseq INT(11) NOT NULL, remote_cseq INT(11) NOT NULL, contact VARCHAR(128) NOT NULL, record_route TEXT, expires INT(11) NOT NULL, status INT(11) DEFAULT 2 NOT NULL, reason VARCHAR(64) NOT NULL, version INT(11) DEFAULT 0 NOT NULL, socket_info VARCHAR(64) NOT NULL, local_contact VARCHAR(128) NOT NULL, from_user VARCHAR(64) NOT NULL, from_domain VARCHAR(64) NOT NULL, updated INT(11) NOT NULL, CONSTRAINT rls_watcher_idx UNIQUE (callid, to_tag, from_tag) ) ENGINE=MyISAM; CREATE INDEX rls_watchers_update ON rls_watchers (watcher_username, watcher_domain, event); CREATE INDEX rls_watchers_expires ON rls_watchers (expires); CREATE INDEX updated_idx ON rls_watchers (updated); kamailio-4.0.4/utils/kamctl/mysql/uid_domain-create.sql0000644000000000000000000000147712223032461021661 0ustar rootrootINSERT INTO version (table_name, table_version) values ('uid_domain','2'); CREATE TABLE uid_domain ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, did VARCHAR(64) NOT NULL, domain VARCHAR(64) NOT NULL, flags INT UNSIGNED DEFAULT 0 NOT NULL, CONSTRAINT domain_idx UNIQUE (domain) ) ENGINE=MyISAM; CREATE INDEX did_idx ON uid_domain (did); INSERT INTO version (table_name, table_version) values ('uid_domain_attrs','1'); CREATE TABLE uid_domain_attrs ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, did VARCHAR(64), name VARCHAR(32) NOT NULL, type INT DEFAULT 0 NOT NULL, value VARCHAR(128), flags INT UNSIGNED DEFAULT 0 NOT NULL, CONSTRAINT domain_attr_idx UNIQUE (did, name, value) ) ENGINE=MyISAM; CREATE INDEX domain_did ON uid_domain_attrs (did, flags); kamailio-4.0.4/utils/kamctl/mysql/cpl-create.sql0000644000000000000000000000051612223032461020320 0ustar rootrootINSERT INTO version (table_name, table_version) values ('cpl','1'); CREATE TABLE cpl ( id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, username VARCHAR(64) NOT NULL, domain VARCHAR(64) DEFAULT '' NOT NULL, cpl_xml TEXT, cpl_bin TEXT, CONSTRAINT account_idx UNIQUE (username, domain) ) ENGINE=MyISAM; kamailio-4.0.4/utils/kamctl/kamctl.ctlbase0000644000000000000000000000463212223032461017230 0ustar rootroot# # $Id$ # # control tool for maintaining Kamailio # #=================================================================== [ "${IMPCTLBASE}" = "yes" ] && return export IMPCTLBASE="yes" ##### ----------------------------------------------- ##### ### common variables and functions for CTL engines # # period in which stats are reprinted if [ -z "$WATCH_PERIOD" ] ; then WATCH_PERIOD=2 fi # ##### ------------------------------------------------ ##### ### usage functions # usage_cisco_restart() { echo mecho " -- command 'cisco_restart' - restart CISCO phone (NOTIFY)" echo cat < ................ restart phone configured for EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_cisco_restart" usage_online() { echo mecho " -- command 'online' - dump online users from memory" echo cat < ......................... ping with SIP OPTIONS EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_ping" usage_usrloc() { echo mecho " -- command 'ul|alias' - manage user location or aliases" echo cat <]................ show in-RAM online users ul show --brief..................... show in-RAM online users in short format ul rm [].... delete user's usrloc entries ul add ............ introduce a permanent usrloc entry ul add .. introduce a temporary usrloc entry EOF } USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_usrloc" usage_ctlcommon() { echo mecho " -- command 'ps' - print details about running processes" echo cat < { if [ $# -ne 1 ] ; then merr "kamailio_drop function takes one param" exit 1 fi DB_PATH=$1 minfo "DBTEXT ... erasing all files at: $DB_PATH" rm -rf $DB_PATH } kamailio_create () # pars: { if [ $# -ne 1 ] ; then merr "kamailio_create function takes one param (DB_PATH)" exit 1 fi DB_PATH=$1 minfo "creating DBTEXT tables at: $DB_PATH ..." mkdir -p $DB_PATH for TABLE in $STANDARD_TABLES; do mdbg "Creating core table: $TABLE" cp $DB_SCHEMA/$TABLE $DB_PATH/$TABLE if [ $? -ne 0 ] ; then merr "Creating core tables failed!" exit 1 fi done get_answer $INSTALL_PRESENCE_TABLES "Install presence related tables? (y/n): " if [ "$ANSWER" = "y" ]; then presence_create $1 fi get_answer $INSTALL_EXTRA_TABLES "Install tables for $EXTRA_MODULES? (y/n): " if [ "$ANSWER" = "y" ]; then extra_create $1 fi } presence_create () # pars: { if [ $# -ne 1 ] ; then merr "presence_create function takes one param (DB_PATH)" exit 1 fi DB_PATH=$1 minfo "creating DBTEXT presence tables at: $DB_PATH ..." mkdir -p $DB_PATH for TABLE in $PRESENCE_TABLES; do mdbg "Creating presence table: $TABLE" cp $DB_SCHEMA/$TABLE $DB_PATH/$TABLE if [ $? -ne 0 ] ; then merr "Creating presence tables failed!" exit 1 fi done } # end presence_create extra_create () # pars: { if [ $# -ne 1 ] ; then merr "extra_create function takes one param" exit 1 fi minfo "creating DBTEXT extra tables at: $DB_PATH ..." for TABLE in $EXTRA_TABLES; do mdbg "Creating extra table: $TABLE" cp $DB_SCHEMA/$TABLE $DB_PATH/$TABLE if [ $? -ne 0 ] ; then merr "Creating extra tables failed!" exit 1 fi done } # end extra_create kamailio-4.0.4/utils/kamctl/kamdbfunc.oracle0000644000000000000000000000231312223032460017530 0ustar rootroot#!/bin/sh # $Id$ # # Script for common functions for Oracle engine in Kamailio # # History: if [ -z "$EGREP" ]; then EGREP="egrep" fi # read any password prompt_oracle_pw() { case $1 in rw) if [ -n "$DBRWPW" ]; then return fi CURDBUSER="$DBRWUSER" ;; ro) if [ -n "$DBROPW" ]; then return fi CURDBUSER="$DBROUSER" ;; root) if [ -n "$DBROOTPW" ]; then return fi CURDBUSER="$DBROOTUSER" ;; sys) if [ -n "$DBSYSPW" ]; then return fi CURDBUSER="$DBSYSUSER" ;; *) merr "prompt_oracle_pw: argument error" exit 1 ;; esac savetty=`stty -g` echo -n "Oracle password for $CURDBUSER: " stty -echo case $1 in rw) read DBRWPW export DBRWPW CURPW=$DBRWPW ;; ro) read DBROPW export DBROPW CURPW=$DBROPW ;; root) read DBROOTPW export DBROOTPW CURPW=$DBROOTPW ;; sys) read DBSYSPW export DBSYSPW CURPW=$DBSYSPW ;; esac stty $savetty echo if [ -z "$CURPW" ]; then merr "empty password is illegal" exit 1 fi } check_oracle_log() { if [ -f $ORALOG ]; then $EGREP -qi "error" $ORALOG if [ $? -eq 0 ]; then echo "NOTE: last errors stored in $ORALOG" return 0 fi rm $ORALOG fi return 1 } kamailio-4.0.4/utils/kamctl/kamctl0000755000000000000000000015146312223032461015624 0ustar rootroot#!/bin/bash # # $Id$ # # control tool for maintaining Kamailio # #=================================================================== ### version for this script VERSION='3.3.0' PATH=$PATH:/usr/local/sbin/ # for testing only, please don't enable this in production environments # as this introduce security risks TEST="false" ### include config files if [ -f /etc/kamailio/kamctlrc ]; then . /etc/kamailio/kamctlrc fi if [ -f /usr/local/etc/kamailio/kamctlrc ]; then . /usr/local/etc/kamailio/kamctlrc fi if [ -f ~/.kamctlrc ]; then . ~/.kamctlrc fi if [ $TEST = "true" ]; then if [ -f ./kamctlrc ]; then . ./kamctlrc fi fi ### force values for variables in this section # you better set the variables in ~/.kamctlrc if [ -z "$ETCDIR" ] ; then ETCDIR="/usr/local/etc/kamailio" fi if [ -z "$MYDIR" ] ; then MYDIR=`dirname $0` fi if [ -z "$MYLIBDIR" ] ; then MYLIBDIR="/usr/local/lib/kamailio/kamctl" if [ ! -d "$MYLIBDIR" ]; then MYLIBDIR=$MYDIR fi fi ##### ------------------------------------------------ ##### ### load base functions # if [ -f "$MYLIBDIR/kamctl.base" ]; then . "$MYLIBDIR/kamctl.base" else echo -e "Cannot load core functions '$MYLIBDIR/kamctl.base' - exiting ...\n" exit -1 fi # locate kamcmd if [ -z "$SERCMD" ] ; then # try same dir as kamctl SERCMD="$MYDIR/kamcmd" if [ ! -f "$SERCMD" -o ! -x "$SERCMD" ] ; then # try standard location installed from sources SERCMD="/usr/local/sbin/kamcmd" if [ ! -f "$SERCMD" -o ! -x "$SERCMD" ] ; then # try source tree location SERCMD="$MYDIR/../sercmd/kamcmd" if [ ! -f "$SERCMD" -o ! -x "$SERCMD" ] ; then # try locate it with which SERCMD=`which kamcmd` if [ ! -f "$SERCMD" -o ! -x "$SERCMD" ] ; then mdbg "kamcmd tool not found" fi fi fi fi else if [ ! -f "$SERCMD" -o ! -x "$SERCMD" ] ; then merr "SERCMD does not point to an executable file" exit -1; fi fi # ##### ------------------------------------------------ ##### ### DBENGINE # DBENGINELOADED=0 case $DBENGINE in MYSQL|mysql|MySQL) if [ -f "$MYLIBDIR/kamctl.mysql" ]; then . "$MYLIBDIR/kamctl.mysql" DBENGINELOADED=1 fi ;; PGSQL|pgsql|postgres|postgresql|POSTGRESQL) if [ -f "$MYLIBDIR/kamctl.pgsql" ]; then . "$MYLIBDIR/kamctl.pgsql" DBENGINELOADED=1 fi ;; ORACLE|oracle|Oracle) if [ -f "$MYLIBDIR/kamctl.oracle" ]; then . "$MYLIBDIR/kamctl.oracle" DBENGINELOADED=1 fi ;; DBTEXT|dbtext|textdb) if [ -f "$MYLIBDIR/kamctl.dbtext" ]; then . "$MYLIBDIR/kamctl.dbtext" DBENGINELOADED=1 fi ;; DB_BERKELEY|db_berkeley|BERKELEY|berkeley) if [ -f "$MYLIBDIR/kamctl.db_berkeley" ]; then . "$MYLIBDIR/kamctl.db_berkeley" DBENGINELOADED=1 fi ;; SQLITE|sqlite) if [ -f "$MYLIBDIR/kamctl.sqlite" ]; then . "$MYLIBDIR/kamctl.sqlite" DBENGINELOADED=1 fi ;; esac if [ $DBENGINELOADED -eq 1 ] ; then mdbg "database engine '$DBENGINE' loaded" elif [ -n "$DBENGINE" ] ; then mwarn "database engine not found - tried '$DBENGINE'" fi # ##### ------------------------------------------------ ##### ### CTLENGINE # require_kamcmd() { if [ -z "$SERCMD" ] ; then merr "kamcmd tool is missing" exit -1 fi } CTLENGINELOADED=0 if [ -z "$CTLENGINE" ] ; then CTLENGINE="FIFO" fi case $CTLENGINE in FIFO|fifo) if [ -f "$MYLIBDIR/kamctl.fifo" ]; then . "$MYLIBDIR/kamctl.fifo" CTLENGINELOADED=1 fi ;; UNIXSOCK|unixsock) if [ -f "$MYLIBDIR/kamctl.unixsock" ]; then . "$MYLIBDIR/kamctl.unixsock" CTLENGINELOADED=1 fi ;; SER_MI|ser_mi|SERCMD_MI|sercmd_mi|SERCMDMI|sercmdmi) require_kamcmd if [ -f "$MYLIBDIR/kamctl.ser_mi" ]; then . "$MYLIBDIR/kamctl.ser_mi" CTLENGINELOADED=1 fi ;; esac #### ------------------------------------------------- ##### ### Load kamcmd interface # if [ -f "$MYLIBDIR/kamctl.ser" ]; then . "$MYLIBDIR/kamctl.ser" fi if [ $CTLENGINELOADED -eq 1 ] ; then mdbg "Control engine '$CTLENGINE' loaded" else mwarn "no control engine found - tried '$CTLENGINE'" fi # ##### ------------------------------------------------ ##### ### common functions # usage() { CMD=`basename $0` if [ "0$VERIFY_ACL" -eq 1 ] ; then EXTRA_TEXT="ACL privileges are: $ACL_GROUPS" fi cat < /dev/null if [ $? -ne 0 ] ; then echo "$RES" | $EGREP "^400" > /dev/null if [ $? -eq 0 ] ; then merr "400; check if you use aliases in Kamailio" exit 1 fi echo "$RES" | $EGREP "^200" > /dev/null if [ $? -eq 0 ] ; then ALIAS_UL_EXISTS=1 fi # other errors merr "$RES" exit 1 fi } check_db_alias() { require_dbengine ALIAS_DB_EXISTS=0 QUERY="select count(*) from $DA_TABLE where $DA_ALIAS_USER_COLUMN='$1' \ and $DA_ALIAS_DOMAIN_COLUMN='$2';" CNT=`$DBROCMD "$QUERY" | $EGREP -v ERROR | $LAST_LINE` mdbg "check_db_alias: alias counter=$CNT" if [ "$CNT" = "0" ] ; then ALIAS_DB_EXISTS=0 else ALIAS_DB_EXISTS=1 fi } # # check for alias duplicates # params: user domain # output: false if exists, true otherwise check_alias() { ALIAS_EXISTS=0 if [ "$ENABLE_ALIASES" = "1" ] ; then check_ul_alias "$1" "$2" if [ "$ALIAS_UL_EXISTS" = "0" ] ; then ALIAS_EXISTS=0 else ALIAS_EXISTS=1 fi elif [ "$ENABLE_ALIASES" = "2" ] ; then check_db_alias "$1" "$2" if [ "$ALIAS_DB_EXISTS" = "0" ] ; then ALIAS_EXISTS=0 else ALIAS_EXISTS=1 fi fi } # db-based aliases alias_db() { if [ "$#" -lt 2 ] ; then merr "alias_db - too few parameters" echo usage_alias_db exit 1 fi require_dbengine shift case $1 in list) if [ $# -eq 2 ] ; then # print aliases for user check_aor "$2" if [ "$?" -ne "0" ] ; then merr "alias_db - <$2> is not a valid AoR (user@domain)" exit 1 fi set_user $2 CLAUSE="WHERE $DA_USER_COLUMN='$OSERUSER' AND \ $DA_DOMAIN_COLUMN='$OSERDOMAIN'" mecho "Dumping aliases for user=<$2>" echo QUERY="SELECT CONCAT($DA_ALIAS_USER_COLUMN,\ '@',$DA_ALIAS_DOMAIN_COLUMN) AS ALIAS FROM $DA_TABLE $CLAUSE;" $DBROCMD "$QUERY" # | $AWK 'BEGIN {line=0;} # /^\+/ { next } # { if(line==0) print "ALIASES"; # else print line ")\t" $1 "@" $2; # line++; }' elif [ $# -eq 1 ] ; then mecho "Dumping all aliases may take long: do you want to proceed? [Y|N] " read answer if [ "$answer" = "y" -o "$answer" = "Y" ] ; then mecho "Dumping all aliases..." echo else exit 1 fi QUERY="SELECT $DA_ALIAS_USER_COLUMN, $DA_ALIAS_DOMAIN_COLUMN,\ $DA_USER_COLUMN, $DA_DOMAIN_COLUMN FROM $DA_TABLE;" $DBROCMD "$QUERY" # | $AWK 'BEGIN {line=0;} # /^\+/ { next } # { line++; # if(line==1) print "SIP-ID \tALIAS\n"; # else print $3 "@" $4 "\t" $1 "@" $2 }' else merr "alias_db - wrong number of params for command [list]" echo usage_alias_db exit 1 fi exit $? ;; show) if [ $# -ne 2 ] ; then merr "alias_db - wrong number of params for command [show]" usage_alias_db exit 1 fi check_aor "$2" if [ "$?" -ne "0" ] ; then merr "alias_db - $2 is not a valid AoR (user@domain)" exit 1 fi set_user $2 CLAUSE="WHERE $DA_ALIAS_USER_COLUMN='$OSERUSER' AND \ $DA_ALIAS_DOMAIN_COLUMN='$OSERDOMAIN'" QUERY="SELECT CONCAT($DA_USER_COLUMN,'@',$DA_DOMAIN_COLUMN) \ AS 'SIP-ID' FROM $DA_TABLE $CLAUSE ; " $DBROCMD "$QUERY" #TMP_UUID=`sql_ro_query "$QUERY" | $AWK 'BEGIN {line=0;} # /^\+/ { next } # { line++; # if(line==2) print $1 "@" $2;}'` # #if [ "$TMP_UUID" = "" ] ; then # mecho "non-existent alias <$2>" # exit 1 #fi # #echo "Details for alias <$2>" #echo #echo "SIP-ID: $TMP_UUID" #echo #exit $? ;; add) if [ $# -ne 3 ] ; then usage_alias_db exit 1 fi shift check_aor "$1" if [ "$?" -ne "0" ] ; then err "alias_db - $1 is not a valid AoR (user@domain)" exit 1 fi check_aor "$2" if [ "$?" -ne "0" ] ; then err "alias_db - $2 is not a valid AoR (user@domain)" exit 1 fi set_user $1 TMP_OSERUSER=$OSERUSER TMP_OSERDOMAIN=$OSERDOMAIN set_user $2 if is_value_in_db $DA_TABLE $DA_ALIAS_USER_COLUMN $TMP_OSERUSER; then minfo "$TMP_OSERUSER alias already in $DA_TABLE table" exit 0 fi QUERY="INSERT INTO $DA_TABLE ($DA_USER_COLUMN,$DA_DOMAIN_COLUMN,\ $DA_ALIAS_USER_COLUMN,$DA_ALIAS_DOMAIN_COLUMN) VALUES ('$OSERUSER',\ '$OSERDOMAIN','$TMP_OSERUSER','$TMP_OSERDOMAIN' );" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "alias_db - SQL Error" exit 1 fi exit $? ;; rm) if [ $# -ne 2 ] ; then merr "alias_db - wrong numbers of parameters" usage_alias_db exit 1 fi shift check_aor "$1" if [ "$?" -ne "0" ] ; then merr "alias_db - $1 is not a valid URI" exit 1 fi set_user $1 CLAUSE="WHERE $DA_ALIAS_USER_COLUMN='$OSERUSER' AND \ $DA_ALIAS_DOMAIN_COLUMN='$OSERDOMAIN'" QUERY="DELETE FROM $DA_TABLE $CLAUSE;" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "alias_db - SQL Error" exit 1 fi exit $? ;; help) usage_alias_db "alone" ;; *) usage_alias_db exit 1 ;; esac } # end db-aliases # ##### ------------------------------------------------ ##### ### AVP management # # avp list [-T table] [-u ] # [-a attribute] [-v value] [-t type] ... list AVPs # avp add [-T table] # ............ add AVP (*) # avp rm [-T table] [-u ] # [-a attribute] [-v value] [-t type] ... remove AVP (*) avpops() { require_dbengine if [ "$#" -lt 2 ] ; then merr "avp - too few parameters" minfo "see '$0 avp help'" exit 1 fi if [ "$1" = "avp" ] ; then shift else merr "avp - unknown command $1" minfo "see '$0 avp help'" exit 1 fi case $1 in list) shift CLAUSE="" while [ "$#" != "0" ] do TMP_ARG=$1 shift case $TMP_ARG in -T) if [ -z "$1" ] ; then merr "avp list - table name parameter missing" exit 1 fi AVP_TABLE=$1 ;; -u) if [ -z "$1" ] ; then merr "avp list - user id or uuid parameter missing" exit 1 fi is_aor "$1" if [ "$?" -eq "0" ] ; then set_user $1 if [ "$CLAUSE" = "" ] ; then CLAUSE=" WHERE $AVP_USER_COLUMN='$OSERUSER' \ AND $AVP_DOMAIN_COLUMN='$OSERDOMAIN'" else CLAUSE="$CLAUSE AND \ $AVP_USER_COLUMN='$OSERUSER' AND $AVP_DOMAIN_COLUMN='$OSERDOMAIN'" fi else if [ "$CLAUSE" = "" ] ; then CLAUSE=" WHERE $AVP_UUID_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_UUID_COLUMN='$1'" fi fi ;; -a) if [ -z "$1" ] ; then merr "avp list - attribute name parameter missing" exit 1 fi if [ "$CLAUSE" = "" ] ; then CLAUSE=" WHERE $AVP_ATTRIBUTE_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_ATTRIBUTE_COLUMN='$1'" fi ;; -v) if [ -z "$1" ] ; then merr "avp list - value parameter missing" exit 1 fi if [ "$CLAUSE" = "" ] ; then CLAUSE=" WHERE $AVP_VALUE_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_VALUE_COLUMN='$1'" fi ;; -t) if [ -z "$1" ] ; then merr "avp list - type parameter missing" exit 1 fi if [ "$CLAUSE" = "" ] ; then CLAUSE=" WHERE $AVP_TYPE_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_TYPE_COLUMN='$1'" fi ;; *) merr "avp list - unknown parameter $1" exit 1 ;; esac shift done QUERY="SELECT $AVP_UUID_COLUMN,$AVP_USER_COLUMN,\ $AVP_DOMAIN_COLUMN,$AVP_ATTRIBUTE_COLUMN,$AVP_TYPE_COLUMN,$AVP_VALUE_COLUMN \ FROM $AVP_TABLE $CLAUSE;" mdbg "Query: $QUERY" mecho "Dumping AVPs" echo $DBROCMD "$QUERY" # | $AWK 'BEGIN {line=0;} # /^\+/ { next } # { if(line==0) print "## UUID \tUserID \tAttribute \tType \tValue\n"; # else { # ORS_BAK=ORS; # ORS=""; # print line ") " $1 $2 "@" $3 "\t" $4 "\t\"" $5; # for (i=6;i<=NF;++i) print FS $i; # ORS=ORS_BAK; # print "\""; # } # line++; # }' exit $? ;; add) shift if [ $# -ne 4 ] ; then if [ $# -ne 6 ] ; then merr "avp add - bad number of parameters" exit 1 fi fi if [ $# -eq 6 ] ; then if [ "$1" = "-T" ] ; then AVP_TABLE=$2 shift shift else mecho "avp add - unknown parameter '$1'" exit 1 fi fi is_aor "$1" if [ "$?" -eq "0" ] ; then set_user $1 else AVP_UUID=$1 fi QUERY="INSERT INTO $AVP_TABLE \ ($AVP_UUID_COLUMN,$AVP_USER_COLUMN,$AVP_DOMAIN_COLUMN,$AVP_ATTRIBUTE_COLUMN,\ $AVP_TYPE_COLUMN,$AVP_VALUE_COLUMN,$AVP_MODIFIED_COLUMN) \ VALUES ('$AVP_UUID','$OSERUSER','$OSERDOMAIN','$2',$3,'$4',NOW());" # echo "Query: $QUERY" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "avp add - SQL Error" exit 1 else echo mecho "avp add - attribute added" fi exit $? ;; rm) shift CLAUSE="" while [ "$#" != "0" ] do TMP_ARG=$1 shift case $TMP_ARG in -T) if [ -z "$1" ] ; then merr "avp rm - table name parameter missing" exit 1 fi AVP_TABLE=$1 ;; -u) if [ -z "$1" ] ; then merr "avp rm - user id or uuid parameter missing" exit 1 fi is_aor "$1" if [ "$?" -eq "0" ] ; then set_user $1 if [ "$CLAUSE" = "" ] ; then CLAUSE="WHERE $AVP_USER_COLUMN='$OSERUSER' \ AND $AVP_DOMAIN_COLUMN='$OSERDOMAIN'" else CLAUSE="$CLAUSE AND \ $AVP_USER_COLUMN='$OSERUSER' AND $AVP_DOMAIN_COLUMN='$OSERDOMAIN'" fi else if [ "$CLAUSE" = "" ] ; then CLAUSE="WHERE $AVP_UUID_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_UUID_COLUMN='$1'" fi fi ;; -a) if [ -z "$1" ] ; then merr "avp rm - attribute name parameter missing" exit 1 fi if [ "$CLAUSE" = "" ] ; then CLAUSE="WHERE $AVP_ATTRIBUTE_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_ATTRIBUTE_COLUMN='$1'" fi ;; -v) if [ -z "$1" ] ; then merr "avp rm - value parameter missing" exit 1 fi if [ "$CLAUSE" = "" ] ; then CLAUSE="WHERE $AVP_VALUE_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_VALUE_COLUMN='$1'" fi ;; -t) if [ -z "$1" ] ; then merr "avp rm - type parameter missing" exit 1 fi if [ "$CLAUSE" = "" ] ; then CLAUSE="WHERE $AVP_TYPE_COLUMN='$1'" else CLAUSE="$CLAUSE AND $AVP_TYPE_COLUMN='$1'" fi ;; *) merr "avp rm - unknown parameter $1" exit 1 ;; esac shift done QUERY="DELETE FROM $AVP_TABLE $CLAUSE;" mdbg "Query: $QUERY" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "avp rm - SQL Error" exit 1 else echo mecho "avp rm - AVP(s) deleted" fi exit $? ;; help) usage_avp ;; *) merr "avp - unknown command" usage exit 1 ;; esac } # end avpops() # ##### ------------------------------------------------ ##### ### cisco restart # cisco_restart() { require_ctlengine myhost=`get_my_host` CMD="t_uac_dlg NOTIFY $1 . . \"From:sip:daemon@$myhost\r\nTo:<$1>\r\nEvent:check-sync\r\nContact:sip:daemon@$myhost\r\n\"" RET=`$CTLCMD $CMD | head -1` print_status $RET } # ##### ------------------------------------------------ ##### ### DB operations # db_ops() { require_dbengine case $1 in exec|query) shift if [ $# -ne 1 ] ; then merr "missing query parameter" exit 1 fi $DBCMD "$1" ;; roexec|roquery) shift if [ $# -ne 1 ] ; then merr "missing query parameter" exit 1 fi $DBROCMD "$1" ;; run) shift if [ $# -ne 1 ] ; then merr "missing query parameter" exit 1 fi eval QUERY=\$$1 if [ -z "$QUERY" ] ; then merr "missing query value" exit 1 fi $DBCMD "$QUERY" ;; rorun) shift if [ $# -ne 1 ] ; then merr "missing query parameter" exit 1 fi eval QUERY=\$$1 if [ -z "$QUERY" ] ; then merr "missing query value" exit 1 fi $DBROCMD "$QUERY" ;; show) shift if [ $# -ne 1 ] ; then merr "missing table parameter" exit 1 fi QUERY="select * FROM $1;" $DBROCMD "$QUERY" ;; showg) shift if [ $# -ne 1 ] ; then merr "missing table parameter" exit 1 fi QUERY="select * FROM $1\\G;" $DBROCMD "$QUERY" ;; *) usage_db_ops exit 1 esac } # ##### ------------------------------------------------ ##### ### domain management # domain() { case $1 in reload) require_ctlengine $CTLCMD domain_reload ;; show) require_ctlengine $CTLCMD domain_dump ;; showdb) require_dbengine QUERY="select * FROM $DOMAIN_TABLE ; " $DBROCMD "$QUERY" ;; add) require_dbengine shift if [ $# -ne 1 ] ; then merr "missing domain parameter" exit 1 fi if is_value_in_db $DOMAIN_TABLE $DO_DOMAIN_COLUMN $1; then minfo "$1 already in $DOMAIN_TABLE table" exit 0 fi QUERY="insert into $DOMAIN_TABLE ($DO_DOMAIN_COLUMN, \ $DO_LAST_MODIFIED_COLUMN) VALUES ('$1',now());" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "domain - SQL Error" exit 1 fi minfo "execute '$0 domain reload' to synchronize cache and database" ;; rm) require_dbengine shift if [ $# -ne 1 ] ; then merr "missing domain parameter" exit 1 fi QUERY="delete from $DOMAIN_TABLE where domain='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "domain - SQL Error" exit 1 fi minfo "execute '$0 domain reload' to synchronize cache and database" ;; *) usage_domain exit 1 esac } # ##### ------------------------------------------------ ##### ### uid_domain management # uid_domain() { case $1 in reload) require_kamcmd $SERCTLCMD domain.reload ;; show) require_kamcmd $SERCTLCMD domain.dump ;; showdb) require_dbengine QUERY="select * FROM $UID_DOMAIN_TABLE ; " $DBROCMD "$QUERY" ;; add) require_dbengine shift if [ $# -lt 1 ] ; then merr "too few parameters" exit 1 fi DOMAIN=$1 DID=$2 FLAGS=$3 if [ -z "$2" ] ; then DID=$DOMAIN fi if [ -z "$3" ] ; then FLAGS=$(( $SRDB_LOAD_SER | $SRDB_CANON | $SRDB_FOR_SERWEB )) fi if is_value_in_db $UID_DOMAIN_TABLE $UID_DO_DOMAIN_COLUMN $DOMAIN; then minfo "$1 already in $UID_DOMAIN_TABLE table" exit 0 fi QUERY="insert into $UID_DOMAIN_TABLE ($UID_DO_DID_COLUMN,$UID_DO_DOMAIN_COLUMN,$UID_DO_FLAGS_COLUMN) VALUES ('$DID','$DOMAIN',$FLAGS);" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "uid_domain - SQL Error" exit 1 fi minfo "execute '$0 uid_domain reload' to synchronize cache and database" ;; rm) require_dbengine shift if [ $# -ne 1 ] ; then merr "missing domain parameter" exit 1 fi QUERY="delete from $UID_DOMAIN_TABLE where domain='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "domain - SQL Error" exit 1 fi minfo "execute '$0 uid_domain reload' to synchronize cache and database" ;; *) usage_uid_domain exit 1 esac } # ##### ------------------------------------------------ ##### ### permissions trusted management # permissions_trusted() { case $1 in reload) require_ctlengine $CTLCMD trusted_reload ;; dump) require_ctlengine $CTLCMD trusted_dump ;; show) require_dbengine QUERY="select * FROM $TRUSTED_TABLE ; " $DBROCMD "$QUERY" ;; add) require_dbengine shift if [ $# -lt 2 ] ; then usage_trusted exit 1 fi if is_value_in_db $TRUSTED_TABLE src_ip $1; then minfo "$1 already in $TRUSTED_TABLE table" exit 0 fi case $2 in any|udp|tcp|tls|sctp|none) ;; *) merr "unknown protocol" exit 1 esac PATTERN="" if [ ! -z "$3" ]; then PATTERN="$3" fi QUERY="insert into $TRUSTED_TABLE \ ( $TRUSTED_SRC_IP_COLUMN, $TRUSTED_PROTO_COLUMN, \ $TRUSTED_FROM_PATTERN_COLUMN, $TRUSTED_TAG_COLUMN) \ VALUES ('$1', '$2', '$PATTERN', '$4');" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "trusted - SQL Error" exit 1 fi minfo "execute '$0 trusted reload' to synchronize cache and database" ;; rm) require_dbengine shift if [ $# -ne 1 ] ; then usage_trusted exit 1 fi QUERY="delete from $TRUSTED_TABLE where $TRUSTED_SRC_IP_COLUMN='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "trusted - SQL Error" exit 1 fi minfo "execute '$0 trusted reload' to synchronize cache and database" ;; *) usage_trusted exit 1 esac } # ##### ------------------------------------------------ ##### ### permissions address management # permissions_address() { case $1 in reload) require_ctlengine $CTLCMD address_reload ;; dump) require_ctlengine $CTLCMD address_dump ;; show) require_dbengine QUERY="select * FROM $ADDRESS_TABLE ; " $DBROCMD "$QUERY" ;; add) require_dbengine shift if [ $# -lt 2 ] ; then usage_address exit 1 fi AMASK=32 if [ ! -z "$3" ]; then AMASK="$3" fi APORT=0 if [ ! -z "$4" ]; then APORT="$4" fi ATAG="" if [ ! -z "$5" ]; then ATAG="$5" fi QUERY="insert into $ADDRESS_TABLE \ (grp, ip_addr, mask, port, tag) \ VALUES ($1, '$2', $AMASK, $APORT, '$ATAG');" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "permissions address - SQL Error" exit 1 fi minfo "execute '$0 address reload' to synchronize cache and database" ;; rm) require_dbengine shift if [ $# -ne 2 ] ; then usage_address exit 1 fi QUERY="delete from $ADDRESS_TABLE where grp=$1 and ip_addr='$2';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "permissions address - SQL Error" exit 1 fi minfo "execute '$0 address reload' to synchronize cache and database" ;; *) usage_address exit 1 esac } # ##### ------------------------------------------------ ##### ### LCR management # lcr() { case $1 in show_gws) merr "command disabled" exit 1 require_dbengine mecho "lcr gateways" QUERY="select * FROM $GW_TABLE ORDER BY $LCR_ID_COLUMN, $LCR_GW_GRPID_COLUMN; " $DBROCMD "$QUERY" ;; show_routes) merr "command disabled" exit 1 require_dbengine mecho "lcr routes" QUERY="select * FROM $LCR_TABLE ORDER BY $LCR_ID_COLUMN, $LCR_PREFIX_COLUMN; " $DBROCMD "$QUERY" ;; reload) merr "command disabled" exit 1 $CTLCMD lcr.reload ;; dump_gws) merr "command disabled" exit 1 $CTLCMD lcr.dump_gws ;; dump_routes) merr "command disabled" exit 1 $CTLCMD lcr.dump_lcrs ;; eval_weights) shift $AWK 'BEGIN { if (ARGC < 2) { printf("Usage: lcr eval_weights \n"); exit; } iters = 100000; for (i = 1; i < ARGC; i++) { counts[i] = 0; } for (i = 1; i <= iters; i++) { for (j = 1; j < ARGC; j++) { elem[j] = ARGV[j] * rshift(int(2147483647 * rand()), 8); } at = 1; max = elem[at]; for (j = 2; j < ARGC; j++) { if (elem[j] > max) { max = elem[j]; at = j; } } counts[at] = counts[at] + 1; } for (i = 1; i < ARGC; i++) { printf("weight %d probability %.4f\n", ARGV[i], counts[i]/iters); } }' $@ ;; *) usage_lcr exit 1 esac } # ##### ------------------------------------------------ ##### ### CARRIERROUTE management # cr() { require_dbengine require_ctlengine case $1 in show) mecho "cr carrier names" QUERY="select * FROM $CARRIER_NAME_TABLE ORDER BY $CARRIERROUTE_CARRIER_NAME_ID_COLUMN; " $DBROCMD "$QUERY" mecho "cr domain names" QUERY="select * FROM $DOMAIN_NAME_TABLE ORDER BY $CARRIERROUTE_DOMAIN_NAME_ID_COLUMN; " $DBROCMD "$QUERY" mecho "cr routes" QUERY="select * FROM $CARRIERROUTE_TABLE ORDER BY \ $CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN,\ $CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN,\ $CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN,\ $CARRIERROUTE_CARRIERROUTE_PROB_COLUMN;" $DBROCMD "$QUERY" ;; reload) $CTLCMD cr_reload_routes ;; dump) $CTLCMD cr_dump_routes ;; addcn) shift if [ $# -ne 2 ] ; then merr "cr - missing carrier id or name" exit 1 fi QUERY="insert into $CARRIER_NAME_TABLE ( $CARRIERROUTE_CARRIER_NAME_ID_COLUMN, \ $CARRIERROUTE_CARRIER_NAME_CARRIER_COLUMN) \ VALUES ($1, '$2');" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "cr - SQL Error" exit 1 fi minfo "execute '$0 cr reload' to synchronize cache and database" ;; rmcn) shift if [ $# -ne 1 ] ; then merr "cr - missing carrier id to be removed" exit 1 fi QUERY="delete from $CARRIER_NAME_TABLE where $CARRIERROUTE_CARRIER_NAME_ID_COLUMN='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "cr - SQL Error" exit 1 fi minfo "execute '$0 cr reload' to synchronize cache and database" ;; adddn) shift if [ $# -ne 2 ] ; then merr "cr - missing domain id or name" exit 1 fi QUERY="insert into $DOMAIN_NAME_TABLE ( $CARRIERROUTE_DOMAIN_NAME_ID_COLUMN, \ $CARRIERROUTE_DOMAIN_NAME_DOMAIN_COLUMN) \ VALUES ($1, '$2');" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "cr - SQL Error" exit 1 fi minfo "execute '$0 cr reload' to synchronize cache and database" ;; rmdn) shift if [ $# -ne 1 ] ; then merr "cr - missing domain id to be removed" exit 1 fi QUERY="delete from $DOMAIN_NAME_TABLE where $CARRIERROUTE_DOMAIN_NAME_ID_COLUMN='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "cr - SQL Error" exit 1 fi minfo "execute '$0 cr reload' to synchronize cache and database" ;; addcarrier) shift if [ $# -lt 4 ] ; then merr "cr - too few parameters" exit 1 fi PROB=1 STRIP=0 REWRITE_PREFIX= REWRITE_SUFFIX= COMMENT= FLAGS=0 MASK=0 if [ $# -gt 4 ] ; then PROB=$5 if [ $# -gt 5 ] ; then STRIP=$6 if [ $# -gt 6 ] ; then REWRITE_PREFIX=$7 if [ $# -gt 7 ] ; then REWRITE_SUFFIX=$8 if [ $# -gt 8 ] ; then MASK=$9 if [ $# -gt 9 ] ; then FLAGS=${10} if [ $# -gt 10 ] ; then COMMENT=${11} fi fi fi fi fi fi fi CARRIER=$1 SCAN_PREFIX=$2 DOMAIN=$3 REWRITE_HOST=$4 echo $FLAGS echo $MASK QUERY="insert into $CARRIERROUTE_TABLE \ ( $CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_PROB_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_STRIP_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_REWRITE_HOST_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_REWRITE_PREFIX_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_REWRITE_SUFFIX_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_COMMENT_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_FLAGS_COLUMN, \ $CARRIERROUTE_CARRIERROUTE_MASK_COLUMN ) \ VALUES ($CARRIER, '$SCAN_PREFIX', '$DOMAIN', $PROB, $STRIP, \ '$REWRITE_HOST', '$REWRITE_PREFIX', '$REWRITE_SUFFIX', '$COMMENT', \ $FLAGS, $MASK);" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "cr - SQL Error" exit 1 fi minfo "execute '$0 cr reload' to synchronize cache and database" ;; rmcarrier) shift if [ $# -ne 3 ] ; then merr "cr - too few parameters" exit 1 fi CARRIER=$1 SCAN_PREFIX=$2 DOMAIN=$3 QUERY="delete from $CARRIERROUTE_TABLE where $CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN='$CARRIER' AND \ $CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN='$SCAN_PREFIX' AND \ $CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN=$DOMAIN ;" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "cr - SQL Error" exit 1 fi minfo "execute '$0 cr reload' to synchronize cache and database" ;; *) usage_cr exit 1 esac } # ##### ------------------------------------------------ ##### ### DISPATCHER management # dispatcher() { require_dbengine require_ctlengine case $1 in show) mecho "dispatcher gateways" QUERY="select * FROM $DISPATCHER_TABLE ORDER BY $DISPATCHER_SETID_COLUMN; " $DBROCMD "$QUERY" ;; addgw) shift if [ $# -lt 3 ] ; then merr "too few parameters" usage_dispatcher exit 1 fi if [ $# -gt 3 ] ; then DISPATCHER_DESCRIPTION=$4 else DISPATCHER_DESCRIPTION="" fi DISPATCHER_SETID=$1 DISPATCHER_DESTINATION=$2 DISPATCHER_FLAGS=$3 QUERY="insert into $DISPATCHER_TABLE \ ( $DISPATCHER_SETID_COLUMN, $DISPATCHER_DESTINATION_COLUMN, $DISPATCHER_FLAGS_COLUMN, $DISPATCHER_DESCRIPTION_COLUMN ) \ VALUES ($DISPATCHER_SETID,'$DISPATCHER_DESTINATION',$DISPATCHER_FLAGS,'$DISPATCHER_DESCRIPTION');" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dispatcher - SQL Error" exit 1 fi $CTLCMD ds_reload ;; rmgw) shift if [ $# -ne 1 ] ; then merr "missing gateway id to be removed" exit 1 fi QUERY="delete from $DISPATCHER_TABLE where $DISPATCHER_ID_COLUMN='$1';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dispatcher - SQL Error" exit 1 fi $CTLCMD ds_reload ;; reload) $CTLCMD ds_reload ;; dump) $CTLCMD ds_list ;; *) usage_dispatcher exit 1 esac } # ##### ------------------------------------------------ ##### ### DIALPLAN management # dialplan() { require_dbengine require_ctlengine case $1 in show) shift if [ $# -gt 0 ] ; then mecho "dialplan $1 tables" QUERY="select * FROM $DIALPLAN_TABLE WHERE $DIALPLAN_DPID_COLUMN=$1 ORDER BY $DIALPLAN_PR_COLUMN ; " else mecho "dialplan tables" QUERY="select * FROM $DIALPLAN_TABLE ORDER BY $DIALPLAN_DPID_COLUMN, $DIALPLAN_PR_COLUMN; " fi $DBROCMD "$QUERY" ;; addrule) shift if [ $# -lt 8 ] ; then merr "too few parameters" usage_dialplan exit 1 fi DIALPLAN_DPID=$1 DIALPLAN_PR=$2 DIALPLAN_MATCH_OP=$3 case $DIALPLAN_MATCH_OP in equal) DIALPLAN_MATCH_OP=0 ;; regexp) DIALPLAN_MATCH_OP=1 ;; *) merr "dialplan - unexpected $DIALPLAN_MATCH_OP for operating matching. Use 'equal' or 'regexp'!" exit 1 esac DIALPLAN_MATCH_EXP=$4 DIALPLAN_MATCH_LEN=$5 DIALPLAN_SUBST_EXP=$6 DIALPLAN_REPL_EXP=$7 DIALPLAN_ATTRS=$8 QUERY="insert into $DIALPLAN_TABLE \ ( $DIALPLAN_DPID_COLUMN, $DIALPLAN_PR_COLUMN, $DIALPLAN_MATCH_OP_COLUMN, \ $DIALPLAN_MATCH_EXP_COLUMN, $DIALPLAN_MATCH_LEN_COLUMN, \ $DIALPLAN_SUBST_EXP_COLUMN, $DIALPLAN_REPL_EXP_COLUMN, \ $DIALPLAN_ATTRS_COLUMN ) \ VALUES ( $DIALPLAN_DPID, $DIALPLAN_PR, $DIALPLAN_MATCH_OP, \ '$DIALPLAN_MATCH_EXP', $DIALPLAN_MATCH_LEN, '$DIALPLAN_SUBST_EXP', \ '$DIALPLAN_REPL_EXP', '$DIALPLAN_ATTRS')"; mecho "$QUERY" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dialplan - SQL Error" exit 1 fi $CTLCMD dp_reload ;; rm) QUERY="delete from $DIALPLAN_TABLE; " $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dialplan - SQL Error" exit 1 fi $CTLCMD dp_reload ;; rmdpid) shift if [ $# -lt 1 ] ; then merr "too few parameters" usage_dialplan exit 1 fi DIALPLAN_DPID=$1 QUERY="delete from $DIALPLAN_TABLE where $DIALPLAN_DPID_COLUMN=$DIALPLAN_DPID; " $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dialplan - SQL Error" exit 1 fi $CTLCMD dp_reload ;; rmrule) shift if [ $# -lt 2 ] ; then merr "too few parameters" usage_dialplan exit 1 fi DIALPLAN_DPID=$1 DIALPLAN_PR=$2 QUERY="delete from $DIALPLAN_TABLE where $DIALPLAN_DPID_COLUMN=$DIALPLAN_DPID AND $DIALPLAN_PR_COLUMN=$DIALPLAN_PR; " $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "dialplan - SQL Error" exit 1 fi $CTLCMD dp_reload ;; reload) $CTLCMD dp_reload ;; *) usage_dialplan exit 1 esac } # ##### ------------------------------------------------ ##### ### kamailio_start # kamailio_start() { echo minfo "Starting Kamailio : " if [ -r $PID_FILE ] ; then ps axw | $EGREP kamailio ls -l $PID_FILE minfo "PID file exists ($PID_FILE)! Kamailio already running?" exit 1 fi if [ ! -x "$OSERBIN" ] ; then echo merr "Kamailio binaries not found at $OSERBIN" merr "set OSERBIN to the path of kamailio in $0 or ~/.kamctlrc" exit 1 fi if [ $SYSLOG = 1 ] ; then $OSERBIN -P $PID_FILE -f $ETCDIR/kamailio.cfg $STARTOPTIONS 1>/dev/null 2>/dev/null else $OSERBIN -P $PID_FILE -E -f $ETCDIR/kamailio.cfg $STARTOPTIONS fi sleep 3 if [ ! -s $PID_FILE ] ; then echo merr "PID file $PID_FILE does not exist -- Kamailio start failed" exit 1 fi minfo "started (pid: `cat $PID_FILE`)" } # ##### ------------------------------------------------ ##### ### kamailio_stop # kamailio_stop() { echo minfo "Stopping Kamailio : " if [ -r $PID_FILE ] ; then kill `cat $PID_FILE` minfo "stopped" else echo merr "No PID file found ($PID_FILE)! Kamailio probably not running" minfo "check with 'ps axw | $EGREP kamailio'" exit 1 fi } # ##### ------------------------------------------------ ##### ### options_ping # options_ping() { myhost=`get_my_host` require_ctlengine CMD="t_uac_dlg OPTIONS \"$1\" \".\" \".\" \"From:sip:daemon@$myhost"$'\r\n'"To:<$1>"$'\r\n'"Contact:sip:daemon@$myhost"$'\r\n'"\"" RET=`$CTLCMD $CMD | head -1` print_status $RET } # ##### ------------------------------------------------ ##### ### rpid management # rpid() { if [ "$#" -lt 2 ] ; then merr "rpid - too few parameters" exit 1 fi shift; require_dbengine case $1 in show) if [ $# -eq 2 ] ; then set_user $2 is_user $2 if [ $? -ne 0 ] ; then merr "rpid - invalid user '$2'" exit 1; fi CLAUSE=" WHERE $SUBSCRIBER_COLUMN='$OSERUSER' AND \ $REALM_COLUMN='$OSERDOMAIN' " elif [ $# -ne 1 ] ; then usage_rpid exit 1 fi QUERY="select $SUBSCRIBER_COLUMN, $RPID_COLUMN FROM $SUB_TABLE \ $CLAUSE ; " $DBROCMD "$QUERY" ;; add|rm) MODE=$1; if [ "$MODE" = "add" ] ; then ARG_NUM=3; else ARG_NUM=2; fi if [ $# -lt $ARG_NUM ] ; then usage_rpid exit 1 fi set_user $2 is_user $2 if [ $? -ne 0 ] ; then merr "rpid - invalid user '$2'" exit 1 fi shift 2 if [ "$MODE" = "add" ] ; then RPID_VAL="'$1'"; else RPID_VAL=NULL; fi QUERY="UPDATE $SUB_TABLE SET $RPID_COLUMN=$RPID_VAL \ WHERE $SUBSCRIBER_COLUMN='$OSERUSER' AND $REALM_COLUMN='$OSERDOMAIN';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "rpid - SQL Error" exit 1 fi $0 rpid show "$OSERUSER@$OSERDOMAIN" ;; *) usage_rpid exit 1 ;; esac } # ##### ------------------------------------------------ ##### ### SPEEDDIAL management # speeddial() { if [ "$#" -lt 2 ] ; then merr "speeddial - too few parameters" echo usage_speeddial exit 1 fi require_dbengine shift case $1 in list) if [ $# -eq 2 ] ; then # print speed-dials for user check_aor "$2" if [ "$?" -ne "0" ] ; then merr "speeddial - <$2> is not a valid AoR (user@domain)" exit 1 fi set_user $2 CLAUSE="WHERE $SD_USER_COLUMN='$OSERUSER' AND \ $SD_DOMAIN_COLUMN='$OSERDOMAIN'" mecho "Dumping speed-dials for user=<$2>" echo QUERY="SELECT CONCAT($SD_SD_USER_COLUMN,'@',\ $SD_SD_DOMAIN_COLUMN) AS 'Short number', $SD_NEW_URI_COLUMN AS 'New URI',\ $SD_DESC_COLUMN FROM $SD_TABLE $CLAUSE;" $DBROCMD "$QUERY" #| $AWK 'BEGIN {line=0;} # /^\+/ { next } #{ if(line==0) print "## SpeedDial \tNew-URI \tDescription\n"; # else { # ORS_BAK=ORS; # ORS=""; # print line ") " $1 "@" $2 "\t" $3 "\t\"" $4; # for (i=5;i<=NF;++i) print FS $i; # ORS=ORS_BAK; # print "\""; # } # line++; #}' elif [ $# -eq 1 ] ; then mecho "Dumping all speed-dials may take long: do you want to proceed? [Y|N] " read answer if [ "$answer" = "y" -o "$answer" = "Y" ] ; then mecho "Dumping all speed-dials..." echo else exit 1 fi QUERY="SELECT CONCAT($SD_SD_USER_COLUMN,'@',\ $SD_SD_DOMAIN_COLUMN) AS 'Short number', CONCAT($SD_USER_COLUMN,'@',\ $SD_DOMAIN_COLUMN) AS 'Owner', $SD_NEW_URI_COLUMN AS 'New URI',\ $SD_DESC_COLUMN FROM $SD_TABLE;" $DBROCMD "$QUERY" #| $AWK 'BEGIN {line=0;} # /^\+/ { next } # { line++; #if(line==1) print "SIP-ID \tSpeedDial \tNew-URI \tDescritpion\n"; # else { # ORS_BAK=ORS; # ORS=""; # print $3 "@" $4 "\t" $1 "@" $2 "\t" $5 "\t\"" $6; # for (i=7;i<=NF;++i) print FS $i; # ORS=ORS_BAK; # print "\""; # } # }' else merr "speeddial - wrong number of params for command [list]" usage_speeddial exit 1 fi exit $? ;; show) if [ $# -ne 2 ] ; then merr "speeddial - wrong number of params for command [show]" usage_speeddial exit 1 fi check_aor "$2" if [ "$?" -ne "0" ] ; then merr "speeddial - $2 is not a valid AoR (user@domain)" exit 1 fi set_user $2 CLAUSE="WHERE $SD_SD_USER_COLUMN='$OSERUSER' AND \ $SD_SD_DOMAIN_COLUMN='$OSERDOMAIN'" QUERY="SELECT CONCAT($SD_USER_COLUMN,'@',$SD_DOMAIN_COLUMN) \ AS 'Owner', $SD_NEW_URI_COLUMN AS 'New URI', $SD_DESC_COLUMN FROM \ $SD_TABLE $CLAUSE ; " mecho "Details for speeddial <$2>" $DBROCMD "$QUERY" # | $AWK 'BEGIN {line=0;} /^\+/ { next } # { # if(line==0) print "## SIP-ID \tNew-URI \tDescritpion\n"; # else { # ORS_BAK=ORS;usage_kamailio_monitor() { # ORS=""; # print line ") " $1 "@" $2 "\t" $3 "\t\"" $4; # for (i=5;i<=NF;++i) print FS $i; # ORS=ORS_BAK; # print "\""; # } # line++; # }' exit $? ;; add) if [ $# -ne 4 ] ; then if [ $# -ne 5 ] ; then merr "speeddial - wrong number of parameters" usage_speeddial exit 1 fi fi shift check_aor "$1" if [ "$?" -ne "0" ] ; then merr "speeddial - $1 is not a valid AoR (user@domain)" exit 1 fi check_aor "$2" if [ "$?" -ne "0" ] ; then merr "speeddial - $2 is not a valid AoR (user@domain)" exit 1 fi check_sipaor "$3" if [ "$?" -ne "0" ] ; then merr "speeddial - $3 is not a valid SIP AoR (sip:user@domain)" exit 1 fi set_user $1 TMP_OSERUSER=$OSERUSER TMP_OSERDOMAIN=$OSERDOMAIN set_user $2 QUERY="INSERT INTO $SD_TABLE ($SD_USER_COLUMN,$SD_DOMAIN_COLUMN,\ $SD_SD_USER_COLUMN,$SD_SD_DOMAIN_COLUMN,$SD_NEW_URI_COLUMN,$SD_DESC_COLUMN) \ VALUES ('$TMP_OSERUSER','$TMP_OSERDOMAIN','$OSERUSER','$OSERDOMAIN','$3','$4');" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "speeddial - SQL Error" exit 1 fi mecho "ok - spedd dial added" echo exit $? ;; rm) if [ $# -ne 3 ] ; then merr "speeddial rm - invalid number of parameters" usage_speeddial exit 1 fi shift check_aor "$1" if [ "$?" -ne "0" ] ; then merr "speeddial - $1 is not a valid AoR (user@domain)" exit 1 fi check_aor "$2" if [ "$?" -ne "0" ] ; then merr "speeddial - $2 is not a valid AoR (user@domain)" exit 1 fi set_user $1 TMP_OSERUSER=$OSERUSER TMP_OSERDOMAIN=$OSERDOMAIN set_user $2 CLAUSE="WHERE $SD_USER_COLUMN='$TMP_OSERUSER' AND \ $SD_DOMAIN_COLUMN='$TMP_OSERDOMAIN' AND $SD_SD_USER_COLUMN='$OSERUSER' AND \ $SD_SD_DOMAIN_COLUMN='$OSERDOMAIN'" QUERY="DELETE FROM $SD_TABLE $CLAUSE;" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "speeddial - SQL Error" exit 1 fi mecho "ok - spedd dial deleted" echo ;; help) usage_speeddial ;; *) merr "speeddial - unknown command" usage_speeddial exit 1 ;; esac } # end speed_dial() # ##### ================================================ ##### ### subscriber management # subscriber() { if [ "$#" -lt 2 ] ; then merr "too few parameters" usage_subscriber exit 1 fi require_dbengine case $1 in add) if [ $# -ne 3 ] ; then usage_subscriber exit 1 fi shift credentials $1 $2 is_user $1 if [ $? -eq 0 ] ; then minfo "user '$1' already exists" exit 1 fi set_user $1 check_alias $OSERUSER $OSERDOMAIN if [ "$ALIAS_EXISTS" = "1" ] ; then minfo "user '$1' already exists as alias" exit 1 fi if [ "$STORE_PLAINTEXT_PW" = "1" ] ; then PASS="$2" else PASS="" fi QUERY="insert into $SUB_TABLE ($SUBSCRIBER_COLUMN,\ $REALM_COLUMN,$HA1_COLUMN,$HA1B_COLUMN,$PASSWORD_COLUMN) \ values ('$OSERUSER','$OSERDOMAIN','$HA1','$HA1B','$PASS');"; $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "introducing the new user '$1' to the database failed" else mecho "new user '$1' added" fi ;; passwd) if [ $# -ne 3 ] ; then usage_subscriber exit 1 fi shift credentials $1 $2 is_user $1 if [ $? -ne 0 ] ; then merr "non-existent user '$1'" exit 1 fi if [ "$STORE_PLAINTEXT_PW" = "1" ] ; then PASS="$2" else PASS="" fi QUERY="update $SUB_TABLE set $HA1_COLUMN='$HA1', \ $HA1B_COLUMN='$HA1B', $PASSWORD_COLUMN='$PASS' \ WHERE $SUBSCRIBER_COLUMN='$OSERUSER' and $REALM_COLUMN='$OSERDOMAIN';" $DBCMD "$QUERY" if [ $? -ne 0 ] ; then merr "password change failed" else minfo "password change succeeded" fi ;; rm) if [ $# -ne 2 ] ; then usage_subscriber exit 1 fi require_ctlengine shift is_user $1 if [ $? -ne 0 ] ; then merr "non-existent user '$1'" exit 1 fi # begin with remove all user's privileges acl revoke $1 > /dev/null 2>&1 # destroy db-aliases QUERY="delete from $DA_TABLE where $DA_USER_COLUMN='$OSERUSER' \ and $DA_DOMAIN_COLUMN='$OSERDOMAIN';" $DBCMD "$QUERY" # destroy the user now QUERY="delete from $SUB_TABLE where $SUBSCRIBER_COLUMN='$OSERUSER' \ and $REALM_COLUMN='$OSERDOMAIN';" $DBCMD "$QUERY" # and also all his contacts $0 ul rm $1 > /dev/null 2>&1 ;; esac } # ##### ================================================ ##### ### USRLOC management # usrloc() { if [ "$#" -lt 2 ] ; then merr "usrloc - too few parameters" usage_usrloc exit 1 fi require_ctlengine if [ "$1" = "alias" ] ; then USRLOC_TABLE="$ALS_TABLE" if [ -z "$USRLOC_TABLE" ] ; then USRLOC_TABLE=aliases fi CHECK_SUB=1 elif [ "$1" = "ul" ] ; then USRLOC_TABLE="$UL_TABLE" if [ -z "$USRLOC_TABLE" ] ; then USRLOC_TABLE=location fi CHECK_SUB=0 elif [ "$1" = "usrloc" ] ; then USRLOC_TABLE="$UL_TABLE" if [ -z "$USRLOC_TABLE" ] ; then USRLOC_TABLE=location fi CHECK_SUB=0 else merr "usrloc - unknown subcommand '$1'" usage_usrloc exit 1 fi shift case $1 in show) if [ $# -eq 2 ] ; then if [ "$2" = "--brief" ] ; then $CTLCMD ul_dump brief else set_user $2 $CTLCMD ul_show_contact \ $USRLOC_TABLE "$OSERUSER@$OSERDOMAIN" fi elif [ $# -eq 1 ] ; then $CTLCMD ul_dump else merr "wrong number of params" usage_usrloc exit 1 fi exit $? ;; add) if [ $# -eq 3 ] ; then # expires 0 means persistent contact UL_EXPIRES=0 UL_FLAGS=0 BR_FLAGS=0 elif [ $# -eq 4 ] ; then UL_EXPIRES=$4 UL_FLAGS=0 BR_FLAGS=0 else usage_usrloc exit 1 fi shift check_uri "$2" if [ "$?" -ne "0" ] ; then merr "$2 is not a valid URI" exit 1 fi set_user $1 if [ "$CHECK_SUB" -ne 0 ] ; then is_user $1 if [ $? -eq 0 ] ; then merr "overlap of alias with an existing subscriber name" exit 1; fi fi check_alias $OSERUSER $OSERDOMAIN if [ "$ALIAS_EXISTS" = "1" ] ; then if [ "$CHECK_SUB" -ne 0 ] ; then minfo "alias already defined" else merr "AOR is an alias" fi exit 1 fi $CTLCMD ul_add "$USRLOC_TABLE" "$OSERUSER@$OSERDOMAIN" "$2" \ "$UL_EXPIRES" "1.00" "0" "$UL_FLAGS" "$BR_FLAGS" "$ALL_METHODS" exit $? ;; rm) if [ $# -eq 2 ] ; then shift set_user $1 $CTLCMD ul_rm $USRLOC_TABLE "$OSERUSER@$OSERDOMAIN" elif [ $# -eq 3 ] ; then shift set_user $1 check_uri "$2" if [ "$?" -ne "0" ] ; then merr "$2 is not a valid SIP URI (sip:[user@]domain)" exit 1 fi $CTLCMD ul_rm_contact $USRLOC_TABLE "$OSERUSER@$OSERDOMAIN" "$2" else merr "wrong number of params" usage_usrloc exit 1 fi ;; *) usage_usrloc exit 1 ;; esac } ##### ================================================ ##### ### TLS CA management # tls_ca() { if [ "$1" = "rootCA" ] ; then if [ -z $2 ] ; then # use default CA_BASE=$ETCDIR/tls else CA_BASE=`(cd $2;pwd)` fi if [ ! -d $CA_BASE ] ; then merr "Config directory ($CA_BASE) does not exist" exit 1 fi CA_CONF='ca.conf' CA_PATH=$CA_BASE/rootCA if [ ! -f $CA_BASE/$CA_CONF ] ; then merr "root CA config file ($CA_BASE/$CA_CONF) does not exist" exit 1 fi if [ -d $CA_PATH ] ; then mwarn "root CA directory ($CA_PATH) exists! Remove it (y/n)?" read X if [ "$X" != "y" -a "$X" != "Y" ] ; then exit 1 fi fi mecho "Creating directory $CA_PATH and its sub-tree" mkdir -p $CA_PATH if [ $? -ne 0 ] ; then merr "Failed to create root directory $CA_PATH" exit 1 fi rm -fr $CA_PATH/* mkdir $CA_PATH/private mkdir $CA_PATH/certs touch $CA_PATH/index.txt echo 01 >$CA_PATH/serial mecho "Creating CA self-signed certificate" ( cd $CA_PATH; openssl req -config $CA_BASE/$CA_CONF -x509 -newkey \ rsa:2048 -days 365 -out ./cacert.pem -outform PEM ) if [ $? -ne 0 ] ; then merr "Failed to create self-signed certificate" exit 1 fi mecho "Protecting CA private key" chmod 600 $CA_PATH/private/cakey.pem mecho "DONE" minfo "Private key can be found in $CA_PATH/private/cakey.pem" minfo "Certificate can be found in $CA_PATH/cacert.pem" elif [ "$1" = "userCERT" ] ; then if [ -z $2 ] ; then merr "Missing user name parameter" exit 1 fi if [ -z $3 ] ; then # use default CA_BASE=$ETCDIR/tls else CA_BASE=`(cd $3;pwd)` fi if [ ! -d $CA_BASE ] ; then merr "Config directory ($CA_BASE) does not exist" exit 1 fi USER_DIR=$CA_BASE/$2 USER_CFG=$CA_BASE/$2.conf USER=$2 REQ_CFG=$CA_BASE/request.conf if [ ! -f $USER_CFG ] ; then merr "User config file $USER_CFG not found" exit 1 fi if [ ! -f $REQ_CFG ] ; then merr "Request config file $REQ_CFG not found" exit 1 fi mecho "Using config file $USER_CFG" if [ -d $USER_DIR ] ; then mwarn "User CERT directory ($USER_DIR) exists! Remove it (y/n)?" read X if [ "$X" != "y" -a "$X" != "Y" ] ; then exit 1 fi fi mecho "Creating directory $USER_DIR" mkdir -p $USER_DIR if [ $? -ne 0 ] ; then merr "Failed to create user directory $USER_DIR " exit 1 fi rm -fr $USER_DIR/* mecho "Creating user certificate request" openssl req -config $USER_CFG -out $USER_DIR/$USER-cert_req.pem \ -keyout $USER_DIR/$USER-privkey.pem -new -nodes if [ $? -ne 0 ] ; then merr "Failed to generate certificate request" exit 1 fi mecho "Signing certificate request" ( cd $CA_BASE ; openssl ca -config $REQ_CFG -in \ $USER_DIR/$USER-cert_req.pem -out $USER_DIR/$USER-cert.pem ) if [ $? -ne 0 ] ; then merr "Failed to generate certificate request" exit 1 fi mecho "Generating CA list" cat $CA_BASE/rootCA/cacert.pem >> $USER_DIR/$USER-calist.pem mecho "DONE" minfo "Private key is locate at $USER_DIR/$USER-privkey.pem " minfo "Certificate is locate at $USER_DIR/$USER-cert.pem " minfo "CA-List is locate at $USER_DIR/$USER-calist.pem " else merr "unknown TLS command $1" usage_tls exit 1 fi } # ##### ================================================ ##### ### main command switch # case $1 in acl) shift acl "$@" ;; add) subscriber "$@" ;; passwd) subscriber "$@" ;; rm) subscriber "$@" ;; alias|ul|usrloc) usrloc "$@" ;; alias_db|aliasdb) alias_db "$@" ;; avp) avpops "$@" ;; cisco_restart) if [ "$#" -ne 2 ] ; then usage_cisco_restart exit 1 fi cisco_restart $2 ;; db) shift db_ops "$@" ;; showdb|userdb) usage exit 1 ;; domain) shift domain "$@" ;; uid_domain) shift uid_domain "$@" ;; trusted) shift permissions_trusted "$@" ;; address) shift permissions_address "$@" ;; fifo|mi|unixsock|ser_mi|sercmd_mi|sercmdmi|kamcmd_mi|kamcmdmi) require_ctlengine shift $CTLCMD "$@" ;; ser|sercmd|kamcmd) require_kamcmd shift $SERCTLCMD "$@" ;; lcr) shift lcr "$@" ;; cr) shift cr "$@" ;; dispatcher) shift dispatcher "$@" ;; dialplan) shift dialplan "$@" ;; monitor|console|moni|con) require_ctlengine $KAMAILIO_MONITOR "$@" ;; online) require_ctlengine $CTLCMD ul_dump | $EGREP -i aor | awk '{print $2}' | sort | sort -mu exit $? ;; ping) # error handling is hacked -- filter_fl should not # consume positive status -- that should be done by # calling app if [ "$#" -ne 2 ] ; then usage_ping exit 1 fi options_ping $2 ;; ps) require_ctlengine $CTLCMD ps ;; uptime) require_ctlengine $CTLCMD uptime ;; stats) require_ctlengine if [ "$#" -eq 1 ] ; then $CTLCMD get_statistics all else $CTLCMD get_statistics "${2}:" fi ;; restart) kamailio_stop sleep 2 kamailio_start ;; rpid) rpid "$@" ;; speeddial|speed_dial) speeddial "$@" ;; tls) shift tls_ca "$@" ;; start) kamailio_start ;; stop) kamailio_stop ;; version) echo "$0 $VERSION" ;; *) usage exit 1 ;; esac kamailio-4.0.4/utils/kamctl/kamctl.80000644000000000000000000000743312223032461015764 0ustar rootroot.\" $Id$ .TH kamctl 8 05.02.2009 Kamailio "Kamailio" .\" Process with .\" groff -man -Tascii kamctl.8 .\" .SH NAME kamctl \- Kamailio control tool .SH SYNOPSIS .B kamctl .BI command [ .BI parameters ] .SH DESCRIPTION .B kamctl is a shell script to control .B Kamailio SIP server It can be used to manage users, domains, aliases and other server options. .SH COMMANDS .TP 16 .I Daemon Commands: .TP .B start Start Kamalio .TP .B restart Restart Kamalio .TP .B stop Stop Kamalio .TP .B online Display online users .TP .B monitor Show server's internal status .TP .B ping Ping with SIP OPTIONS .TP 16 .I Access control list (acl) managment commands: .TP .B acl show [] Show user membership .TP .B acl grant Grant user membership (*) .TP .B acl revoke [] Grant user membership(s) (*) .TP 16 .I Least cost routes (lcr) managment command: .TP .B lcr dump Show in memory gateways and routes tables .TP .B lcr reload Reload lcr gateways and routes .TP 16 .I Carrierroute tables('cr') managment commands: .TP .B cr show Show tables .TP .B cr reload Reload tables .TP .B cr dump Show in memory tables .TP .B cr addrt Add a tree .TP .B cr rmrt Remove a tree .TP .B cr addcarrier Add a carrier (prob, strip, rewrite_prefix, rewrite_suffix, flags, mask and comment are optional arguments) .TP .B cr rmcarrier Remove a carrier .TP 16 .I Remote-Party-ID (RPID) managment commands: .TP .B rpid add Add rpid for a user (*) .TP .B rpid rm Set rpid to NULL for a user (*) .TP .B rpid show Show rpid of a user .TP 16 .I Subscriber managment commands: .TP .B add Add a new subscriber (*) .TP .B passwd Change user's password (*) .TP .B rm Delete a user (*) .TP 16 .I Commands to manage 'trusted': .TP .B trusted show Show db content .TP .B trusted dump Show cache content .TP .B trusted reload Reload db table into cache .TP .B trusted add Add a new entry (from_pattern and tag are optional arguments) .TP .B trusted rm Remove all entres for the given src_ip .TP 16 .I Dispatcher managment commands: .TP .B dispatcher show Show dispatcher gateways .TP .B dispatcher reload Reload dispatcher gateways .TP .B dispatcher dump Show in memory dispatcher gateways .TP .B dispatcher addgw Add gateway .TP .B dispatcher rmgw Delete gateway .TP 16 .I Cisco restart command: .TP .B cisco_restart Restart phone configured for .TP 16 .I User location('ul') or aliases managment commands: .B ul show [] Show in-RAM online users .TP .B ul show --brief Show in-RAM online users in short format .TP .B ul rm [] Delete user's usrloc entries .TP .B ul add Introduce a permanent usrloc entry .TP .B ul add Introduce a temporary usrloc entry .TP 16 .I Fifo commands: .TP .B fifo Send raw FIFO command .SH FILES .PD 0 .I /etc/kamailio/.kamctlrc .br .I /usr/local/etc/kamailio/.kamctlrc .br .I ~/.kamctlrc .br .SH NOTES .PP Commands labeled with (*) will prompt for a MySQL password. If the environment variable PW is set, the password will not be prompted. .SH AUTHORS see .B /usr/share/doc/kamailio/AUTHORS .SH SEE ALSO .BR kamailio(8), .BR kamailio.cfg(5) .PP Full documentation on Kamailio is available at .I http://www.kamailio.org/. .PP Mailing lists: .nf users@lists.kamailio.org - Kamailio user community .nf devel@lists.kamailio.org - Kamailio development, new features and unstable version kamailio-4.0.4/utils/kamctl/Makefile0000644000000000000000000002655212223032460016062 0ustar rootroot# $Id$ COREPATH=../.. include $(COREPATH)/Makefile.defs include $(COREPATH)/config.mak all: @echo "No compilation needed for kamctl" install-if-newer: install install: install-cfg install-bin install-man install-modules install-cfg: $(cfg_prefix)/$(cfg_dir) # kamctl config $(INSTALL_TOUCH) $(cfg_prefix)/$(cfg_dir)/kamctlrc.sample $(INSTALL_CFG) kamctlrc \ $(cfg_prefix)/$(cfg_dir)/kamctlrc.sample if [ ! -f $(cfg_prefix)/$(cfg_dir)/kamctlrc ]; then \ mv -f $(cfg_prefix)/$(cfg_dir)/kamctlrc.sample \ $(cfg_prefix)/$(cfg_dir)/kamctlrc; \ fi install-bin: $(bin_prefix)/$(bin_dir) cat kamctl | \ sed -e "s#/usr/local/sbin#$(bin_target)#g" | \ sed -e "s#/usr/local/lib/kamailio#$(lib_target)#g" | \ sed -e "s#/usr/local/etc/kamailio#$(cfg_target)#g" >/tmp/kamctl $(INSTALL_TOUCH) $(bin_prefix)/$(bin_dir)/kamctl $(INSTALL_BIN) /tmp/kamctl $(bin_prefix)/$(bin_dir) rm -fr /tmp/kamctl sed -e "s#/usr/local/sbin#$(bin_target)#g" \ < kamctl.base > /tmp/kamctl.base mkdir -p $(modules_prefix)/$(lib_dir)/kamctl $(INSTALL_TOUCH) \ $(modules_prefix)/$(lib_dir)/kamctl $(INSTALL_CFG) /tmp/kamctl.base \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.base rm -fr /tmp/kamctl.base sed -e "s#/usr/local#$(bin_target)#g" \ < kamctl.ctlbase > /tmp/kamctl.ctlbase $(INSTALL_CFG) /tmp/kamctl.ctlbase \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.ctlbase rm -fr /tmp/kamctl.ctlbase sed -e "s#/usr/local#$(bin_target)#g" \ < kamctl.fifo > /tmp/kamctl.fifo $(INSTALL_CFG) /tmp/kamctl.fifo \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.fifo rm -fr /tmp/kamctl.fifo sed -e "s#/usr/local#$(bin_target)#g" \ < kamctl.ser > /tmp/kamctl.ser $(INSTALL_CFG) /tmp/kamctl.ser \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.ser rm -fr /tmp/kamctl.ser sed -e "s#/usr/local#$(bin_target)#g" \ < kamctl.ser_mi > /tmp/kamctl.ser_mi $(INSTALL_CFG) /tmp/kamctl.ser_mi \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.ser_mi rm -fr /tmp/kamctl.ser_mi sed -e "s#/usr/local#$(bin_target)#g" \ < kamctl.unixsock > /tmp/kamctl.unixsock $(INSTALL_CFG) /tmp/kamctl.unixsock \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.unixsock rm -fr /tmp/kamctl.unixsock sed -e "s#/usr/local#$(bin_target)#g" \ < kamctl.sqlbase > /tmp/kamctl.sqlbase $(INSTALL_CFG) /tmp/kamctl.sqlbase \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.sqlbase rm -fr /tmp/kamctl.sqlbase # install db setup base script sed -e "s#/usr/local/sbin#$(bin_target)#g" \ -e "s#/usr/local/etc/kamailio#$(cfg_target)#g" \ -e "s#/usr/local/share/kamailio#$(data_target)#g" \ < kamdbctl.base > /tmp/kamdbctl.base $(INSTALL_CFG) /tmp/kamdbctl.base \ $(modules_prefix)/$(lib_dir)/kamctl/kamdbctl.base rm -fr /tmp/kamdbctl.base cat kamdbctl | \ sed -e "s#/usr/local/sbin#$(bin_target)#g" | \ sed -e "s#/usr/local/lib/kamailio#$(lib_target)#g" | \ sed -e "s#/usr/local/etc/kamailio#$(cfg_target)#g" >/tmp/kamdbctl $(INSTALL_TOUCH) $(bin_prefix)/$(bin_dir)/kamdbctl $(INSTALL_BIN) /tmp/kamdbctl $(bin_prefix)/$(bin_dir) rm -fr /tmp/kamdbctl install-man: $(man_prefix)/$(man_dir)/man8 $(man_prefix)/$(man_dir)/man5 sed -e "s#/etc/$(NAME)/$(NAME)\.cfg#$(cfg_target)$(NAME).cfg#g" \ -e "s#/usr/sbin/#$(bin_target)#g" \ -e "s#/usr/lib/$(NAME)/modules/#$(modules_target)#g" \ -e "s#/usr/share/doc/$(NAME)/#$(doc_target)#g" \ < kamctl.8 > $(man_prefix)/$(man_dir)/man8/kamctl.8 chmod 644 $(man_prefix)/$(man_dir)/man8/kamctl.8 sed -e "s#/etc/$(NAME)/$(NAME)\.cfg#$(cfg_target)$(NAME).cfg#g" \ -e "s#/usr/sbin/#$(bin_target)#g" \ -e "s#/usr/lib/$(NAME)/modules/#$(modules_target)#g" \ -e "s#/usr/share/doc/$(NAME)/#$(doc_target)#g" \ < kamdbctl.8 > $(man_prefix)/$(man_dir)/man8/kamdbctl.8 chmod 644 $(man_prefix)/$(man_dir)/man8/kamdbctl.8 # MYSQLON?=yes install-modules: $(bin_prefix)/$(bin_dir) # install MySQL stuff if [ "$(MYSQLON)" = "yes" ]; then \ mkdir -p $(modules_prefix)/$(lib_dir)/kamctl ; \ sed -e "s#/usr/local/sbin#$(bin_target)#g" \ < kamctl.mysql > /tmp/kamctl.mysql ; \ $(INSTALL_CFG) /tmp/kamctl.mysql \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.mysql ; \ rm -fr /tmp/kamctl.mysql ; \ sed -e "s#/usr/local/share/kamailio#$(data_target)#g" \ < kamdbctl.mysql > /tmp/kamdbctl.mysql ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/kamctl/kamdbctl.mysql ; \ $(INSTALL_CFG) /tmp/kamdbctl.mysql $(modules_prefix)/$(lib_dir)/kamctl/ ; \ rm -fr /tmp/kamdbctl.mysql ; \ mkdir -p $(data_prefix)/$(data_dir)/mysql ; \ for FILE in $(wildcard mysql/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/mysql/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/mysql/`basename "$$FILE"` ; \ fi ;\ done ; \ fi # install PostgreSQL stuff if [ "$(PGSQLON)" = "yes" ]; then \ mkdir -p $(modules_prefix)/$(lib_dir)/kamctl ; \ sed -e "s#/usr/local/sbin#$(bin_target)#g" \ < kamctl.pgsql > /tmp/kamctl.pgsql ; \ $(INSTALL_CFG) /tmp/kamctl.pgsql \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.pgsql ; \ rm -fr /tmp/kamctl.pgsql ; \ sed -e "s#/usr/local/share/kamailio#$(data_target)#g" \ < kamdbctl.pgsql > /tmp/kamdbctl.pgsql ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/kamctl/kamdbctl.pgsql ; \ $(INSTALL_CFG) /tmp/kamdbctl.pgsql $(modules_prefix)/$(lib_dir)/kamctl/ ; \ rm -fr /tmp/kamdbctl.pgsql ; \ mkdir -p $(data_prefix)/$(data_dir)/postgres ; \ for FILE in $(wildcard postgres/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/postgres/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/postgres/`basename "$$FILE"` ; \ fi ;\ done ; \ fi # install Oracle stuff if [ "$(ORACLEON)" = "yes" ]; then \ mkdir -p $(modules_prefix)/$(lib_dir)/kamctl ; \ sed -e "s#/usr/local/sbin#$(bin_target)#g" \ < kamctl.oracle > /tmp/kamctl.oracle ; \ $(INSTALL_CFG) /tmp/kamctl.oracle \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.oracle ; \ rm -fr /tmp/kamctl.oracle ; \ sed -e "s#/usr/local/share/kamailio#$(data_target)#g" \ < kamdbctl.oracle > /tmp/kamdbctl.oracle ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/kamctl/kamdbctl.oracle ; \ $(INSTALL_CFG) /tmp/kamdbctl.oracle $(modules_prefix)/$(lib_dir)/kamctl/ ; \ rm -fr /tmp/kamdbctl.oracle ; \ sed -e "s#/usr/local/share/kamailio#$(data_target)#g" \ < kamdbfunc.oracle > /tmp/kamdbfunc.oracle ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/kamctl/kamdbfunc.oracle ; \ $(INSTALL_CFG) /tmp/kamdbfunc.oracle $(modules_prefix)/$(lib_dir)/kamctl/ ; \ rm -fr /tmp/kamdbfunc.oracle ; \ mkdir -p $(data_prefix)/$(data_dir)/oracle ; \ for FILE in $(wildcard oracle/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/oracle/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/oracle/`basename "$$FILE"` ; \ fi ;\ done ; \ mkdir -p $(data_prefix)/$(data_dir)/oracle/inc ; \ for FILE in $(wildcard oracle/inc/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/oracle/inc/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/oracle/inc/`basename "$$FILE"` ; \ fi ;\ done ; \ mkdir -p $(data_prefix)/$(data_dir)/oracle/admin ; \ for FILE in $(wildcard oracle/admin/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/oracle/admin/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/oracle/admin/`basename "$$FILE"` ; \ fi ;\ done ; \ $(MAKE) -C ../db_oracle/ ; \ $(INSTALL_BIN) ../db_oracle/kamailio_orasel $(bin_prefix)/$(bin_dir) ; \ fi # install Berkeley database stuff if [ "$(BERKELEYDBON)" = "yes" ]; then \ mkdir -p $(modules_prefix)/$(lib_dir)/kamctl ; \ sed -e "s#/usr/local/share/kamailio/#$(data_target)#g" \ < kamctl.db_berkeley > /tmp/kamctl.db_berkeley ; \ $(INSTALL_CFG) /tmp/kamctl.db_berkeley \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.db_berkeley ; \ rm -fr /tmp/kamctl.db_berkeley ; \ sed -e "s#/usr/local/share/kamailio#$(data_target)#g" \ < kamdbctl.db_berkeley > /tmp/kamdbctl.db_berkeley ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/kamctl/kamdbctl.db_berkeley ; \ $(INSTALL_CFG) /tmp/kamdbctl.db_berkeley $(modules_prefix)/$(lib_dir)/kamctl/ ; \ rm -fr /tmp/kamdbctl.db_berkeley ; \ mkdir -p $(data_prefix)/$(data_dir)/db_berkeley/kamailio ; \ for FILE in $(wildcard db_berkeley/kamailio/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/db_berkeley/kamailio/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/db_berkeley/kamailio/`basename "$$FILE"` ; \ fi ;\ done ; \ $(MAKE) -C ../db_berkeley/ ; \ $(INSTALL_BIN) ../db_berkeley/kambdb_recover $(bin_prefix)/$(bin_dir) ; \ fi # install dbtext stuff if [ "$(DBTEXTON)" = "yes" ]; then \ mkdir -p $(modules_prefix)/$(lib_dir)/kamctl ; \ sed -e "s#/usr/local/share/kamailio/#$(data_target)#g" \ < kamctl.dbtext > /tmp/kamctl.dbtext ; \ $(INSTALL_CFG) /tmp/kamctl.dbtext \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.dbtext ; \ rm -fr /tmp/kamctl.dbtext ; \ sed -e "s#/usr/local/share/kamailio#$(data_target)#g" \ < kamdbctl.dbtext > /tmp/kamdbctl.dbtext ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/kamctl/kamdbctl.dbtext ; \ $(INSTALL_CFG) /tmp/kamdbctl.dbtext $(modules_prefix)/$(lib_dir)/kamctl/ ; \ rm -fr /tmp/kamdbctl.dbtext ; \ mkdir -p $(modules_prefix)/$(lib_dir)/kamctl/dbtextdb ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/kamctl/dbtextdb/dbtextdb.py ; \ $(INSTALL_BIN) dbtextdb/dbtextdb.py $(modules_prefix)/$(lib_dir)/kamctl/dbtextdb/ ; \ mkdir -p $(data_prefix)/$(data_dir)/dbtext/kamailio ; \ for FILE in $(wildcard dbtext/kamailio/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/dbtext/kamailio/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/dbtext/kamailio/`basename "$$FILE"` ; \ fi ;\ done ;\ fi # install sqlite stuff if [ "$(SQLITEON)" = "yes" ]; then \ mkdir -p $(modules_prefix)/$(lib_dir)/kamctl ; \ sed -e "s#/usr/local/sbin#$(bin_target)#g" \ < kamctl.sqlite > /tmp/kamctl.sqlite ; \ $(INSTALL_CFG) /tmp/kamctl.sqlite \ $(modules_prefix)/$(lib_dir)/kamctl/kamctl.sqlite ; \ rm -fr /tmp/kamctl.sqlite ; \ sed -e "s#/usr/local/share/kamailio#$(data_target)#g" \ < kamdbctl.sqlite > /tmp/kamdbctl.sqlite ; \ $(INSTALL_TOUCH) $(modules_prefix)/$(lib_dir)/kamctl/kamdbctl.sqlite ; \ $(INSTALL_CFG) /tmp/kamdbctl.sqlite $(modules_prefix)/$(lib_dir)/kamctl/ ; \ rm -fr /tmp/kamdbctl.sqlite ; \ mkdir -p $(data_prefix)/$(data_dir)/db_sqlite ; \ for FILE in $(wildcard db_sqlite/*) ; do \ if [ -f $$FILE ] ; then \ $(INSTALL_TOUCH) $$FILE \ $(data_prefix)/$(data_dir)/db_sqlite/`basename "$$FILE"` ; \ $(INSTALL_CFG) $$FILE \ $(data_prefix)/$(data_dir)/db_sqlite/`basename "$$FILE"` ; \ fi ;\ done ; \ fi $(cfg_prefix)/$(cfg_dir): mkdir -p $(cfg_prefix)/$(cfg_dir) $(bin_prefix)/$(bin_dir): mkdir -p $(bin_prefix)/$(bin_dir) $(man_prefix)/$(man_dir)/man8: mkdir -p $(man_prefix)/$(man_dir)/man8 $(man_prefix)/$(man_dir)/man5: mkdir -p $(man_prefix)/$(man_dir)/man5 kamailio-4.0.4/utils/kamctl/kamctl.unixsock0000644000000000000000000000463412223032460017457 0ustar rootroot# # $Id$ # # control tool for maintaining Kamailio # #=================================================================== ##### ----------------------------------------------- ##### ### UNIXSOCK specific variables and functions # ##### ----------------------------------------------- ##### ### load CTL base # if [ -f "$MYLIBDIR/kamctl.ctlbase" ]; then . "$MYLIBDIR/kamctl.ctlbase" else mwarn "Cannot load CTL core functions '$MYLIBDIR/kamctl.ctlbase' ..." # exit -1 fi # ##### ----------------------------------------------- ##### ### parameters # export CHROOT_DIR # needed for kamunix if [ -z "$OSER_UNIXSOCK" ]; then OSER_UNIXSOCK=$CHROOT_DIR/tmp/kamailio.sock fi if [ -z "$OSERUNIX" ]; then OSERUNIX=kamunix fi # ##### ----------------------------------------------- ##### ### functions # usage_unixsock() { echo mecho " -- command 'unixsock'" echo cat < #include static int use_syslog = 0; static int log_level = LOG_WARNING; void init_log(char *_prgname, int _use_syslog) { use_syslog = _use_syslog; if (use_syslog) { openlog(_prgname, LOG_PID, LOG_DAEMON); } } void set_log_level(int level) { log_level = level; } void destroy_log(void) { if (use_syslog) closelog(); } void log_stdout(char * format, va_list ap) { vfprintf(stdout, format, ap); fflush(stdout); } void pdb_log(int priority, char * format, ...) { va_list ap; if (priority<=log_level) { va_start(ap, format); if (use_syslog) vsyslog(priority, format, ap); else log_stdout(format, ap); va_end(ap); } } kamailio-4.0.4/utils/pdbt/dt.h0000644000000000000000000000600512223032460014647 0ustar rootroot/* * Copyright (C) 2009 1&1 Internet AG * * This file is part of sip-router, a free SIP server. * * sip-router is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * sip-router is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _DT_H_ #define _DT_H_ #include "common.h" struct dt_node_t { struct dt_node_t *child[10]; carrier_t carrier; }; /* Allocates memory for the root node and initializes it. Returns a pointer to an initialized root node on success, NULL otherwise. */ struct dt_node_t *dt_init(); /* Deletes a subtree and frees memory including node. Memory for the root node is not freed. */ void dt_delete(struct dt_node_t *root, struct dt_node_t *node); /* Deletes the whole tree and frees the memory including the root node. */ void dt_destroy(struct dt_node_t **root); /* Deletes everything but the root node. The root node is initialized. This will make an empty tree like after dt_init. */ void dt_clear(struct dt_node_t *root); /* Inserts a number with a corresponding carrier id. Nodes are created if necessary and the node after the last digit is marked with the given carrier id. Returns 0 on success, -1 otherwise. */ int dt_insert(struct dt_node_t *root, const char *number, int numberlen, carrier_t carrier); /* Returns the number of nodes in the given tree. */ int dt_size(struct dt_node_t *root); /* Returns the number of nodes in the given tree that are marked with a carrier id (carrier>0). */ int dt_loaded_nodes(struct dt_node_t *root); /* Returns the number of leaf nodes in the given tree. On leaf nodes the leaf flag is set, on other nodes it is cleared. */ int dt_leaves(struct dt_node_t *root); /* Finds the longest prefix match of number in root. Sets *carrier according to value in dtree. Return the number of matched digits. In case no (partial) match is found, return -1 (i.e, not even the very first digit could be prefixed). */ int dt_longest_match(struct dt_node_t *root, const char *number, int numberlen, carrier_t *carrier); /* Returns 1 if number is found in root and set *carrier according to value in dtree. Returns 0 if the number is not found. */ int dt_contains(struct dt_node_t *root, const char *number, int numberlen, carrier_t *carrier); /* Optimizes the tree by means of compression. Effectively, this reduces the tree to a set of nodes representing shortest prefixes (as opposed to [likely complete] phone numbers). */ void dt_optimize(struct dt_node_t *root); #endif kamailio-4.0.4/utils/pdbt/carrier.h0000644000000000000000000000245612223032460015675 0ustar rootroot/* * Copyright (C) 2009 1&1 Internet AG * * This file is part of sip-router, a free SIP server. * * sip-router is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * sip-router is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _CARRIER_H_ #define _CARRIER_H_ #include "common.h" /* Initializes data structures. Must be called before any of the other functions! Returns 0 on success, -1 otherwise. */ void init_carrier_names(); /* Loads carrier names from a file. Format: "D[0-9][0-9][0-9] ". */ int load_carrier_names(char *filename); /* Returns a name for the given carrier id. Always returns a string, even if id is invalid or the id is unknown. */ char *carrierid2name(carrier_t carrier); #endif kamailio-4.0.4/utils/pdbt/debian/0000755000000000000000000000000012223032460015310 5ustar rootrootkamailio-4.0.4/utils/pdbt/debian/pdb-tools.install0000644000000000000000000000011512223032460020600 0ustar rootrootdebian/pdbt/usr/bin/pdbt usr/bin/ debian/copyright usr/share/doc/pdb-tools/ kamailio-4.0.4/utils/pdbt/debian/pdb-server.dirs0000644000000000000000000000002512223032460020241 0ustar rootrootusr/share/pdb-server kamailio-4.0.4/utils/pdbt/debian/NEWS0000644000000000000000000000000012223032460015775 0ustar rootrootkamailio-4.0.4/utils/pdbt/debian/copyright0000644000000000000000000000143612223032460017247 0ustar rootroot# # (C) Copyright 2009, 1&1 Internet AG # # This package is part of sip-router, a free SIP server. # # sip-router is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # sip-router is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA kamailio-4.0.4/utils/pdbt/debian/control0000644000000000000000000000110512223032460016710 0ustar rootrootSource: pdbt Section: net Priority: optional Maintainer: Henning Westerholt Build-Depends: debhelper (>= 4.0.0) Standards-Version: 3.6.2 Package: pdb-tools Architecture: any Depends: ui-pdb-tools-config Description: Tools for handling the PDB Tools for building a mmap image of the PDB and running queries on this mmap image. Package: pdb-server Architecture: any Description: Service for querying the carrier id via UDP. Provides a service for querying the carrier id of any German phone number via UDP. Needs a prebuilt mmap image of the PDB. kamailio-4.0.4/utils/pdbt/debian/changelog0000644000000000000000000000021312223032460017156 0ustar rootrootpdbt (0.0.1) etch; urgency=low * initial release -- Henning Westerholt Wed, 09 Sep 2009 18:07:30 +0200 kamailio-4.0.4/utils/pdbt/debian/pdb-server.install0000644000000000000000000000017012223032460020747 0ustar rootrootdebian/pdbt/usr/bin/pdb_server usr/bin/ debian/pdbt/etc/pdb_server.conf etc/ debian/copyright usr/share/doc/pdb-server/ kamailio-4.0.4/utils/pdbt/debian/rules0000755000000000000000000000370212223032460016372 0ustar rootroot#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 CFLAGS = -Wall -g ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) CFLAGS += -O0 else CFLAGS += -O2 endif configure: configure-stamp configure-stamp: dh_testdir # Add here commands to configure the package. touch configure-stamp build: build-stamp build-stamp: configure-stamp dh_testdir # Add here commands to compile the package. $(MAKE) #docbook-to-man debian/ui-pdbt.sgml > ui-pdbt.1 touch build-stamp clean: dh_testdir dh_testroot rm -f build-stamp configure-stamp # Add here commands to clean up after the build process. -$(MAKE) clean dh_clean install: build dh_testdir dh_testroot dh_clean -k dh_installdirs # Add here commands to install the package into debian/ui-pdbt. mkdir -p $(CURDIR)/debian/pdbt/usr/bin mkdir -p $(CURDIR)/debian/pdbt/etc $(MAKE) install DESTDIR=$(CURDIR)/debian/pdbt # Build architecture-independent files here. binary-indep: build install # We have nothing to do by default. # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_installchangelogs # dh_installdocs dh_installexamples dh_install # dh_installmenu # dh_installdebconf dh_installlogrotate # dh_installemacsen # dh_installpam # dh_installmime dh_installinit dh_installcron # dh_installinfo dh_installman dh_link dh_strip dh_compress dh_fixperms # dh_perl # dh_python # dh_makeshlibs dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install configure kamailio-4.0.4/utils/pdbt/debian/pdb-server.postinst0000644000000000000000000000062412223032460021170 0ustar rootroot#! /bin/sh set -e case "$1" in configure) adduser --quiet --system --group --disabled-password \ --shell /bin/false --gecos "PDBServer" \ --home /home/pdb pdb || true ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# kamailio-4.0.4/utils/pdbt/debian/compat0000644000000000000000000000000212223032460016506 0ustar rootroot4 kamailio-4.0.4/utils/pdbt/dtm.c0000644000000000000000000000440512223032460015021 0ustar rootroot/* * Copyright (C) 2009 1&1 Internet AG * * This file is part of sip-router, a free SIP server. * * sip-router is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * sip-router is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "dtm.h" #include "carrier.h" #include "log.h" #include #include #include #include #include #include #include #include struct dtm_node_t *dtm_load(char *filename) { struct dtm_node_t *mroot; int fd; int len; int nodes; fd = open(filename, O_RDONLY); if (fd < 0) { LERR("cannot open file '%s'\n", filename); return NULL; } len=lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); nodes=len/sizeof(struct dtm_node_t); LINFO("file contains %ld nodes (size=%ld, rest=%ld)\n", (long int)nodes, (long int)len, (long int)len%sizeof(struct dtm_node_t)); mroot=mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); if (mroot==MAP_FAILED) { LERR("cannot mmap file '%s', error=%d (%s)\n", filename, errno, strerror(errno)); close(fd); return NULL; } return mroot; } int dtm_longest_match(struct dtm_node_t *mroot, const char *number, int numberlen, carrier_t *carrier) { dtm_node_index_t node = 0; int nmatch = -1; int i=0; unsigned int digit; if (mroot[node].carrier > 0) { nmatch=0; *carrier = mroot[node].carrier; } while (i9) return nmatch; node = mroot[node].child[digit]; if (node == NULL_CARRIERID) return nmatch; i++; if (node<0) { nmatch=i; *carrier = -node; return nmatch; } if (mroot[node].carrier > 0) { nmatch=i; *carrier = mroot[node].carrier; } } return nmatch; } kamailio-4.0.4/utils/pdbt/pdb_server.c0000644000000000000000000001447412223032460016377 0ustar rootroot/* * Copyright (C) 2009 1&1 Internet AG * * This file is part of sip-router, a free SIP server. * * sip-router is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * sip-router is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include "pdb_server_backend.h" #include "log.h" #define NETBUFSIZE 200 #define DEFAULT_BINDADDR "0.0.0.0" #define DEFAULT_PORT 5574 void print_usage(char *program) { set_log_level(LOG_INFO); LINFO("Listens on a UDP port for queries and sends answer UDP\n"); LINFO("packets back.\n"); LINFO("\n"); LINFO("Usage: %s [/, /<\/tbody>/' | tr -d '\r' | tr '\n' '@' | sed 's/<\/table>.*$//' | sed 's/<\/tbody>.*$//' # probably also possible to use this: # http://www.bundesnetzagentur.de/cae/servlet/contentblob/156772/publicationFile/8492/KonsolidiertesVerzPortierungsk.zip # main page (for reference): # http://www.bundesnetzagentur.de/cln_1932/DE/Sachgebiete/Telekommunikation/RegulierungTelekommunikation/Nummernverwaltung/TechnischeNummern/Portierungskennung/KonsolidiertesVerzPortKenn_Basepage.html?nn=120380kamailio-4.0.4/utils/pdbt/scripts/get_carrier_names_finland.sh0000755000000000000000000000251012223032460023256 0ustar rootroot#!/bin/bash # Copyright (C) 2010 Mikko Lehto, mikko dot lehto at setera dot fi # # This file is part of sip-router, a free SIP server. # # sip-router is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version # # sip-router is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Small helper script for Finland: # Download the directory of finish carrier names and the respective IDs from # the regulation authorities and convert this into the format which the pdbt tool # understands. wget -O - "http://www2.ficora.fi/numerointi/nure_numbering.asp?nums=tot&lang=en" | awk '//, /<\/tbody>/' | awk -F"/,"",$1) gsub(/.*>/,"",$2); gsub(/ä/,"ä",$2); gsub(/Å/,"Ã…",$2); gsub(/ö/,"ö",$2); if ( $2 != "") { printf "D%.3d %s\n",$1,$2 } }' kamailio-4.0.4/utils/pdbt/log.h0000644000000000000000000000277612223032460015034 0ustar rootroot/* * Copyright (C) 2009 1&1 Internet AG * * This file is part of sip-router, a free SIP server. * * sip-router is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * sip-router is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _LOG_H_ #define _LOG_H_ #include void init_log(char *_prgname, int _use_syslog); void set_log_level(int level); void destroy_log(void); void pdb_log(int priority, char * format, ...); #define LEMERG(fmt, args...) pdb_log(LOG_EMERG, fmt, ## args) #define LALERT(fmt, args...) pdb_log(LOG_ALERT, fmt, ## args) #define LCRIT(fmt, args...) pdb_log(LOG_CRIT, fmt, ## args) #define LERR(fmt, args...) pdb_log(LOG_ERR, fmt, ## args) #define LWARNING(fmt, args...) pdb_log(LOG_WARNING, fmt, ## args) #define LNOTICE(fmt, args...) pdb_log(LOG_NOTICE, fmt, ## args) #define LINFO(fmt, args...) pdb_log(LOG_INFO, fmt, ## args) #define LDEBUG(fmt, args...) pdb_log(LOG_DEBUG, fmt, ## args) #endif kamailio-4.0.4/utils/pdbt/docs/0000755000000000000000000000000012223032460015016 5ustar rootrootkamailio-4.0.4/utils/pdbt/docs/data_format.txt0000644000000000000000000000132712223032460020043 0ustar rootroot* File format off the number portability data * The number portability data consists only from the number or number block in the internation format followed by an semi-colon and the carrier identity. Each entry must be in a separate line. Format [0-9]{n,m}; [0-9][0-9][0-9] For example: 495041748;012 493687413;009 The meaning of this entry is that the first number or block 495041748 belongs to the carrier 12, the second to the carrier 9. * File format of the carrier name file * Each carrier id and name must be in a separate line. Format: D[0-9][0-9][0-9] For example: D001 Deutsche Telekom AG, Bonn D012 BT Germany GmbH & Co. OHG This means that the 495041748 block belongs to British Telecom Germany. kamailio-4.0.4/utils/pdbt/docs/network_protocol.txt0000644000000000000000000000153712223032460021177 0ustar rootroot* Network protocol of pdb server and sip-router module * The pdb server daemon listen only for UDP messages. The requests contains in the UDP payload the number starting with a international prefix, e.g. '49' for germany. It must contain only numbers like this: '49721913742734'. The answer packet contains then the number string from the respective request, null-terminated and followed by two bytes which represents the result. This two bytes are interpreted as 16 bit signed integer value, the UDP payload is in network byte order (most significant byte first). Possible values for the search request: * 0: the number could not be found * 1-999 the number was found and the result represents its carrier ID * 1000: the number could be found, but its owned from a carriers which is not interesting for us and belongs to the "other carrier" group kamailio-4.0.4/utils/pdbt/carrier.c0000644000000000000000000000442512223032460015666 0ustar rootroot/* * Copyright (C) 2009 1&1 Internet AG * * This file is part of sip-router, a free SIP server. * * sip-router is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * sip-router is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define _GNU_SOURCE #include "carrier.h" #include "log.h" #include #include #include #include char *cnames[MAX_CARRIERID+1]; void init_carrier_names() { int i; for (i=0; i<=MAX_CARRIERID; i++) cnames[i]=NULL; cnames[OTHER_CARRIERID] = "other carriers merged by this tool"; } int load_carrier_names(char *filename) { FILE * fp; char * line = NULL; size_t len = 0; ssize_t read; char *p; int n; char idstr[4]; long int id; int ret=0; idstr[3]=0; fp = fopen(filename, "r"); if (fp == NULL) { LERR("cannot open file '%s'\n", filename); return -1; } n=1; while ((read = getline(&line, &len, fp)) != -1) { p=line; len=strlen(p); if (len<=5) { LWARNING("invalid line %ld\n", (long int)n); ret=-1; goto nextline; } idstr[0] = p[1]; idstr[1] = p[2]; idstr[2] = p[3]; p+=5; len-=5; id = strtol(idstr, NULL, 10); if (!IS_VALID_PDB_CARRIERID(id)) { LWARNING("invalid carrier id '%s'\n", idstr); ret=-1; goto nextline; } cnames[id]=malloc(len+1); if (cnames[id]==NULL) { LERR("out of memory (needed %ld bytes)\n", (long int)len); ret=-1; exit(-1); } strncpy(cnames[id], p, len - 1); cnames[id][len - 1]=0; nextline: n++; } if (line) free(line); fclose(fp); return ret; } char *carrierid2name(carrier_t carrier) { char *s; if (!IS_VALID_CARRIERID(carrier)) s="invalid carrier id"; else s=cnames[carrier]; if (s==NULL) s="unknown carrier"; return s; } kamailio-4.0.4/utils/pdbt/Makefile0000644000000000000000000000121712223032460015527 0ustar rootroot.phony: all clean install header=common.h carrier.h dt.h dtm.h pdb_server_backend.h log.h obj=dt.o dtm.o carrier.o pdb_server_backend.o log.o pdb_server_obj=pdb_server_backend.o dtm.o log.o cflags=-Wall -O2 -g # -march=x86-64 extdep=Makefile all: pdbt pdb_server $(obj): %.o : %.c %.h $(header) $(extdep) gcc $(cflags) -c $< pdbt: pdbt.c $(obj) $(header) $(extdep) gcc $(cflags) -o $@ $< $(obj) pdb_server: pdb_server.c $(pdb_server_obj) $(header) $(extdep) gcc $(cflags) -o $@ $< $(pdb_server_obj) clean: rm -f *~ *.o pdbt pdb_server install: cp pdbt $(DESTDIR)/usr/bin/ cp pdb_server $(DESTDIR)/usr/bin/ cp pdb_server.conf $(DESTDIR)/etc/ kamailio-4.0.4/utils/pdbt/common.h0000644000000000000000000000302212223032460015524 0ustar rootroot/* * Copyright (C) 2009 1&1 Internet AG * * This file is part of sip-router, a free SIP server. * * sip-router is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * sip-router is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _COMMON_H_ #define _COMMON_H_ #include /* 0 no carrier id defined. 1..999 are regular carrier ids. 1000 is used as fake carrier id when merging carriers we are not interested in. -1000..-1 used in dtm to indicate a carrier id and that no more nodes will follow (leaf node compression). -1001 used in dtm to mark a pointer to a child node as NULL. */ #define MIN_PDB_CARRIERID 1 #define MAX_PDB_CARRIERID 999 #define OTHER_CARRIERID 1000 #define MAX_CARRIERID 1000 #define NULL_CARRIERID -1001 #define IS_VALID_PDB_CARRIERID(id) ((id>=MIN_PDB_CARRIERID) && (id<=MAX_PDB_CARRIERID)) #define IS_VALID_CARRIERID(id) ((id>=MIN_PDB_CARRIERID) && (id<=MAX_CARRIERID)) typedef int16_t carrier_t; #endif kamailio-4.0.4/utils/sercmd/0000755000000000000000000000000012223032461014413 5ustar rootrootkamailio-4.0.4/utils/sercmd/parse_listen_id.c0000644000000000000000000001306312223032461017726 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* History: * -------- * 2006-02-20 created by andrei */ #include #include /* malloc */ #include #include /* getservbyname*/ #include /* ntohs */ #include "parse_listen_id.h" /* ser source compat. defines*/ #define pkg_malloc malloc #define pkg_free free #ifdef __SUNPRO_C #define DBG(...) #define LOG(lev, ...) fprintf(stderr, __VA_ARGS__) #else #define DBG(fmt, args...) #define LOG(lev, fmt, args...) fprintf(stderr, fmt, ## args) #endif /* converts a str to an u. short, returns the u. short and sets *err on * error and if err!=null */ static inline unsigned short str2s(const char* s, unsigned int len, int *err) { unsigned short ret; int i; unsigned char *limit; unsigned char *init; unsigned char* str; /*init*/ str=(unsigned char*)s; ret=i=0; limit=str+len; init=str; for(;str= '0') ){ ret=ret*10+*str-'0'; i++; if (i>5) goto error_digits; }else{ /* error unknown char */ goto error_char; } } if (err) *err=0; return ret; error_digits: DBG("str2s: ERROR: too many letters in [%.*s]\n", (int)len, init); if (err) *err=1; return 0; error_char: DBG("str2s: ERROR: unexpected char %c in %.*s\n", *str, (int)len, init); if (err) *err=1; return 0; } /* parse proto:address:port or proto:address */ /* returns struct id_list on success (pkg_malloc'ed), 0 on error * WARNING: it will add \0 in the string*/ /* parses: * tcp|udp|unix:host_name:port * tcp|udp|unix:host_name * host_name:port * host_name * * * where host_name=string, ipv4 address, [ipv6 address], * unix socket path (starts with '/') */ struct id_list* parse_listen_id(char* l, int len, enum socket_protos def) { char* p; enum socket_protos proto; char* name; char* port_str; int port; int err; struct servent* se; char* s; struct id_list* id; s=pkg_malloc((len+1)*sizeof(char)); if (s==0){ LOG(L_ERR, "ERROR:parse_listen_id: out of memory\n"); goto error; } memcpy(s, l, len); s[len]=0; /* null terminate */ /* duplicate */ proto=UNKNOWN_SOCK; port=0; name=0; port_str=0; p=s; if ((*p)=='[') goto ipv6; /* find proto or name */ for (; *p; p++){ if (*p==':'){ *p=0; if (strcasecmp("tcp", s)==0){ proto=TCP_SOCK; goto find_host; }else if (strcasecmp("udp", s)==0){ proto=UDP_SOCK; goto find_host; }else if (strcasecmp("unixd", s)==0){ proto=UNIXD_SOCK; goto find_host; }else if ((strcasecmp("unix", s)==0)||(strcasecmp("unixs", s)==0)){ proto=UNIXS_SOCK; goto find_host; #ifdef USE_FIFO }else if (strcasecmp("fifo", s)==0){ proto=FIFO_SOCK; goto find_host; #endif }else{ proto=UNKNOWN_SOCK; /* this might be the host */ name=s; goto find_port; } } } name=s; goto end; /* only name found */ find_host: p++; if (*p=='[') goto ipv6; name=p; for (; *p; p++){ if ((*p)==':'){ *p=0; goto find_port; } } goto end; /* nothing after name */ ipv6: name=p; p++; for(;*p;p++){ if(*p==']'){ if(*(p+1)==':'){ p++; *p=0; goto find_port; }else if (*(p+1)==0) goto end; }else{ goto error; } } find_port: p++; port_str=(*p)?p:0; end: /* fix all the stuff */ if (name==0) goto error; if (proto==UNKNOWN_SOCK){ /* try to guess */ if (port_str){ switch(def){ case TCP_SOCK: case UDP_SOCK: proto=def; break; default: proto=UDP_SOCK; DBG("guess:%s is a tcp socket\n", name); } }else if (name && strchr(name, '/')){ switch(def){ case TCP_SOCK: case UDP_SOCK: DBG("guess:%s is a unix socket\n", name); proto=UNIXS_SOCK; break; default: /* def is filename based => use default */ proto=def; } }else{ /* using default */ proto=def; } } if (port_str){ port=str2s(port_str, strlen(port_str), &err); if (err){ /* try getservbyname */ se=getservbyname(port_str, (proto==TCP_SOCK)?"tcp":(proto==UDP_SOCK)?"udp":0); if (se) port=ntohs(se->s_port); else goto error; } }else{ /* no port, check if the hostname is a port * (e.g. tcp:3012 == tcp:*:3012 */ if (proto==TCP_SOCK|| proto==UDP_SOCK){ port=str2s(name, strlen(name), &err); if (err){ port=0; }else{ name="*"; /* inaddr any */ } } } id=pkg_malloc(sizeof(struct id_list)); if (id==0){ LOG(L_ERR, "ERROR:parse_listen_id: out of memory\n"); goto error; } id->name=name; id->proto=proto; id->data_proto=P_BINRPC; id->port=port; id->buf=s; id->next=0; return id; error: if (s) pkg_free(s); return 0; } kamailio-4.0.4/utils/sercmd/EXAMPLES0000644000000000000000000000254212223032460015556 0ustar rootroot# $id$ sercmd usage examples help: sercmd -h use an udp ser control socket: ser config: loadmodule "modules/ctl/ctl.so" modparam("ctl", "binrpc", "udp:localhost:2046") modparam("ctl", "binrpc", "tcp:localhost:2046") modparam("ctl", "binrpc", "unixs:/tmp/unix_stream") modparam("ctl", "binrpc", "unixd:/tmp/unix_dgram") sercmd: sercmd -s udp:localhost:2046 core.version use a tcp socket: sercmd -s tcp:localhost:2046 core.version use a stream unix socket: sercmd -s unixs:/tmp/unix_stream core.version use a datagram unix socket: sercmd -s unixd:/tmp/unix_dgram core.version list available commands on ser side: sercmd -s unixd:/tmp/unix_drgam ls list all available commands (including sercmd builtins or aliases): sercmd -s unixd:/tmp/unix_dgram ? or sercmd -s unixd:/tmp/unix_dgram help get help on one command: sercmd -s unixd:/tmp/unix_dgram help core.ps list ser processes: sercmd -s unixd:/tmp/unix_dgram ps send an rpc command to ser: sercmd -s unixd:/tmp/unix_dgram core.shmmem format the output: sercmd -s unixd:/tmp/unix_dgram -f 'pid:%v desc:"%v"\n' core.ps (note: you could use just ps instead of core.ps) format the output as csv: sercmd -s unixd:/tmp/unix_dgram -f '%v,' core.ps enter interactive mode: sercmd -s unixd:/tmp/unix_dgram (note: type help,or ? to see the command list, tab expansion should also work) kamailio-4.0.4/utils/sercmd/license.h0000644000000000000000000000210512223032460016203 0ustar rootroot/* * $Id$ * * sercmd GPL license, standard disclaimer and copyright */ #ifndef __license_h_ #define __license_h_ #define COPYRIGHT "Copyright 2006 iptelorg GmbH" #define DISCLAIMER \ "This is free software with ABSOLUTELY NO WARRANTY.\n\ For details type `warranty'." #define LICENSE \ " This program is free software; you can redistribute it and/or modify\n\ it under the terms of the GNU General Public License as published by\n\ the Free Software Foundation; either version 2 of the License , or\n\ (at your option) any later version.\n\ \n\ This program is distributed in the hope that it will be useful,\n\ but WITHOUT ANY WARRANTY; without even the implied warranty of\n\ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\ GNU General Public License for more details.\n\ \n\ You should have received a copy of the GNU General Public License\n\ along with this program. If not, write to\n\ \n\ The Free Software Foundation, Inc.\n\ 51 Franklin Street, Fifth Floor,\n\ Boston, MA 02110-1301, USA." #endif kamailio-4.0.4/utils/sercmd/sercmd.80000644000000000000000000000117212223032460015761 0ustar rootroot.\" $Id$ .TH kamcmd 8 23.10.2012 Kamailio "sip-router" .\" Process with .\" groff -man -Tascii kamcmd.8 .\" .SH NAME kamcmd \- sip-router command line tool .SH SYNOPSIS .B kamcmd [ .B \-h ] .SH DESCRIPTION .B kamcmd is a command line to interact with .B Kamailio SIP server It can be used to manage users, domains, aliases and other server options. .SH OPTIONS .TP 12 .B \-h display a short usage description, including all available options. .SH AUTHORS see .B /usr/share/doc/kamailio/AUTHORS .SH SEE ALSO .BR kamailio(8), .BR kamailio.cfg(5) .PP Full documentation on sip-router is available at .I http://www.kamailio.org/. kamailio-4.0.4/utils/sercmd/sercmd.c0000644000000000000000000015261612223032460016046 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * send commands using binrpc * * History: * -------- * 2006-02-14 created by andrei * 2009-06-29 command line completion for cfg groups and vars (andrei) * 2009-06-30 command line completion for mi cmds (andrei) * 2010-08-08 command line completion for counters/statistic (andrei) */ #include /* exit, abort */ #include #include #include #include #include /* isprint */ #include #include /* unix sock*/ #include /* udp sock */ #include /* writev */ #include /* gethostbyname */ #include /* time */ #ifdef USE_READLINE #include #include #define USE_CFG_VARS /* cfg group and vars completion */ #define USE_MI /* mi completion */ #define USE_COUNTERS /* counters/statistics completion */ #endif #include "parse_listen_id.h" #include "license.h" #include "../../modules/ctl/ctl_defaults.h" /* default socket & port */ #include "../../modules/ctl/binrpc.h" #include "../../modules/ctl/binrpc.c" /* ugly hack */ #ifndef NAME #define NAME "sercmd" #endif #ifndef VERSION #define VERSION "0.2" #endif #define IOVEC_CNT 20 #define MAX_LINE_SIZE 16384 /* for non readline mode */ #define MAX_REPLY_SIZE 65536 #define MAX_BODY_SIZE 65536 #define MAX_BINRPC_ARGS 256 #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 108 #endif static char id[]="$Id$"; static char version[]= NAME " " VERSION; static char compiled[]= __TIME__ " " __DATE__; static char help_msg[]="\ Usage: " NAME " [options][-s address] [ cmd ]\n\ Options:\n\ -s address unix socket name or host name to send the commands on\n\ -R name force reply socket name, for the unix datagram socket mode\n\ -D dir create the reply socket in the directory if no reply \n\ socket is forced (-R) and a unix datagram socket is selected\n\ as the transport\n\ -f format print the result using format. Format is a string containing\n\ %v at the places where values read from the reply should be\n\ substituted. To print '%v', escape it using '%': %%v.\n\ -v Verbose \n\ -V Version number\n\ -h This help message\n\ address:\n\ [proto:]name[:port] where proto is one of tcp, udp, unixs or unixd\n\ e.g.: tcp:localhost:2048 , unixs:/tmp/ser_ctl\n\ cmd:\n\ method [arg1 [arg2...]]\n\ arg:\n\ string or number; to force a number to be interpreted as string \n\ prefix it by \"s:\", e.g. s:1\n\ Examples:\n\ " NAME " -s unixs:/tmp/ser_unix system.listMethods\n\ " NAME " -f \"pid: %v desc: %v\\n\" -s udp:localhost:2047 core.ps \n\ " NAME " ps # uses default ctl socket \n\ " NAME " # enters interactive mode on the default socket \n\ " NAME " -s tcp:localhost # interactive mode, default port \n\ "; int verbose=0; char* reply_socket=0; /* unix datagram reply socket name */ char* sock_dir=0; /* same as above, but only the directory */ char* unix_socket=0; struct sockaddr_un mysun; int quit; /* used only in interactive mode */ struct binrpc_val* rpc_array; int rpc_no=0; #ifdef USE_CFG_VARS struct binrpc_val* cfg_vars_array; int cfg_vars_no; struct cfg_var_grp{ struct cfg_var_grp* next; str grp_name; /**< group name */ str* var_names; /**< str array, null terminated */ int var_no; }; struct cfg_var_grp* cfg_grp_lst; /** cfg groups list, allong with var names*/ struct cfg_var_grp* crt_cfg_grp; #endif /* USE_CFG_VARS */ #ifdef USE_MI struct binrpc_val* mi_which_array; int mi_which_no; str* mi_cmds; int mi_cmds_no; #endif /* USE_MI */ #ifdef USE_COUNTERS struct binrpc_val* cnt_grps_array; /* response array */ int cnt_grps_no; /* number of response records */ struct cnt_var_grp { struct cnt_var_grp * next; str grp_name; str* var_names; /**< str array (null terminated strings)*/ int var_no; struct binrpc_val* cnt_vars_array; /* var_name will point here */ int cnt_vars_no; /* cnt_vars_array size (no. of response records) */ }; struct cnt_var_grp* cnt_grp_lst; /* counters groups list, allong with vars */ struct cnt_var_grp* crt_cnt_grp; #endif /* USE_COUNTERS */ #define IOV_SET(vect, str) \ do{\ (vect).iov_base=(str); \ (vect).iov_len=strlen((str)); \ }while(0) #define INT2STR_MAX_LEN (19+1+1) /* 2^64~= 16*10^18 => 19+1 digits + \0 */ /* returns a pointer to a static buffer containing l in asciiz & sets len */ static inline char* int2str(unsigned int l, int* len) { static char r[INT2STR_MAX_LEN]; int i; i=INT2STR_MAX_LEN-2; r[INT2STR_MAX_LEN-1]=0; /* null terminate */ do{ r[i]=l%10+'0'; i--; l/=10; }while(l && (i>=0)); if (l && (i<0)){ fprintf(stderr, "BUG: int2str: overflow\n"); } if (len) *len=(INT2STR_MAX_LEN-2)-i; return &r[i+1]; } static char* trim_ws(char* l) { char* ret; for(;*l && ((*l==' ')||(*l=='\t')||(*l=='\n')||(*l=='\r')); l++); ret=l; if (*ret==0) return ret; for(l=l+strlen(l)-1; (l>ret) && ((*l==' ')||(*l=='\t')||(*l=='\n')||(*l=='\r')); l--); *(l+1)=0; return ret; } int gen_cookie() { return rand(); } struct binrpc_cmd{ char* method; int argc; struct binrpc_val argv[MAX_BINRPC_ARGS]; }; struct cmd_alias{ char* name; char* method; char* format; /* reply print format */ }; struct sercmd_builtin{ char* name; int (*f)(int, struct binrpc_cmd*); char* doc; }; static int sercmd_help(int s, struct binrpc_cmd* cmd); static int sercmd_ver(int s, struct binrpc_cmd* cmd); static int sercmd_quit(int s, struct binrpc_cmd* cmd); static int sercmd_warranty(int s, struct binrpc_cmd* cmd); static struct cmd_alias cmd_aliases[]={ { "ps", "core.ps", "%v\t%v\n" }, { "list", "system.listMethods", 0 }, { "ls", "system.listMethods", 0 }, { "server", "core.version", 0 }, { "serversion", "core.version", 0 }, { "who", "ctl.who", "[%v] %v: %v %v -> %v %v\n"}, { "listen", "ctl.listen", "[%v] %v: %v %v\n"}, { "dns_mem_info", "dns.mem_info", "%v / %v\n"}, { "dns_debug", "dns.debug", "%v (%v): size=%v ref=%v expire=%vs last=%vs ago f=%v\n"}, { "dns_debug_all", "dns.debug_all", "%v (%v) [%v]: size=%v ref=%v expire=%vs last=%vs ago f=%v\n" "\t\t%v:%v expire=%vs f=%v\n"}, { "dst_blacklist_mem_info", "dst_blacklist.mem_info", "%v / %v\n"}, { "dst_blacklist_debug", "dst_blacklist.debug", "%v:%v:%v expire:%v flags: %v\n"}, {0,0,0} }; static struct sercmd_builtin builtins[]={ { "?", sercmd_help, "help"}, { "help", sercmd_help, "displays help for a command"}, { "version", sercmd_ver, "displays " NAME "version"}, { "quit", sercmd_quit, "exits " NAME }, { "exit", sercmd_quit, "exits " NAME }, { "warranty", sercmd_warranty, "displays " NAME "'s warranty info"}, { "license", sercmd_warranty, "displays " NAME "'s license"}, {0,0} }; #ifdef USE_READLINE enum complete_states { COMPLETE_INIT, COMPLETE_CMD_NAME, #ifdef USE_CFG_VARS COMPLETE_CFG_GRP, COMPLETE_CFG_VAR, #endif /* USE_CFG_VARS */ #ifdef USE_MI COMPLETE_MI, #endif /* USE_Mi */ #ifdef USE_COUNTERS COMPLETE_CNT_GRP, COMPLETE_CNT_VAR, #endif /* USE_COUNTERS */ COMPLETE_NOTHING }; /* instead of rl_attempted_completion_over which is not present in some readline emulations, use attempted_completion_state */ static enum complete_states attempted_completion_state; static int crt_param_no; /* commands for which we complete the params to other method names */ char* complete_params_methods[]={ "?", "h", "help", "system.methodSignature", "system.methodHelp", 0 }; #ifdef USE_CFG_VARS /* commands for which we complete the first param with a cfg var grp*/ char* complete_params_cfg_var[]={ "cfg.get", "cfg.help", "cfg.set_delayed_int", "cfg.set_delayed_string", "cfg.set_now_int", "cfg.set_now_string", 0 }; #endif /* USE_CFG_VARS */ #ifdef USE_MI /* commands for which we complete the first param with an mi command*/ char* complete_params_mi[]={ "mi", "mi_fifo", "mi_dg", "mi_xmlrpc", 0 }; #endif /* USE_MI */ #ifdef USE_COUNTERS /* commands for which we complete the first param with a counter group */ char* complete_param1_counter_grp[] = { "cnt.get", "cnt.get_raw", "cnt.grp_get_all", "cnt.reset", "cnt.var_list", "cnt.help", 0 }; /* commands for which we completed the 2nd param with a counter name */ char* complete_param2_counter_name[] = { "cnt.get", "cnt.get_raw", "cnt.reset", "cnt.help", 0 }; #endif /* USE_COUNTERS */ #endif /* USE_READLINE */ static int parse_arg(struct binrpc_val* v, char* arg) { int i; double f; char* tmp; int len; i=strtol(arg, &tmp, 10); if ((tmp==0) || (*tmp)){ f=strtod(arg, &tmp); if ((tmp==0) || (*tmp)){ /* not an int or a float => string */ len=strlen(arg); if ((len>=2) && (arg[0]=='s') && (arg[1]==':')){ tmp=&arg[2]; len-=2; }else{ tmp=arg; } v->type=BINRPC_T_STR; v->u.strval.s=tmp; v->u.strval.len=len; }else{ /* float */ v->type=BINRPC_T_DOUBLE; v->u.fval=f; } }else{ /* int */ v->type=BINRPC_T_INT; v->u.intval=i; } return 0; } static int parse_cmd(struct binrpc_cmd* cmd, char** argv, int count) { int r; cmd->method=argv[0]; if ((count-1)>MAX_BINRPC_ARGS){ fprintf(stderr, "ERROR: too many args %d, only %d allowed\n", count-1, MAX_BINRPC_ARGS); return -1; } for (r=1; rargv[r-1], argv[r])<0) return -1; } cmd->argc=r-1; return 0; } void print_binrpc_val(struct binrpc_val* v, int ident) { int r; if ((v->type==BINRPC_T_STRUCT) && !v->u.end) ident--; /* fix to have strut beg. idented differently */ for (r=0; rname.s){ printf("%.*s: ", v->name.len, v->name.s); } switch(v->type){ case BINRPC_T_INT: printf("%d", v->u.intval); break; case BINRPC_T_STR: case BINRPC_T_BYTES: printf("%.*s", v->u.strval.len, v->u.strval.s); break; case BINRPC_T_ARRAY: printf("%c", (v->u.end)?']':'['); break; case BINRPC_T_STRUCT: printf("%c", (v->u.end)?'}':'{'); break; case BINRPC_T_DOUBLE: printf("%f", v->u.fval); break; default: printf("ERROR: unknown type %d\n", v->type); }; } /* opens, and connects on a STREAM unix socket * returns socket fd or -1 on error */ int connect_unix_sock(char* name, int type) { struct sockaddr_un ifsun; int s; int len; int ret; int retries; retries=0; s=-1; memset(&ifsun, 0, sizeof (struct sockaddr_un)); len=strlen(name); if (len>UNIX_PATH_MAX){ fprintf(stderr, "ERROR: connect_unix_sock: name too long " "(%d > %d): %s\n", len, UNIX_PATH_MAX, name); goto error; } ifsun.sun_family=AF_UNIX; memcpy(ifsun.sun_path, name, len); #ifdef HAVE_SOCKADDR_SA_LEN ifsun.sun_len=len; #endif s=socket(PF_UNIX, type, 0); if (s==-1){ fprintf(stderr, "ERROR: connect_unix_sock: cannot create unix socket" " %s: %s [%d]\n", name, strerror(errno), errno); goto error; } if (type==SOCK_DGRAM){ /* we must bind so that we can receive replies */ if (reply_socket==0){ if (sock_dir==0) sock_dir="/tmp"; retry: ret=snprintf(mysun.sun_path, UNIX_PATH_MAX, "%s/" NAME "_%d", sock_dir, rand()); if ((ret<0) ||(ret>=UNIX_PATH_MAX)){ fprintf(stderr, "ERROR: buffer overflow while trying to" "generate unix datagram socket name"); goto error; } }else{ if (strlen(reply_socket)>UNIX_PATH_MAX){ fprintf(stderr, "ERROR: buffer overflow while trying to" "use the provided unix datagram socket name (%s)", reply_socket); goto error; } strcpy(mysun.sun_path, reply_socket); } mysun.sun_family=AF_UNIX; if (bind(s, (struct sockaddr*)&mysun, sizeof(mysun))==-1){ if (errno==EADDRINUSE && (reply_socket==0) && (retries < 10)){ retries++; /* try another one */ goto retry; } fprintf(stderr, "ERROR: could not bind the unix socket to" " %s: %s (%d)\n", mysun.sun_path, strerror(errno), errno); goto error; } unix_socket=mysun.sun_path; } if (connect(s, (struct sockaddr *)&ifsun, sizeof(ifsun))==-1){ fprintf(stderr, "ERROR: connect_unix_sock: connect(%s): %s [%d]\n", name, strerror(errno), errno); goto error; } return s; error: if (s!=-1) close(s); return -1; } int connect_tcpudp_socket(char* address, int port, int type) { struct sockaddr_in addr; struct hostent* he; int sock; sock=-1; /* resolve destination */ he=gethostbyname(address); if (he==0){ fprintf(stderr, "ERROR: could not resolve %s\n", address); goto error; } /* open socket*/ addr.sin_family=he->h_addrtype; addr.sin_port=htons(port); memcpy(&addr.sin_addr.s_addr, he->h_addr_list[0], he->h_length); sock = socket(he->h_addrtype, type, 0); if (sock==-1){ fprintf(stderr, "ERROR: socket: %s\n", strerror(errno)); goto error; } if (connect(sock, (struct sockaddr*) &addr, sizeof(struct sockaddr))!=0){ fprintf(stderr, "ERROR: connect: %s\n", strerror(errno)); goto error; } return sock; error: if (sock!=-1) close(sock); return -1; } static void hexdump(unsigned char* buf, int len, int ascii) { int r, i; /* dump it in hex */ for (r=0; rmethod, strlen(cmd->method)); if (ret<0) goto binrpc_err; for (r=0; rargc; r++){ switch(cmd->argv[r].type){ case BINRPC_T_STR: ret=binrpc_addstr(&body, cmd->argv[r].u.strval.s, cmd->argv[r].u.strval.len); break; case BINRPC_T_INT: ret=binrpc_addint(&body, cmd->argv[r].u.intval); break; case BINRPC_T_DOUBLE: ret=binrpc_adddouble(&body, cmd->argv[r].u.fval); break; default: fprintf(stderr, "ERROR: unsupported type %d\n", cmd->argv[r].type); } if (ret<0) goto binrpc_err; } ret=binrpc_build_hdr(BINRPC_REQ, binrpc_pkt_len(&body), cookie, msg_hdr, BINRPC_MAX_HDR_SIZE); if (ret<0) goto binrpc_err; v[0].iov_base=msg_hdr; v[0].iov_len=ret; v[1].iov_base=msg_body; v[1].iov_len=binrpc_pkt_len(&body); write_again: if ((n=writev(s, v, 2))<0){ if (errno==EINTR) goto write_again; goto error_send; } return n; error_send: return -1; binrpc_err: return -2; } static int binrpc_errno=0; /* reads the whole reply * returns < 0 on error, reply size on success + initializes in_pkt * if ret==-2 (parse error), sets binrpc_errno to the binrpc error * error returns: -1 - read error (check errno) * -2 - binrpc parse error (chekc binrpc_errno) * -3 - cookie error (the cookied doesn't match) * -4 - message too big */ static int get_reply(int s, unsigned char* reply_buf, int max_reply_size, int cookie, struct binrpc_parse_ctx* in_pkt, unsigned char** body) { unsigned char* crt; unsigned char* hdr_end; unsigned char* msg_end; int n; int ret; hdr_end=crt=reply_buf; msg_end=reply_buf+max_reply_size; binrpc_errno=0; do{ n=read(s, crt, (int)(msg_end-crt)); if (n<=0){ if (errno==EINTR) continue; goto error_read; } if (verbose >= 3){ /* dump it in hex */ printf("received %d bytes in reply (@offset %d):\n", n, (int)(crt-reply_buf)); hexdump(crt, n, 1); } crt+=n; /* parse header if not parsed yet */ if (hdr_end==reply_buf){ hdr_end=binrpc_parse_init(in_pkt, reply_buf, n, &ret); if (ret<0){ if (ret==E_BINRPC_MORE_DATA) continue; goto error_parse; } if (verbose>1){ printf("new packet: type %02x, len %d, cookie %02x\n", in_pkt->type, in_pkt->tlen, in_pkt->cookie); } if (in_pkt->cookie!=cookie){ fprintf(stderr, "bad reply, cookie doesn't match: sent %02x " "and received %02x\n", cookie, in_pkt->cookie); goto error; } msg_end=hdr_end+in_pkt->tlen; if ((int)(msg_end-reply_buf)>max_reply_size) goto error_toolong; } }while(crtoffset, *p, binrpc_error(ret)); goto error; } rec++; if (fmt){ print_binrpc_val(&val, 0); }else{ print_binrpc_val(&val, in_pkt->in_struct+in_pkt->in_array); putchar('\n'); } } if (fmt && *f){ /* print the rest, with empty values */ while(*f){ s=f; f=parse_fmt(f, &val.type, &f_size); printf("%.*s", f_size, s); } } return 0; error: return -1; /*error_read_again: fprintf(stderr, "ERROR: more data needed\n"); return -2; */ } static int print_fault(struct binrpc_parse_ctx* in_pkt, unsigned char* body, int size) { printf("error: "); return print_body(in_pkt, body, size, "%v - %v\n"); } static int run_binrpc_cmd(int s, struct binrpc_cmd * cmd, char* fmt) { int cookie; unsigned char reply_buf[MAX_REPLY_SIZE]; unsigned char* msg_body; struct binrpc_parse_ctx in_pkt; int ret; cookie=gen_cookie(); if ((ret=send_binrpc_cmd(s, cmd, cookie))<0){ if (ret==-1) goto error_send; else goto binrpc_err; } /* read reply */ memset(&in_pkt, 0, sizeof(in_pkt)); if ((ret=get_reply(s, reply_buf, MAX_REPLY_SIZE, cookie, &in_pkt, &msg_body))<0){ switch(ret){ case -1: goto error_read; case -2: goto error_parse; case -3: goto error_cookie; case -4: goto error_toobig; } goto error; } switch(in_pkt.type){ case BINRPC_FAULT: if (print_fault(&in_pkt, msg_body, in_pkt.tlen)<0){ goto error; } break; case BINRPC_REPL: if (print_body(&in_pkt, msg_body, in_pkt.tlen, fmt)<0){ goto error; } break; default: fprintf(stderr, "ERROR: not a reply\n"); goto error; } if (verbose) printf(".\n"); /* normal exit */ return 0; binrpc_err: fprintf(stderr, "ERROR while building the packet: %s\n", binrpc_error(ret)); goto error; error_parse: fprintf(stderr, "ERROR while parsing the reply: %s\n", binrpc_error(binrpc_errno)); goto error; error_cookie: fprintf(stderr, "ERROR: cookie does not match\n"); goto error; error_toobig: fprintf(stderr, "ERROR: reply too big\n"); goto error; error_send: fprintf(stderr, "ERROR: send packet failed: %s (%d)\n", strerror(errno), errno); goto error; error_read: fprintf(stderr, "ERROR: read reply failed: %s (%d)\n", strerror(errno), errno); goto error; error: return -1; } static int parse_line(struct binrpc_cmd* cmd, char* line) { char* p; int count; cmd->method=strtok(line, " \t"); if (cmd->method==0) goto error_no_method; count=0; for(p=strtok(0, " \t"); p; p=strtok(0, " \t")){ if (count>=MAX_BINRPC_ARGS) goto error_too_many; if (parse_arg(&cmd->argv[count], p)<0){ goto error_arg; } count++; } cmd->argc=count; return 0; error_no_method: printf( "ERROR: no method name\n"); return -1; error_too_many: printf("ERROR: too many arguments (%d), no more than %d allowed\n", count, MAX_BINRPC_ARGS); return -1; error_arg: printf("ERROR: bad argument %d: %s\n", count+1, p); return -1; } /* resolves builtin aliases */ static void fix_cmd(struct binrpc_cmd* cmd, char** format) { int r; for (r=0; cmd_aliases[r].name; r++){ if (strcmp(cmd_aliases[r].name, cmd->method)==0){ cmd->method=cmd_aliases[r].method; if (*format==0) *format=cmd_aliases[r].format; break; } } } /* intercept builtin commands, returns 1 if intercepted, 0 if not, <0 on error */ static int run_builtins(int s, struct binrpc_cmd* cmd) { int r; int ret; for (r=0; builtins[r].name; r++){ if (strcmp(builtins[r].name, cmd->method)==0){ ret=builtins[r].f(s, cmd); return (ret<0)?ret:1; } } return 0; } /* runs command from cmd */ inline static int run_cmd(int s, struct binrpc_cmd* cmd, char* format) { int ret; char* fmt; fmt=format; fix_cmd(cmd, &fmt); if (!(ret=run_builtins(s, cmd))){ ret=run_binrpc_cmd(s, cmd, fmt); } return (ret>0)?0:ret; } /* runs a command represented in line */ inline static int run_line(int s, char* l, char* format) { struct binrpc_cmd cmd; int ret; if ((ret=parse_line(&cmd, l))==0){ return run_cmd(s, &cmd, format); } return ret; } static void free_rpc_array(struct binrpc_val* a, int size) { int r; for (r=0; roffset, *p, binrpc_error(ret)); goto error; } if (rec>=*records){ t=realloc(a, *records*sizeof(struct binrpc_val)*2); if (t==0) goto error_mem; a=t; *records*=2; } a[rec]=val; if (val.name.s){ if ((a[rec].name.s=malloc(val.name.len+1))==0) goto error_mem; memcpy(a[rec].name.s, val.name.s, val.name.len); a[rec].name.s[val.name.len+1]=0; /* 0-term */ } if (val.u.strval.s){ if (val.type==BINRPC_T_STR){ if ((a[rec].u.strval.s=malloc(val.u.strval.len+1))==0) goto error_mem; memcpy(a[rec].u.strval.s, val.u.strval.s, val.u.strval.len); a[rec].u.strval.s[val.u.strval.len]=0; /* 0-term */ }else if (val.type==BINRPC_T_BYTES){ if ((a[rec].u.strval.s=malloc(val.u.strval.len))==0) goto error_mem; memcpy(a[rec].u.strval.s, val.u.strval.s, val.u.strval.len); } } rec++; } if (rec && (rec<*records)){ a=realloc(a, rec*sizeof(struct binrpc_val)); } *records=rec; return a; error_mem: fprintf(stderr, "ERROR: parse_reply_body: out of memory\n"); error: if (a){ free_rpc_array(a, rec); } *records=0; return 0; } static int get_sercmd_list(int s) { struct binrpc_cmd cmd; int cookie; unsigned char reply_buf[MAX_REPLY_SIZE]; unsigned char* msg_body; struct binrpc_parse_ctx in_pkt; int ret; cmd.method="system.listMethods"; cmd.argc=0; cookie=gen_cookie(); if ((ret=send_binrpc_cmd(s, &cmd, cookie))<0){ if (ret==-1) goto error_send; else goto binrpc_err; } /* read reply */ memset(&in_pkt, 0, sizeof(in_pkt)); if ((ret=get_reply(s, reply_buf, MAX_REPLY_SIZE, cookie, &in_pkt, &msg_body))<0){ goto error; } switch(in_pkt.type){ case BINRPC_FAULT: if (print_fault(&in_pkt, msg_body, in_pkt.tlen)<0){ goto error; } break; case BINRPC_REPL: rpc_no=100; /* default cmd list */ if ((rpc_array=parse_reply_body(&rpc_no, &in_pkt, msg_body, in_pkt.tlen))==0) goto error; break; default: fprintf(stderr, "ERROR: not a reply\n"); goto error; } return 0; binrpc_err: error_send: error: return -1; } #if defined(USE_CFG_VARS) || defined (USE_MI) || defined (USE_COUNTERS) /** check if cmd is a rpc command. * Quick check (using the internal rpc_array) if cmd is a valid rpc command. * @param cmd - null terminated ascii string * @return 1 on success, 0 on failure. */ static int is_rpc_cmd(char* cmd) { int r; int cmd_len; cmd_len=strlen(cmd); for (r=0; r: */ for (p=grp_name.s; pnext){ if (grp->grp_name.len==grp_name.len && memcmp(grp->grp_name.s, grp_name.s, grp_name.len)==0){ break; /* found */ } } if (grp==0){ /* not found => create a new one */ grp=malloc(sizeof(*grp)); if (grp==0) goto error_mem; memset(grp, 0, sizeof(*grp)); grp->grp_name=grp_name; if (last_grp){ last_grp->next=grp; last_grp=grp; }else{ cfg_grp_lst=grp; last_grp=cfg_grp_lst; } } grp->var_no++; } /* alloc the var arrays per group */ for (grp=cfg_grp_lst; grp; grp=grp->next){ grp->var_names=malloc(sizeof(str)*grp->var_no); if (grp->var_names==0) goto error_mem; memset(grp->var_names, 0, sizeof(str)*grp->var_no); grp->var_no=0; } /* reparse to get the var names per group */ for (r=0; r: */ for (p=grp_name.s; pnext){ if (grp->grp_name.len==grp_name.len && memcmp(grp->grp_name.s, grp_name.s, grp_name.len)==0){ /* add var */ grp->var_names[grp->var_no]=var_name; grp->var_no++; } } break; } } } return 0; binrpc_err: error_send: error: error_mem: return -1; } void free_cfg_grp_lst() { struct cfg_var_grp* grp; struct cfg_var_grp* last; grp=cfg_grp_lst; while(grp){ last=grp; grp=grp->next; free(last); } cfg_grp_lst=0; } #endif /* USE_CFG_VARS */ #ifdef USE_MI /* retrieve the mi list */ static int get_mi_list(int s) { struct binrpc_cmd cmd; int cookie; unsigned char reply_buf[MAX_REPLY_SIZE]; unsigned char* msg_body; struct binrpc_parse_ctx in_pkt; char* p; char* end; str mi_name; int mi_which_results; int r; int ret; cmd.method="mi"; cmd.argv[0].type=BINRPC_T_STR; cmd.argv[0].u.strval.s="which"; cmd.argv[0].u.strval.len=strlen(cmd.argv[0].u.strval.s); cmd.argc=1; if (!is_rpc_cmd(cmd.method)) goto error; cookie=gen_cookie(); if ((ret=send_binrpc_cmd(s, &cmd, cookie))<0){ if (ret==-1) goto error_send; else goto binrpc_err; } /* read reply */ memset(&in_pkt, 0, sizeof(in_pkt)); if ((ret=get_reply(s, reply_buf, MAX_REPLY_SIZE, cookie, &in_pkt, &msg_body))<0){ goto error; } switch(in_pkt.type){ case BINRPC_FAULT: if (print_fault(&in_pkt, msg_body, in_pkt.tlen)<0){ goto error; } break; case BINRPC_REPL: mi_which_no=25; /* default rpc list */ if ((mi_which_array=parse_reply_body(&mi_which_no, &in_pkt, msg_body, in_pkt.tlen))==0) goto error; break; default: fprintf(stderr, "ERROR: not a reply\n"); goto error; } /* get the mi commands number */ mi_which_results=0; for (r=0; r=end) || (*p!=':')) continue; p++; /* skip over to the next ':' */ for(;p=end) continue; p++; /* skip over spaces */ for(;p=end || *p=='\n') continue; if (mi_cmds_no >= mi_which_results){ fprintf(stderr, "BUG: wrong mi cmds no (%d >= %d)\n", mi_cmds_no, mi_which_results); goto error; } mi_name.s=p; for(; pnext){ if (grp->grp_name.len==grp_name.len && memcmp(grp->grp_name.s, grp_name.s, grp_name.len)==0){ break; /* found */ } } if (grp==0){ /* not found => create a new one */ grp=malloc(sizeof(*grp)); if (grp==0) goto error_mem; memset(grp, 0, sizeof(*grp)); grp->grp_name=grp_name; if (last_grp){ last_grp->next=grp; last_grp=grp; }else{ cnt_grp_lst=grp; last_grp=cnt_grp_lst; } } } /* gets vars per group */ for (grp=cnt_grp_lst; grp; grp=grp->next){ cmd.method="cnt.var_list"; cmd.argv[0].type=BINRPC_T_STR; cmd.argv[0].u.strval=grp->grp_name; cmd.argc=1; if (!is_rpc_cmd(cmd.method)) goto error; cookie=gen_cookie(); if ((ret=send_binrpc_cmd(s, &cmd, cookie))<0){ if (ret==-1) goto error_send; else goto binrpc_err; } /* read reply */ memset(&in_pkt, 0, sizeof(in_pkt)); if ((ret=get_reply(s, reply_buf, MAX_REPLY_SIZE, cookie, &in_pkt, &msg_body))<0){ goto error; } switch(in_pkt.type){ case BINRPC_FAULT: if (print_fault(&in_pkt, msg_body, in_pkt.tlen)<0){ goto error; } break; case BINRPC_REPL: grp->cnt_vars_no=100; /* default counter list */ if ((grp->cnt_vars_array=parse_reply_body(&grp->cnt_vars_no, &in_pkt, msg_body, in_pkt.tlen))==0) goto error; break; default: fprintf(stderr, "ERROR: not a reply\n"); goto error; } grp->var_no = 0; grp->var_names=malloc(sizeof(str)*grp->cnt_vars_no); if (grp->var_names==0) goto error_mem; memset(grp->var_names, 0, sizeof(str)*grp->var_no); for (r=0; rcnt_vars_no; r++) { if (grp->cnt_vars_array[r].type!=BINRPC_T_STR) continue; var_name=grp->cnt_vars_array[r].u.strval; grp->var_names[grp->var_no] = var_name; grp->var_no++; } } return 0; binrpc_err: error_send: error: error_mem: return -1; } void free_cnt_grp_lst() { struct cnt_var_grp* grp; struct cnt_var_grp* last; grp=cnt_grp_lst; while(grp){ last=grp; grp=grp->next; if (last->cnt_vars_array) free_rpc_array(last->cnt_vars_array, last->cnt_vars_no); free(last); } cnt_grp_lst=0; } #endif /* USE_COUNTERS */ static void print_formatting(char* prefix, char* format, char* suffix) { if (format){ printf("%s", prefix); for (;*format;format++){ switch(*format){ case '\t': printf("\\t"); break; case '\n': printf("\\n"); break; case '\r': printf("\\r"); break; default: putchar(*format); } } printf("%s", suffix); } } static int sercmd_help(int s, struct binrpc_cmd* cmd) { int r; if (cmd->argc && (cmd->argv[0].type==BINRPC_T_STR)){ /* if it has args, try command help */ for (r=0; cmd_aliases[r].name; r++){ if (strcmp(cmd->argv[0].u.strval.s, cmd_aliases[r].name)==0){ printf("%s is an alias for %s", cmd->argv[0].u.strval.s, cmd_aliases[r].method); print_formatting(" with reply formatting: \"", cmd_aliases[r].format, "\""); putchar('\n'); return 0; } } for(r=0; builtins[r].name; r++){ if (strcmp(cmd->argv[0].u.strval.s, builtins[r].name)==0){ printf("builtin command: %s\n", builtins[r].doc?builtins[r].doc:"undocumented"); return 0; } } cmd->method="system.methodHelp"; if (run_binrpc_cmd(s, cmd, 0)<0){ printf("error: no such command %s\n", cmd->argv[0].u.strval.s); } return 0; } if (rpc_no==0){ if (get_sercmd_list(s)<0) goto error; } for (r=0; rnext; } for(;grp; grp=grp->next){ if (len<=grp->grp_name.len && memcmp(text, grp->grp_name.s, len)==0) { /* zero-term copy of the grp name */ name=malloc(grp->grp_name.len+1); if (name){ memcpy(name, grp->grp_name.s, grp->grp_name.len); name[grp->grp_name.len]=0; } return name; } } break; case COMPLETE_CFG_VAR: if (state==0){ /* init */ len=strlen(text); idx=0; } while(idx < crt_cfg_grp->var_no){ if (len<=crt_cfg_grp->var_names[idx].len && memcmp(text, crt_cfg_grp->var_names[idx].s, len)==0) { /* zero-term copy of the var name */ name=malloc(crt_cfg_grp->var_names[idx].len+1); if (name){ memcpy(name, crt_cfg_grp->var_names[idx].s, crt_cfg_grp->var_names[idx].len); name[crt_cfg_grp->var_names[idx].len]=0; } idx++; return name; } idx++; } break; #endif /* USE_CFG_VARS */ #ifdef USE_MI case COMPLETE_MI: if (state==0){ /* init */ len=strlen(text); idx=0; } while(idx < mi_cmds_no){ if (len<=mi_cmds[idx].len && memcmp(text, mi_cmds[idx].s, len)==0) { /* zero-term copy of the var name */ name=malloc(mi_cmds[idx].len+1); if (name){ memcpy(name, mi_cmds[idx].s, mi_cmds[idx].len); name[mi_cmds[idx].len]=0; } idx++; return name; } idx++; } break; #endif /* USE_MI */ #ifdef USE_COUNTERS case COMPLETE_CNT_GRP: if (state==0){ /* init */ len=strlen(text); cnt_grp=cnt_grp_lst; }else{ cnt_grp=cnt_grp->next; } for(;cnt_grp; cnt_grp=cnt_grp->next){ if (len<=cnt_grp->grp_name.len && memcmp(text, cnt_grp->grp_name.s, len)==0) { /* zero-term copy of the cnt_grp name */ name=malloc(cnt_grp->grp_name.len+1); if (name){ memcpy(name, cnt_grp->grp_name.s, cnt_grp->grp_name.len); name[cnt_grp->grp_name.len]=0; } return name; } } break; case COMPLETE_CNT_VAR: if (state==0){ /* init */ len=strlen(text); idx=0; } while(idx < crt_cnt_grp->var_no){ if (len<=crt_cnt_grp->var_names[idx].len && memcmp(text, crt_cnt_grp->var_names[idx].s, len)==0) { /* zero-term copy of the var name */ name=malloc(crt_cnt_grp->var_names[idx].len+1); if (name){ memcpy(name, crt_cnt_grp->var_names[idx].s, crt_cnt_grp->var_names[idx].len); name[crt_cnt_grp->var_names[idx].len]=0; } idx++; return name; } idx++; } break; #endif /* USE_COUNTERS */ } /* no matches */ return 0; } char** sercmd_completion(const char* text, int start, int end) { int i, j; int cmd_start, cmd_end, cmd_len; int whitespace; #ifdef USE_CFG_VARS struct cfg_var_grp* grp; static int grp_start; int grp_len; #endif /* USE_CFG_VARS */ #ifdef USE_COUNTERS struct cnt_var_grp* cnt_grp; static int cnt_grp_start; int cnt_grp_len; #endif /* USE_COUNTERS */ crt_param_no=0; /* skip over whitespace at the beginning */ for (j=0; (jnext){ if (grp_len==grp->grp_name.len && memcmp(&rl_line_buffer[grp_start], grp->grp_name.s, grp_len)==0) { attempted_completion_state=COMPLETE_CFG_VAR; crt_cfg_grp=grp; goto end; } } } } #endif /* USE_CFG_VARS */ #ifdef USE_COUNTERS /* see if we complete counter names for this command */ for(i=0; complete_param2_counter_name[i]; i++){ if ((cmd_len==strlen(complete_param2_counter_name[i])) && (strncmp(&rl_line_buffer[cmd_start], complete_param2_counter_name[i], cmd_len)==0)){ /* get the group name: */ /* find grp_start */ for(j=cmd_end; (jnext){ if (cnt_grp_len==cnt_grp->grp_name.len && memcmp(&rl_line_buffer[cnt_grp_start], cnt_grp->grp_name.s, cnt_grp_len)==0) { attempted_completion_state=COMPLETE_CNT_VAR; crt_cnt_grp=cnt_grp; goto end; } } } } #endif /* COUNTERS */ } attempted_completion_state=COMPLETE_NOTHING; } end: return 0; /* let readline call sercmd_generator */ } #endif /* USE_READLINE */ /* on exit cleanup */ static void cleanup() { if (unix_socket){ if (unlink(unix_socket)<0){ fprintf(stderr, "ERROR: failed to delete %s: %s\n", unix_socket, strerror(errno)); } } } int main(int argc, char** argv) { int c; char* sock_name; int sock_type; int s; struct binrpc_cmd cmd; struct id_list* sock_id; char* format; char* line; char* l; quit=0; format=0; line=0; s=-1; sock_name=0; sock_type=UNIXS_SOCK; opterr=0; while((c=getopt(argc, argv, "UVhs:D:R:vf:"))!=-1){ switch(c){ case 'V': printf("version: %s\n", version); printf("%s\n", id); printf("%s compiled on %s \n", __FILE__, compiled); exit(0); break; case 'h': printf("version: %s\n", version); printf("%s", help_msg); exit(0); break; case 's': sock_name=optarg; break; case 'R': reply_socket=optarg; break; case 'D': sock_dir=optarg; break; case 'U': sock_type=UDP_SOCK; break; case 'v': verbose++; break; case 'f': format=str_escape(optarg); if (format==0){ fprintf(stderr, "ERROR: memory allocation failure\n"); goto error; } break; case '?': if (isprint(optopt)) fprintf(stderr, "Unknown option `-%c'.\n", optopt); else fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt); goto error; case ':': fprintf(stderr, "Option `-%c' requires an argument.\n", optopt); goto error; default: abort(); } } if (sock_name==0){ sock_name=DEFAULT_CTL_SOCKET; } /* init the random number generator */ srand(getpid()+time(0)); /* we don't need very strong random numbers */ if (sock_name==0){ fprintf(stderr, "ERROR: no ser address specified\n"); goto error; } sock_id=parse_listen_id(sock_name, strlen(sock_name), sock_type); if (sock_id==0){ fprintf(stderr, "ERROR: error parsing ser address %s\n", sock_name); goto error; } switch(sock_id->proto){ case UDP_SOCK: case TCP_SOCK: if (sock_id->port==0){ sock_id->port=DEFAULT_CTL_PORT; /* fprintf(stderr, "ERROR: no port specified: %s:\n", sock_name); goto error; */ } if ((s=connect_tcpudp_socket(sock_id->name, sock_id->port, (sock_id->proto==UDP_SOCK)?SOCK_DGRAM: SOCK_STREAM))<0){ goto error; } break; case UNIXS_SOCK: case UNIXD_SOCK: if ((s=connect_unix_sock(sock_id->name, (sock_id->proto==UNIXD_SOCK)?SOCK_DGRAM: SOCK_STREAM))<0) goto error; break; case UNKNOWN_SOCK: fprintf(stderr, "ERROR: Bad socket type for %s\n", sock_name); goto error; } free(sock_id); /* not needed anymore */ sock_id=0; if (optind>=argc){ /*fprintf(stderr, "ERROR: no command specified\n"); goto error; */ }else{ if (parse_cmd(&cmd, &argv[optind], argc-optind)<0) goto error; if (run_cmd(s, &cmd, format)<0) goto error; goto end; } /* interactive mode */ if (get_sercmd_list(s)==0){ #ifdef USE_CFG_VARS get_cfgvars_list(s); #endif /* USE_CFG_VARS */ #ifdef USE_MI get_mi_list(s); #endif /* USE_MI */ #ifdef USE_COUNTERS get_counters_list(s); #endif /* USE_COUNTERS */ } /* banners */ printf("%s %s\n", NAME, VERSION); printf("%s\n", COPYRIGHT); printf("%s\n", DISCLAIMER); #ifdef USE_READLINE /* initialize readline */ /* allow conditional parsing of the ~/.inputrc file*/ rl_readline_name=NAME; rl_completion_entry_function=sercmd_generator; rl_attempted_completion_function=sercmd_completion; while(!quit){ line=readline(NAME "> "); if (line==0) /* EOF */ break; l=trim_ws(line); /* trim whitespace */ if (*l){ add_history(l); run_line(s, l, format); } free(line); line=0; } #else line=malloc(MAX_LINE_SIZE); if (line==0){ fprintf(stderr, "memory allocation error\n"); goto error; } printf(NAME "> "); fflush(stdout); /* prompt */ while(!quit && fgets(line, MAX_LINE_SIZE, stdin)){ l=trim_ws(line); if (*l){ run_line(s, l, format); } printf(NAME "> "); fflush(stdout); /* prompt */ }; free(line); line=0; #endif /* USE_READLINE */ end: /* normal exit */ if (line) free(line); if (format) free(format); if (rpc_array) free_rpc_array(rpc_array, rpc_no); #ifdef USE_CFG_VARS if (cfg_grp_lst) free_cfg_grp_lst(); if (cfg_vars_array){ free_rpc_array(cfg_vars_array, cfg_vars_no); cfg_vars_array=0; cfg_vars_no=0; } #endif /* USE_CFG_VARS */ #ifdef USE_MI if (mi_cmds) free_mi_cmds(); if (mi_which_array){ free_rpc_array(mi_which_array, mi_which_no); mi_which_array=0; mi_which_no=0; } #endif /* USE_MI */ #ifdef USE_COUNTERS if (cnt_grp_lst) free_cnt_grp_lst(); if (cnt_grps_array){ free_rpc_array(cnt_grps_array, cnt_grps_no); cnt_grps_array=0; cnt_grps_no=0; } #endif /* USE_COUNTERS */ cleanup(); exit(0); error: if (line) free(line); if (format) free(format); if (rpc_array) free_rpc_array(rpc_array, rpc_no); #ifdef USE_CFG_VARS if (cfg_grp_lst) free_cfg_grp_lst(); if (cfg_vars_array){ free_rpc_array(cfg_vars_array, cfg_vars_no); cfg_vars_array=0; cfg_vars_no=0; } #endif /* USE_CFG_VARS */ #ifdef USE_MI if (mi_cmds) free_mi_cmds(); if (mi_which_array){ free_rpc_array(mi_which_array, mi_which_no); mi_which_array=0; mi_which_no=0; } #endif /* USE_MI */ #ifdef USE_COUNTERS if (cnt_grp_lst) free_cnt_grp_lst(); if (cnt_grps_array){ free_rpc_array(cnt_grps_array, cnt_grps_no); cnt_grps_array=0; cnt_grps_no=0; } #endif /* USE_COUNTERS */ cleanup(); exit(-1); } kamailio-4.0.4/utils/sercmd/README0000644000000000000000000001460412223032460015277 0ustar rootroot# $Id$ # # History: # -------- # 2009-05-07 created by Andrei Pelinescu-Onciul Overview ======== kamcmd is a unix tool for interfacing with Kamailio using exported RPCs. It uses binrpc (a proprietary protocol, designed for minimal packet size and fast parsing) over a variety of transports (unix stream sockets, unix datagram sockets, udp or tcp). For more details on binrpc see the ctl module documentation (modules/ctl/README). kamcmd can work in command line mode (the RPC or command name is just another command line parameter) or in interactive mode. The interactive mode supports history and tab-completion (if kamcmd was compiled with libreadline support). On Kamailio side the ctl module must be loaded. Usage ===== kamcmd [options][-s address] [ cmd ] Options: -s address unix socket name or host name to send the commands on -R name force reply socket name, for the unix datagram socket mode -D dir create the reply socket in the directory if no reply socket is forced (-R) and an unix datagram socket is selected as the transport -f format print the result using format. Format is a string containing %v at the places where values read from the reply should be substituted. To print '%v', escape it using '%': %%v. -v Verbose -V Version number -h Help message address: [proto:]name[:port] where proto is one of tcp, udp, unixs, unix or unixd e.g.: tcp:localhost:2048 , unixs:/tmp/ser_ctl If the protocol is not specified, unixs will be used if name is a filesystem path and udp if not. "unixs" or "unix" stand for unix stream sockets and "unixd" for unix datagram sockets. cmd: method [arg1 [arg2...]] arg: string or number; to force a number to be interpreted as string prefix it by "s:", e.g. s:1 If no address is specified (no -s), kamcmd will use by default unixs:/tmp/ser_ctl. This is also the default for the ctl module (if no "binrpc" module parameters are present in the config). Command Types ============= There are 3 types of commands: "raw" RPC, kamcmd aliases and kamcmd builtins. The "raw" RPC commands work directly with ser with no change on the input or the output. To list available RPC commands, use "kamcmd help". The aliases are just easier to remember names for some RPCs, which some time include nicer formatting of the RPC result. One can see all the defined aliases using: kamcmd help|grep alias: . Example: ps is an alias for core.ps with the output formatted in a more readable way. kamcmd ps is equivalent to kamcmd -f"%v\t%v\n" core.ps. Without the formatting, the output of kamcmd core.ps looks like: 11262 attendant 11268 udp receiver child=0 sock=127.0.0.1:5060 ... Using kamcmd ps (or kamcmd -f"%v\t%v\n" core.ps) the output looks like: 11262 attendant 11268 udp receiver child=0 sock=127.0.0.1:5060 ... The built-in commands can combine several different rpcs. One can see all the built-in commands using: kamcmd help|grep builtin: . Getting help on a command ========================= To get the help message associated with a command use kamcmd help . Example: $ kamcmd help ps ps is an alias for core.ps with reply formatting: "%v\t%v\n" $ kamcmd help core.ps Returns the description of running Kamailio processes. Listing all the commands ======================== To see all the available commands (ser RPCs, aliases and bultins) use kamcmd help. To see only the "raw" RPCs, user kamcmd ls. Note: since each module can define its own RPCs, the available RPCs depend on the loaded modules. Examples ======== Using the default socket (requires only loadmodule "ctl" in ser.cfg): $ kamcmd ps 11262 attendant 11268 udp receiver child=0 sock=127.0.0.1:5060 11269 udp receiver child=1 sock=127.0.0.1:5060 11270 udp receiver child=0 sock=192.168.1.101:5060 11271 udp receiver child=1 sock=192.168.1.101:5060 11272 slow timer 11273 timer 11274 ctl handler 11275 tcp receiver child=0 11276 tcp receiver child=1 11277 tcp main process $ kamcmd help # list all the supported commands dst_blacklist.add ctl.who ... $ kamcmd help core.uptime # help for the core.uptime rpc Returns uptime of the Kamailio server. $ kamcmd cfg.cfg_set_int_now debug 5 # turn debug level to 5 (needs cfg) $ kamcmd # enters interactive mode Using a tcp socket (assumes modparam("ctl", "binrpc", "tcp:localhost:2048") in kamailio.cfg) $ kamcmd -s tcp:localhost:2048 core.version Server: Kamailio (3.3.2 (i386/linux)) $ kamcmd -s tcp:localhost:2048 SRV _sip._udp.iptel.org name: _sip._udp.iptel.org type: SRV size (bytes): 104 reference counter: 2 expires in (s): 67693 last used (s): 0 error flags: 0 rr name: sip.iptel.org rr port: 5060 rr priority: 0 rr weight: 0 rr expires in (s): 67693 rr error flags: 0 kamcmd -s tcp:127.0.0.1:2048 # enters interactive mode over tcp For more examples see utils/kamcmd/EXAMPLES [http://git.sip-router.org/cgi-bin/gitweb.cgi?p=sip-router;a=blob;f=utils/kamcmd/EXAMPLES]. Interactive Mode ================ To enter the interactive mode start kamcmd without specifying a command name on the command line. If kamcmd was compiled with libreadline support (automatically if libreadline dev files are installed), the interactive mode will have tab completion and history support. Example: $ kamcmd kamcmd 0.2 Copyright 2006 iptelorg GmbH This is free software with ABSOLUTELY NO WARRANTY. For details type `warranty'. kamcmd> core.s core.sctp_info core.sctp_options core.shmmem kamcmd> help core.shmmem Returns shared memory info. kamcmd> core.shmmem { total: 33554432 free: 33147816 used: 190644 real_used: 406616 max_used: 406616 fragments: 2 } kamcmd> quit Related Stuff ============= * ctl module: required, implements binrpc on Kamailio side, without it kamcmd doesn't work. See modules/ctl/README [http://git.sip-router.org/cgi-bin/gitweb.cgi?p=sip-router;a=blob;f=modules_s/ctl/README]. * cfg_rpc module: allows setting or reading configuration parameters on-the-fly. For example one could change the tcp connection lifetime to 180s using: $ kamcmd cfg.set_now_int tcp.connection_lifetime 180 See modules/cfg_rpc/README [http://git.sip-router.org/cgi-bin/gitweb.cgi?p=sip-router;a=blob;f=modules_s/cfg_rpc/README]. kamailio-4.0.4/utils/sercmd/TODO0000644000000000000000000000010412223032460015075 0ustar rootroot - help for internal commands & aliases - send timeout - reconnect? kamailio-4.0.4/utils/sercmd/parse_listen_id.h0000644000000000000000000000305412223032460017731 0ustar rootroot/* * $Id$ * * Copyright (C) 2006 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* History: * -------- * 2006-02-20 created by andrei */ #ifndef parse_listen_id_h #define parse_listen_id_h enum payload_proto { P_BINRPC , P_FIFO }; enum socket_protos { UNKNOWN_SOCK=0, UDP_SOCK, TCP_SOCK, UNIXS_SOCK, UNIXD_SOCK #ifdef USE_FIFO , FIFO_SOCK #endif }; struct id_list{ char* name; enum socket_protos proto; enum payload_proto data_proto; int port; char* buf; /* name points somewhere here */ struct id_list* next; }; struct id_list* parse_listen_id(char* l, int len, enum socket_protos def); #endif kamailio-4.0.4/utils/sercmd/Makefile0000644000000000000000000000446412223032461016063 0ustar rootroot# $Id$ COREPATH=../.. include $(COREPATH)/Makefile.defs include $(COREPATH)/Makefile.targets auto_gen= RELEASE=0.2 UTIL_SRC_NAME=sercmd ifeq ($(FLAVOUR),kamailio) NAME=kamcmd else NAME=sercmd endif readline_locations= /usr/include/readline/readline.h \ $(LOCALBASE)/include/readline/readline.h use_readline ?= ifneq (,$(MAKECMDGOALS)) ifeq (,$(filter-out $(nodep_targets),$(MAKECMDGOALS))) #set it to empty, we don't need to detect/use it for clean, doc a.s.o override use_readline:= quiet=1 endif endif #ifneq (,$(MAKECMDGOALS)) # erase common DEFS (not needed) C_DEFS:= DEFS:= -DNAME='"$(NAME)"' -DSRNAME='"$(MAIN_NAME)"' -DVERSION='"$(RELEASE)"' \ $(filter -D%HAVE -DARCH% -DOS% -D__CPU% -D__OS%, $(DEFS)) # use proper libs (we can't rely on LIBS value since we might be called # from a module Makefile) #LIBS:=$(filter-out -lfl -ldl -lpthread -lssl -lcrypto, $(LIBS)) ifeq ($(OS), linux) LIBS:= -lresolv endif ifeq ($(OS), solaris) LIBS:= -lresolv -L$(LOCALBASE)/lib -lxnet -lnsl endif ifeq ($(OS), freebsd) LIBS:= endif ifeq ($(OS), dragonfly) LIBS:= -L$(LOCALBASE)/lib endif ifeq ($(OS), openbsd) LIBS:= endif ifeq ($(OS), netbsd) LIBS:= endif ifeq ($(OS), darwin) LIBS:= -lresolv endif ifneq (,$(findstring cygwin, $(OS))) LIBS:= -lresolv endif ifeq ($(use_readline),) readline_path := $(shell \ for r in $(readline_locations) ""; do \ if [ -r "$$r" ] ; then echo $$r; exit; fi; \ done;\ ) ifneq ($(readline_path),) use_readline := 1 endif endif ifeq ($(use_readline),1) DEFS+=-DUSE_READLINE LIBS+=-lreadline -lncurses endif include $(COREPATH)/Makefile.utils ifeq (,$(quiet)) ifeq ($(use_readline),1) $(info readline detected ($(readline_path)) ) $(info command completion enabled) else $(info "no readline include files detected, disabling readline support") $(info "command completion disabled" ) $(info "to force readline support try 'make use_readline=1'") endif endif # ifeq (,$(quiet)) $(NAME).o: .PHONY: msg msg: @if [ "$(use_readline)" = "1" ]; then \ echo; echo "readline detected ($(readline_path)):"; \ echo "command completion enabled"; echo ; \ else \ echo ; \ echo "no readline include files detected, disabling readline support";\ echo "command completion disabled"; \ echo "(to force readline support try 'make use_readline=1')";\ echo ; \ fi modules: kamailio-4.0.4/types.h0000644000000000000000000000213412223032460013312 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _TYPES_H_ #define _TYPES_H_ typedef unsigned int process_bm_t; #endif kamailio-4.0.4/shm_init.h0000644000000000000000000000201412223032460013755 0ustar rootroot/* * $Id$ * * Copyright (C) 2010 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * shm_init.h */ /* * History: * -------- * 2010-01-10 initial version (andrei) */ #ifndef __shm_init_h #define __shm_init_h int shm_initialized(void); int init_shm(void); #endif /*__shm_init_h*/ /* vi: set ts=4 sw=4 tw=79:ai:cindent: */ kamailio-4.0.4/flags.c0000644000000000000000000001641312223032460013242 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * History: * -------- * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2006-02-02 named flags support (andrei) */ /*! * \file * \brief SIP-router core :: * \ingroup core * Module: \ref core */ #include #include "sr_module.h" #include "dprint.h" #include "parser/msg_parser.h" #include "flags.h" #include "error.h" #include "stdlib.h" #include "hashes.h" #include "clist.h" #include "mem/mem.h" /* Script flags */ static flag_t sflags = 0; int setflag( struct sip_msg* msg, flag_t flag ) { msg->flags |= 1 << flag; return 1; } int resetflag( struct sip_msg* msg, flag_t flag ) { msg->flags &= ~ (1 << flag); return 1; } int isflagset( struct sip_msg* msg, flag_t flag ) { return (msg->flags & (1< MAX_FLAG ) { LOG(L_ERR, "ERROR: message flag %d too high; MAX=%d\n", flag, MAX_FLAG ); return 0; } if (flag<0) { LOG(L_ERR, "ERROR: message flag (%d) must be in range %d..%d\n", flag, 0, MAX_FLAG ); return 0; } return 1; } int setsflagsval(flag_t val) { sflags = val; return 1; } int setsflag(flag_t flag) { sflags |= 1 << flag; return 1; } int resetsflag(flag_t flag) { sflags &= ~ (1 << flag); return 1; } int issflagset(flag_t flag) { return (sflags & (1<name.len==len) && (memcmp(fe->name.s, name, len)==0)){ /* found */ return fe; } } return 0; } /* returns flag entry or 0 on not found */ inline static struct flag_entry* get_flag_entry(char* name, int len) { int h; /* get hash */ h=get_hash1_raw(name, len) & (FLAGS_NAME_HASH_ENTRIES-1); return flag_search(&name2flags[h], name, len); } /* returns flag number, or -1 on error */ int get_flag_no(char* name, int len) { struct flag_entry* fe; fe=get_flag_entry(name, len); return (fe)?fe->no:-1; } /* resgiter a new flag name and associates it with pos * pos== -1 => any position will do * returns flag pos on success (>=0) * -1 flag is an alias for an already existing flag * -2 flag already registered * -3 mem. alloc. failure * -4 invalid pos * -5 no free flags */ int register_flag(char* name, int pos) { struct flag_entry* e; int len; unsigned int r; static unsigned int crt_flag=0; unsigned int last_flag; unsigned int h; len=strlen(name); h=get_hash1_raw(name, len) & (FLAGS_NAME_HASH_ENTRIES-1); /* check if the name already exists */ e=flag_search(&name2flags[h], name, len); if (e){ LOG(L_ERR, "ERROR: register_flag: flag %.*s already registered\n", len, name); return -2; } /* check if there is already another flag registered at pos */ if (pos!=-1){ if ((pos<0) || (pos>MAX_FLAG)){ LOG(L_ERR, "ERROR: register_flag: invalid flag %.*s " "position(%d)\n", len, name, pos); return -4; } if (registered_flags[pos]!=0){ LOG(L_WARN, "WARNING: register_flag: %.*s: flag %d already in " "use under another name\n", len, name, pos); /* continue */ } }else{ /* alloc an empty flag */ last_flag=crt_flag+(MAX_FLAG+1); for (; crt_flag!=last_flag; crt_flag++){ r=crt_flag%(MAX_FLAG+1); if (registered_flags[r]==0){ pos=r; break; } } if (pos==-1){ LOG(L_ERR, "ERROR: register_flag: could not register %.*s" " - too many flags\n", len, name); return -5; } } registered_flags[pos]++; e=pkg_malloc(sizeof(struct flag_entry)); if (e==0){ LOG(L_ERR, "ERROR: register_flag: memory allocation failure\n"); return -3; } e->name.s=name; e->name.len=len; e->no=pos; clist_insert(&name2flags[h], e, next, prev); return pos; } #ifdef _GET_AWAY /* wrapping functions for flag processing */ static int fixup_t_flag(void** param, int param_no) { unsigned int *code; char *c; int token; DBG("DEBUG: fixing flag: %s\n", (char *) (*param)); if (param_no!=1) { LOG(L_ERR, "ERROR: TM module: only parameter #1 for flags can be" " fixed\n"); return E_BUG; }; if ( !(code =pkg_malloc( sizeof( unsigned int) )) ) return E_OUT_OF_MEM; *code = 0; c = *param; while ( *c && (*c==' ' || *c=='\t')) c++; /* initial whitespaces */ token=1; if (strcasecmp(c, "white")==0) *code=FL_WHITE; else if (strcasecmp(c, "yellow")==0) *code=FL_YELLOW; else if (strcasecmp(c, "green")==0) *code=FL_GREEN; else if (strcasecmp(c, "red")==0) *code=FL_RED; else if (strcasecmp(c, "blue")==0) *code=FL_BLUE; else if (strcasecmp(c, "magenta")==0) *code=FL_MAGENTA; else if (strcasecmp(c, "brown")==0) *code=FL_BROWN; else if (strcasecmp(c, "black")==0) *code=FL_BLACK; else if (strcasecmp(c, "acc")==0) *code=FL_ACC; else { token=0; while ( *c && *c>='0' && *c<='9' ) { *code = *code*10+ *c-'0'; if (*code > (sizeof( flag_t ) * CHAR_BIT - 1 )) { LOG(L_ERR, "ERROR: TM module: too big flag number: %s; MAX=%d\n", (char *) (*param), sizeof( flag_t ) * CHAR_BIT - 1 ); goto error; } c++; } } while ( *c && (*c==' ' || *c=='\t')) c++; /* terminating whitespaces */ if ( *code == 0 ) { LOG(L_ERR, "ERROR: TM module: bad flag number: %s\n", (char *) (*param)); goto error; } if (*code < FL_MAX && token==0) { LOG(L_ERR, "ERROR: TM module: too high flag number: %s (%d)\n; lower number" " bellow %d reserved\n", (char *) (*param), *code, FL_MAX ); goto error; } /* free string */ pkg_free( *param ); /* fix now */ *param = code; return 0; error: pkg_free( code ); return E_CFG; } #endif kamailio-4.0.4/script_cb.h0000644000000000000000000000522112223032461014117 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2005-02-13 script callbacks devided into request and reply types (bogdan) * 2009-06-01 Added pre- and post-script callback support for all types * of route blocks. (Miklos) */ #ifndef _SCRIPT_CB_H_ #define _SCRIPT_CB_H_ #include "parser/msg_parser.h" typedef int (cb_function)(struct sip_msg *msg, unsigned int flags, void *param); #define PRE_SCRIPT_CB (1<<30) #define POST_SCRIPT_CB (1<<31) /* Pre- and post-script callback flags. Use these flags to register * for the callbacks, and to check the type of the callback from the * functions. * (Power of 2 so more callbacks can be registered at once.) */ enum script_cb_flag { REQUEST_CB=1, FAILURE_CB=2, ONREPLY_CB=4, BRANCH_CB=8, ONSEND_CB=16, ERROR_CB=32, LOCAL_CB=64, EVENT_CB=128 }; /* Callback types used for executing the callbacks. * Keep in sync with script_cb_flag!!! */ enum script_cb_type { REQUEST_CB_TYPE=1, FAILURE_CB_TYPE, ONREPLY_CB_TYPE, BRANCH_CB_TYPE, ONSEND_CB_TYPE, ERROR_CB_TYPE, LOCAL_CB_TYPE, EVENT_CB_TYPE }; struct script_cb{ cb_function *cbf; struct script_cb *next; void *param; }; /* Register pre- or post-script callbacks. * Returns -1 on error, 0 on success */ int register_script_cb( cb_function f, unsigned int flags, void *param ); int init_script_cb(void); void destroy_script_cb(void); /* Execute pre-script callbacks of a given type. * Returns 0 on error, 1 on success */ int exec_pre_script_cb( struct sip_msg *msg, enum script_cb_type type); /* Execute post-script callbacks of a given type. * Always returns 1, success. */ int exec_post_script_cb( struct sip_msg *msg, enum script_cb_type type); #endif kamailio-4.0.4/signals.c0000644000000000000000000000322212223032460013600 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Handle the signals * * History: * -------- * 2005-10-05 split from main.c (andrei) */ /*! * \file * \brief SIP-router core :: * \ingroup core * Module: \ref core */ #include "signals.h" #include "dprint.h" #include #include /* memset */ #ifdef USE_SIGACTION void (*set_sig_h(int sig, void (*handler) (int) ))(int) { struct sigaction act; struct sigaction old; memset(&act, 0, sizeof(act)); act.sa_handler=handler; /* sigemptyset(&act.sa_mask); act.sa_flags=0; */ /* sa_sigaction not set, we use sa_hanlder instead */ return (sigaction (sig, &act, &old)==-1)?SIG_ERR:old.sa_handler; } #endif kamailio-4.0.4/switch.h0000644000000000000000000000466312223032460013460 0ustar rootroot/* * $Id$ * * Copyright (C) 2009 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * /home/andrei/sr.git/switch.h */ /* * History: * -------- * 2009-02-02 initial version (andrei) */ #ifndef __switch_h #define __switch_h #include #include #include "route_struct.h" struct case_stms{ struct rval_expr* ct_rve; struct action* actions; struct case_stms* next; struct case_stms** append; int type; /**< type: MATCH_UNKOWN, MATCH_INT, MATCH_STR, MATCH_RE */ int re_flags; /**< used only for REs */ int is_default; union { int match_int; str match_str; regex_t* match_re; } label; /**< fixed case argument */ }; struct switch_cond_table{ int n; /**< size */ int* cond; /**< int labels array */ struct action** jump; /**< jump points array */ struct action* def; /**< default jump */ }; struct switch_jmp_table{ int first; /**< first int label in the jump table */ int last; /**< last int label in the jump table */ struct action** tbl; /**< jmp table [v-first] iff first<=v<=last */ struct switch_cond_table rest; /**< normal cond. table for the rest */ }; enum match_str_type { MATCH_UNKNOWN, MATCH_INT, MATCH_STR, MATCH_RE }; struct match_str{ enum match_str_type type;/**< string or RE */ int flags; /**< flags for re */ union{ str s; /* string */ regex_t* regex; /**< compiled regex */ }l; }; struct match_cond_table{ int n; /**< size */ struct match_str* match; /**< match array */ struct action** jump; /**< jump points array */ struct action* def; /**< default jmp */ }; int fix_switch(struct action* t); #endif /*__switch_h*/ /* vi: set ts=4 sw=4 tw=79:ai:cindent: */ kamailio-4.0.4/data_lump_rpl.c0000644000000000000000000001026012223032457014771 0ustar rootroot/* * $Id$ * * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * 2002-02-14 : created by bogdan * 2003-09-11 : lump_rpl type added - LUMP_RPL_BODY & LUMP_RPL_HDR (bogdan) * 2003-11-11 : build_lump_rpl merged into add_lump_rpl; types -> flags ; * flags LUMP_RPL_NODUP and LUMP_RPL_NOFREE added (bogdan) * 2006-10-16 add_lump_rpl2 added: same as the old add_lump_rpl, but * returns a lump_rpl**, making a specific lump removal much * more easy (andrei) */ /*! * \file * \brief SIP-router core :: * \ingroup core * Module: \ref core */ #include #include "dprint.h" #include "mem/mem.h" #include "data_lump_rpl.h" struct lump_rpl** add_lump_rpl2(struct sip_msg *msg, char *s, int len, int flags) { struct lump_rpl *lump = 0; struct lump_rpl *foo; struct lump_rpl** ret; /* some checking */ if ( (flags&(LUMP_RPL_HDR|LUMP_RPL_BODY))==(LUMP_RPL_HDR|LUMP_RPL_BODY) || (flags&(LUMP_RPL_HDR|LUMP_RPL_BODY))==0 || (flags&LUMP_RPL_SHMEM) ) { LOG(L_ERR,"ERROR:add_lump_rpl: bad flags combination (%d)!\n",flags); goto error; } if (len<=0 || s==0) { LOG(L_ERR,"ERROR:add_lump_rpl: I won't add an empty lump!\n"); goto error; } /* build the lump */ lump = (struct lump_rpl*) pkg_malloc ( sizeof(struct lump_rpl) + ((flags&LUMP_RPL_NODUP)?0:len) ); if (!lump) { LOG(L_ERR,"ERROR:add_lump_rpl : no free pkg memory !\n"); goto error; } if (flags&LUMP_RPL_NODUP) { lump->text.s = s; } else { lump->text.s = ((char*)lump)+sizeof(struct lump_rpl); memcpy( lump->text.s, s, len); } lump->text.len = len; lump->flags = flags; lump->next = 0; /* add the lump to the msg */ if (!msg->reply_lump) { msg->reply_lump = lump; ret=&msg->reply_lump; }else{ if (!(flags&LUMP_RPL_BODY)) for(foo=msg->reply_lump;foo->next;foo=foo->next); else for(foo=msg->reply_lump; ;foo=foo->next) { if (foo->flags&LUMP_RPL_BODY) { LOG(L_ERR,"ERROR:add_lump_rpl: LUMP_RPL_BODY " "already added!\n"); pkg_free(lump); goto error; } if (foo->next==0) break; } foo->next = lump; ret= &(foo->next); } return ret; error: return 0; } void free_lump_rpl(struct lump_rpl* lump) { if (lump) { if (!((lump->flags)&LUMP_RPL_NOFREE) && ((lump->flags)&LUMP_RPL_NODUP) && lump->text.s) pkg_free(lump->text.s); pkg_free(lump); } } void unlink_lump_rpl(struct sip_msg * msg, struct lump_rpl* lump) { struct lump_rpl *foo,*prev; /* look for the lump to be unlink */ foo = msg->reply_lump; prev = 0; while( foo && foo!=lump ) { prev = foo; foo = foo->next; } /* if the lump was found into the list -> unlink it */ if (foo) { if (prev) prev->next = foo->next; else msg->reply_lump = foo->next; } } void del_nonshm_lump_rpl(struct lump_rpl** list) { struct lump_rpl* it, *tmp; struct lump_rpl** pred; it = *list; pred = list; while(it) { if (!(it->flags & LUMP_RPL_SHMEM)) { tmp = it; *pred = it->next; it = it->next; free_lump_rpl(tmp); continue; } pred = &it->next; it = it->next; } } kamailio-4.0.4/sctp_ev.h0000644000000000000000000000660012223032461013614 0ustar rootroot/* * $Id$ * * Copyright (C) 2009 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * sctp_ev.h - sctp events */ /* * History: * -------- * 2009-04-28 initial version (andrei) */ #ifndef __sctp_ev_h #define __sctp_ev_h #include #include #ifndef USE_SCTP_EV #define SCTP_EV_ASSOC_CHANGE(lip, lport, src, reason, state) #define SCTP_EV_PEER_ADDR_CHANGE(lip, lport, src, reason, state, addr_su) #define SCTP_EV_REMOTE_ERROR(lip, lport, src, err) #define SCTP_EV_SEND_FAILED(lip, lport, src, err) #define SCTP_EV_SHUTDOWN_EVENT(lip, lport, src) #define SCTP_EV_SENDER_DRY_EVENT(lip, lport, src) #else /* USE_SCTP_EV */ #include "ip_addr.h" /** an association has either been opened or closed. * called for each SCTP_ASSOC_CHANGE event. * * @param err - if 0 it should be ignored (no corresp. libc error), if non-0 * it will contain the errno. * @param lip - pointer to an ip_addr containing the local ip * or 0 if dynamic (WARNING can be 0). * @param lport - pointer to an ip_addr containing the local port or 0 * if unknown/dynamic. * @param src - pointer to a sockaddr_union containing the src. * @param proto - protocol used */ #define SCTP_EV_ASSOC_CHANGE(lip, lport, src, reason, state) \ DBG("SCTP_ASSOC_CHANGE from %s on %s:%d: %s\n", \ su2a(src, sizeof(*(src))), ip_addr2a(lip), lport, reason) /** an address part of an assoc. changed state. * called for the SCTP_PEER_ADDR_CHANGE event.*/ #define SCTP_EV_PEER_ADDR_CHANGE(lip, lport, src, reason, state, addr_su) \ DBG("SCTP_PEER_ADDR_CHANGE from %s on %s:%d: %s\n", \ su2a(src, sizeof(*(src))), ip_addr2a(lip), lport, reason) /** remote operation error from the peer. * called for the SCTP_REMOTE_ERROR event.*/ #define SCTP_EV_REMOTE_ERROR(lip, lport, src, err) \ DBG("SCTP_REMOTE_ERROR from %s on %s:%d: %d\n", \ su2a(src, sizeof(*(src))), ip_addr2a(lip), lport, err) /** send failed. * called for the SCTP_SEND_FAILED event.*/ #define SCTP_EV_SEND_FAILED(lip, lport, src, err) \ DBG("SCTP_SEND_FAILED from %s on %s:%d: %d\n", \ su2a(src, sizeof(*(src))), ip_addr2a(lip), lport, err) /** the peer has sent a shutdown. * called for the SCTP_SHUTDOWN_EVENT event.*/ #define SCTP_EV_SHUTDOWN_EVENT(lip, lport, src) \ DBG("SCTP_SHUTDOWN_EVENT from %s on %s:%d\n", \ su2a(src, sizeof(*(src))), ip_addr2a(lip), lport) /** kernel has finished sending all the queued data. * called for the SCTP_SENDER_DRY_EVENT event.*/ #define SCTP_EV_SENDER_DRY_EVENT(lip, lport, src) \ DBG("SCTP_SENDER_DRY_EVENT from %s on %s:%d\n", \ su2a(src, sizeof(*(src))), ip_addr2a(lip), lport) #endif /* USE_SCTP_EV */ #endif /*__sctp_ev_h*/ /* vi: set ts=4 sw=4 tw=79:ai:cindent: */ kamailio-4.0.4/comp_defs.h0000644000000000000000000000216012223032457014112 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*! \brief compatibility defs for emergency roll-back if things do not work ; * if that happens: * - a) define PRESERVE_ZT (affects most of parser) * - b) define DONT_REMOVE_ZT (affects first line) * - c) define SCRATCH * - d) undefine REMOVE_ALL_ZT (affects via) */ #ifndef _COMP_DEFS_H #define _COMP_DEFS_H #endif kamailio-4.0.4/Makefile.rules0000644000000000000000000002126312223032457014600 0ustar rootroot# # $Id$ # # # common Makefile rules, included by main Makefile & the modules # # # Uses: NAME, ALLDEP, CC, CFLAGS, C_DEFS, DEFS, C_INCLUDES, INCLUDES, LIBS, # MKDEP, auto_gen, auto_gen_others, auto_gen_keep, # depends, objs, extra_objs, static_modules, static_modules_path, # LD_RPATH # (all this must be defined previously!, see Makefile.defs & Makefile) # Optional: SER_LIBS - list of ser libraries that will be automatically # built if necessary. Format: path/shortname, where shortname is the # name passed to -l (e.g. for libprint.so the shortname is print) # # History: # -------- # 2007-03-29 set rpath differently for make install # automatically build listed SER_LIBS if needed (andrei) # 2008-06-23 automatically rebuild if make time defines or includes # changed (via makecfg.lst) # 2009-03-10 support for C_DEFS and C_INCLUDES (DEFS and INCLUDES are now # used only for "temporary" defines/includes inside modules or # libs, C_DEFS and C_INCLUDES are used for the common stuff) # (andrei) # 2010-03-09 generate dependencies when compiling .o instead of on # include .d and fix build errors when a .h is moved # support for using MKDEP="makedepend-f-" (andrei) # 2010-03-10 support for on the-fly dependency generation (while compiling, # see CC_MKDEP_OPTS) (andrei) # check if the saved cfg corresponds with the current one # (if not rebuild everything) ifeq (,$(filter $(nodep_targets),$(MAKECMDGOALS))) -include makecfg.lst # if trying to build a lib automatically and the lib is already compiled, # don't rebuild it if the only differences in DEFS or INCLUDES are covered # by LIB_NOREBUILD_DEFS/LIB_NOREBUILD_INCLUDES LIB_NOREBUILD_DEFS= # don't rebuild if the differences are covered by NOREBUILD_DEFS or # NOREBUILD_INCLUDES ifneq ($(strip $(filter-out $(NOREBUILD_DEFS),\ $(C_DEFS) $(DEFS))),$(strip $(CFG_DEFS))) #$(warning different defs: <$(strip $(C_DEFS) $(DEFS))> != ) #$(warning : <$(strip $(CFG_DEFS))>) $(shell rm -f makecfg.lst) endif ifneq ($(strip $(filter-out $(NOREBUILD_INCLUDES),\ $(C_INCLUDES) $(INCLUDES))),$(strip $(CFG_INCLUDES))) $(shell rm -f makecfg.lst) endif endif ALLDEP+=makecfg.lst # returns current type: "" core/unknown, "M" module, "L" libray, "U" util crt_type=$(if $(MOD_NAME),M,$(if $(LIB_NAME),L,$(if $(UTIL_NAME),U))) cmd_CC=$(CC) $(CFLAGS) $(C_INCLUDES) $(INCLUDES) $(C_DEFS) $(DEFS) -c $< -o $@ cmd_LD=$(LD) $(LDFLAGS) $(objs) $(extra_objs) $(ALL_LIBS) $(SER_RPATH) \ -o $(NAME) ifeq (,$(CC_MKDEP_OPTS)) # if CCC_MKDEP_OPTS is empty => CC cannot generate dependencies on the fly cmd_MKDEP=$(MKDEP) $(filter -D% -I%,$(CFLAGS)) $(C_INCLUDES) $(INCLUDES) \ $(C_DEFS) $(DEFS) $< \ | sed -e 's/\#.*//' -e '/:[ ]*$$/d' -e '/^[ ]*$$/d' \ -e 's|.*:|$@: $$(wildcard |' -e 's/\([^\\]\)$$/\1)/'> $*.d else # deps can be generated on the fly by cmd_CC cmd_CC+=$(CC_MKDEP_OPTS) # no MKDEP command any more cmd_MKDEP= endif # CC_MKDEP_OPTS # what will be displayed if quiet==silent silent_cmd_CC=CC ($(CC)) [$(strip $(crt_type) $(NAME))] $@ silent_cmd_LD=LD ($(LD)) [$(strip $(crt_type) $(NAME))] $@ ifneq (,$(filter 0 no off verbose noisy, $(Q) $(QUIET))) override Q:= quiet=verbose #shell optional print oecho=echo $(1) else quiet=silent Q=1 MAKE+= --no-print-directory #shell optional print oecho= endif module_make= if [ -n "$(1)" -a -r "$(1)/Makefile" ]; then \ $(call oecho, "" ;) \ $(call oecho, "" ;) \ if $$(MAKE) -C $(1) $(2) || [ ${err_fail} != 1 ] ; then \ :; \ else \ exit 1; \ fi ; \ fi ; quote:= " escall= $(subst $$,\$$,$(subst $(quote),\$(quote),$1)) exec_cmd= $(if $($(quiet)_cmd_$(1)),\ @echo "$(call escall,$($(quiet)_cmd_$(1)))" ;) $(cmd_$(1)) #implicit rules %.o:%.c $(ALLDEP) $(call exec_cmd,CC) @$(call cmd_MKDEP) # use RPATH and SER_LIBS if needed (make install and the module depends # on some ser libs) ifneq ($(SER_LIBS),) # abspath & realpath don't work on make <= 3.80 SER_LIBS_DIRS:=$(dir $(SER_LIBS)) ifneq (,$(filter install install% %install, $(MAKECMDGOALS))) lib_compile_for_install=yes expected_lib_ipath=$(lib_target) else lib_compile_for_install=$(compile_for_install) # function: expected_lib_ipath ser_lib_dir expected_lib_ipath=$(1) endif ifneq ($(LD_RPATH),) ifneq (,$(filter install install% %install, $(MAKECMDGOALS))) SER_RPATH_LST:=$(lib_target) else # realpath is not supported in make 3.80 or older ifeq (,$(filter-out 3.80 3.80.%,$(MAKE_VERSION))) fullpath=$(shell cd $(1); pwd) else fullpath=$(realpath $(1)) endif SER_RPATH_LST:=$(call fullpath,$(dir $(SER_LIBS))) endif ifneq ($(strip $(SER_RPATH_LST)),) SER_RPATH:=$(addprefix $(LD_RPATH),$(SER_RPATH_LST)) endif endif ifeq ($(OS), darwin) SER_IPATH_LST:=$(addsuffix /libiname.lst,$(SER_LIBS_DIRS)) #$(warning $(NAME) DARWIN, SER_LIBS=$(SER_LIBS), $(SER_LIBS_DIRS), ipath_lst=$(SER_IPATH_LST)) endif endif ALL_LIBS=$(LIBS) ifeq (,$(filter clean %clean clean% proper %proper proper%, $(MAKECMDGOALS))) ifneq ($(SER_LIBS),) -include librpath.lst ifneq ($(SER_RPATH_LST), $(LIB_RPATH_LST)) $(shell rm -f librpath.lst) endif endif SER_LIBS_DEPS:= \ $(foreach l, $(SER_LIBS), $(dir $l)$(LIB_PREFIX)$(notdir $l)$(LIB_SUFFIX)) ALL_LIBS+=$(foreach l, $(SER_LIBS), -L$(dir $l) -l$(notdir $l)) $(NAME): librpath.lst $(SER_LIBS_DEPS) $(SER_LIBS_DEPS): FORCE @$(MAKE) -wC $(dir $@) compile_for_install=$(lib_compile_for_install) \ NOREBUILD_DEFS="$(NOREBUILD_DEFS) $(LIB_NOREBUILD_DEFS)" \ NOREBUILD_INCLUDES="$(NOREBUILD_INCLUDES) $(LIB_NOREBUILD_INCLUDES)" .PHONY: FORCE FORCE: ifneq ($(SER_IPATH_LST),) $(NAME): $(SER_IPATH_LST) $(SER_IPATH_LST): FORCE @if grep \ "COMPILED_INAME:=$(call expected_lib_ipath,$(shell cd $(@D); pwd))" \ $(@) 1>/dev/null 2>/dev/null ; \ then :; \ else \ $(call oecho,"re-building $(@D)" ;) \ $(MAKE) -wC $(@D) compile_for_install=$(lib_compile_for_install) ; \ fi .PHONY: FORCE-BUILD-LIBS FORCE-BUILD-LIBS: @for r in $(SER_LIBS_DIRS) ; do \ $(call oecho,building lib $$r ;) \ $(MAKE) -wC $$r compile_for_install=$(lib_compile_for_install) ; \ done endif endif # normal rules $(NAME): $(objs) $(ALLDEP) $(call exec_cmd,LD) librpath.lst: $(ALLDEP) @echo LIB_RPATH_LST:=$(SER_RPATH_LST) >librpath.lst makecfg.lst: @echo "CFG_DEFS:=$(call escall,$(strip \ $(filter-out $(NOREBUILD_DEFS), $(C_DEFS) $(DEFS))))" >>$@ @echo "CFG_INCLUDES:=$(call escall,$(strip \ $(filter-out $(NOREBUILD_INCLUDES),\ $(C_INCLUDES) $(INCLUDES))))" >>$@ .PHONY: all all: $(NAME) every-module .PHONY: static static: $(objs) # clean only the current directory (no modules or utils) # (it's usefull to have it separated from clean for speeding up make proper) .PHONY: local-clean local-clean: -@rm -f $(objs) $(NAME) $(objs:.o=.il) librpath.lst 2>/dev/null .PHONY: clean clean: local-clean .PHONY: clean-modules clean-modules: -@for r in $(cmodules) $(static_modules_path) "" ; do \ if [ -d "$$r" ]; then \ $(call oecho,"module $$r" ;) \ $(MAKE) -C "$$r" clean ; \ [ -r "$$r"/doc/Makefile ] && $(MAKE) -C "$$r"/doc clean ; \ fi ; \ done # make proper for the local directory .PHONY: proper .PHONY: distclean .PHONY: realclean .PHONY: maintainer-clean proper distclean realclean maintainer-clean: local-clean -@rm -f $(depends) $(auto_gen) $(auto_gen_others) $(auto_gen_keep) \ makecfg.lst 2>/dev/null maintainer-clean: clean-tmp .PHONY: proper-modules .PHONY: distclean-modules .PHONY: realclean-modules .PHONY: maintainer-clean-modules proper-modules realclean-modules distclean-modules maintainer-clean-modules: \ clean_target=$(patsubst %-modules,%,$@) proper-modules realclean-modules distclean-modules maintainer-clean-modules: -@for r in $(cmodules) "" ; do \ if [ -d "$$r" ]; then \ $(MAKE) -C "$$r" $(clean_target); \ [ -r "$$r"/doc/Makefile ] && $(MAKE) -C "$$r"/doc $(clean_target);\ fi ; \ done .PHONY: clean-tmp clean-tmp: -@rm -f TAGS tags *.dbg .*.swp .PHONY: doxygen doxygen: -@mkdir -p $(doxygen_dir) -@echo "Create Doxygen documentation" # disable call graphes, because of the DOT dependencies (cat ./$(COREPATH)/doc/doxygen/ser.doxygen; \ echo "HAVE_DOT=no" ;\ echo "PROJECT_NAME=SIP-ROUTER" ;\ echo "PROJECT_NUMBER=$(NAME)-$(RELEASE)" )| doxygen - -@echo "Doxygen documentation created" .PHONY: clean_doxygen clean_doxygen: -@rm -rf $(doxygen_dir)/{xml,man,rtf,latex,html} -@rmdir --ignore-fail-on-non-empty -p $(doxygen_dir) || true .PHONY: TAGS TAGS: $(MKTAGS) --exclude="obsolete/*" -R . .PHONY: TAGS-ALL TAGS-ALL: $(MKTAGS) -R . ifeq (,$(MAKECMDGOALS)) -include $(depends) else ifeq (,$(strip $(nodep_targets))) include $(COREPATH)/Makefile.targets endif ifneq (,$(filter-out $(nodep_targets),$(MAKECMDGOALS))) -include $(depends) endif endif # ifeq (,$(MAKECMDGOALS)) kamailio-4.0.4/tcp_read.h0000644000000000000000000000254512223032460013735 0ustar rootroot/* * $Id$ * * Copyright (C) 2010 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** tcp internal read functions. * @file tcp_read.h * @ingroup: core * Module: @ref core */ /* * History: * -------- * 2010-05-18 initial version (andrei) */ #ifndef __tcp_read_h #define __tcp_read_h #include "tcp_conn.h" #define RD_CONN_SHORT_READ 1 #define RD_CONN_EOF 2 #define RD_CONN_REPEAT_READ 4 /* read should be repeated (more data) (used so far only by tls) */ #define RD_CONN_FORCE_EOF 65536 int tcp_read_data(int fd, struct tcp_connection *c, char* buf, int b_size, int* flags); #endif /*__tcp_read_h*/ /* vi: set ts=4 sw=4 tw=79:ai:cindent: */ kamailio-4.0.4/examples/0000755000000000000000000000000012223032461013614 5ustar rootrootkamailio-4.0.4/examples/uas.cfg0000644000000000000000000000216312223032460015066 0ustar rootroot# # $Id$ # # this example shows usage of ser as user agent # server which does some functionality (in this # example, 'log' is used to print a notification # on a new transaction) and behaves statefuly # (e.g., it retransmits replies on request # retransmissions) # ------------------ module loading ---------------------------------- loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" # ------------------------- request routing logic ------------------- # main routing logic route{ # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { log("REGISTER"); sl_send_reply("200", "ok"); break; }; # create transaction state; abort if error occured if ( !t_newtran()) { sl_reply_error(); break; }; # the following log will be only printed on receipt of # a new message; retranmissions are absorbed by t_newtran log(1, "New Transaction Arrived\n"); # do what you want to do as a sever... if (uri=~"a@") { if (!t_reply("409", "Bizzar Error")) { sl_reply_error(); }; } else { if (!t_reply("699", "I don't want to chat with you")) { sl_reply_error(); }; }; } kamailio-4.0.4/examples/snmp.cfg0000644000000000000000000000106312223032460015251 0ustar rootroot# # $Id $ ## # Simple configuration file to test SNMP module. Read # modules/snmp/doc/README,SNMP-HOWTO,HOWTO if you want # to know more ## # where the stats will be dumped upon receipt of SIG_USR1 # File is opened/closed when signal is received, and stats # are appended to it statistics="/tmp/statsfile.ser" # should load snmp first, so that other modules can register # their handlers loadmodule "/usr/lib/ser/modules/snmp.so" loadmodule "/usr/lib/ser/modules/print.so" loadmodule "/usr/lib/ser/modules/sl.so" route { sl_send_reply("600", "Busy, busy"); } kamailio-4.0.4/examples/exec_dist.cfg0000644000000000000000000000201012223032460016234 0ustar rootroot# # $Id$ # # Example for distributing load accross multiple devices # fork=no log_stderror=yes listen=192.168.2.16 # ----------- global configuration parameters ------------------------ loadmodule "modules/exec/exec.so" loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" # ----------------- setting module-specific parameters --------------- route{ # uri for my domain ? if (!(uri==myself)) { sl_send_reply("500", "example serves only my domain"); break; }; if (method=="REGISTER") { sl_send_reply("200", "silly example -- pretend registrar"); break; }; exec_dset(' # calculate a characteristic value for this call CHAR=`echo $SIP_HF_CALLID | sum | awk " { print \\\$1 } "` # normalize the value NC=`expr $CHAR % 100` # distribute now # gateway 192.168.2.35 @ 10 % if [ "$NC" -lt 10 ] ; then printf "sip:00$SIP_USER@192.168.2.35" exit fi # anything else at 90 % printf "sip:11$SIP_USER@192.168.2.34" exit # SER adds command-line parameters -- trash them here echo > dev/null '); t_relay(); } kamailio-4.0.4/examples/websocket.cfg0000644000000000000000000002706412223032461016274 0ustar rootroot#!KAMAILIO # # Simple/sample kamailio.cfg for running a proxy/registrar with TLS and # WebSockets support. #!substdef "!DBURL!sqlite:///etc/kamailio/db.sqlite!g" #!substdef "!MY_IP_ADDR!a.b.c.d!g" #!substdef "!MY_DOMAIN!example.com!g" #!substdef "!MY_WS_PORT!80!g" #!substdef "!MY_WSS_PORT!443!g" #!substdef "!MY_MSRP_PORT!9000!g" #!substdef "!MY_WS_ADDR!tcp:MY_IP_ADDR:MY_WS_PORT!g" #!substdef "!MY_WSS_ADDR!tls:MY_IP_ADDR:MY_WSS_PORT!g" #!substdef "!MY_MSRP_ADDR!tls:MY_IP_ADDR:MY_MSRP_PORT!g" #!substdef "!MSRP_MIN_EXPIRES!1800!g" #!substdef "!MSRP_MAX_EXPIRES!3600!g" ##!define LOCAL_TEST_RUN #!define WITH_TLS #!define WITH_WEBSOCKETS #!define WITH_MSRP ####### Global Parameters ######### fork=yes children=4 #!ifdef WITH_TLS enable_tls=1 #!endif listen=MY_IP_ADDR #!ifdef WITH_WEBSOCKETS listen=MY_WS_ADDR #!ifdef WITH_TLS listen=MY_WSS_ADDR #!endif #!endif #!ifdef WITH_MSRP listen=MY_MSRP_ADDR #!endif tcp_connection_lifetime=3604 tcp_accept_no_cl=yes tcp_rd_buf_size=16384 syn_branch=0 #!ifdef LOCAL_TEST_RUN debug=2 mpath="modules_k:modules" #!else debug=0 mpath="/usr/lib64/kamailio/modules_k/:/usr/lib64/kamailio/modules/" #!endif loadmodule "db_sqlite.so" loadmodule "tm.so" loadmodule "sl.so" loadmodule "rr.so" loadmodule "pv.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "siputils.so" loadmodule "xlog.so" loadmodule "sanity.so" loadmodule "ctl.so" loadmodule "auth.so" loadmodule "auth_db.so" loadmodule "kex.so" loadmodule "mi_rpc.so" loadmodule "corex.so" #!ifdef WITH_TLS loadmodule "tls.so" #!endif #!ifdef WITH_MSRP loadmodule "msrp.so" loadmodule "htable.so" loadmodule "cfgutils.so" #!endif #!ifdef WITH_WEBSOCKETS loadmodule "xhttp.so" loadmodule "websocket.so" loadmodule "nathelper.so" #!endif # ----------------- setting module-specific parameters --------------- # ----- tm params ----- # auto-discard branches from previous serial forking leg modparam("tm", "failure_reply_mode", 3) # default retransmission timeout: 30sec modparam("tm", "fr_timer", 30000) # default invite retransmission timeout after 1xx: 120sec modparam("tm", "fr_inv_timer", 120000) # ----- rr params ----- # add value to ;lr param to cope with most of the UAs modparam("rr", "enable_full_lr", 1) # do not append from tag to the RR (no need for this script) modparam("rr", "append_fromtag", 0) # ----- registrar params ----- modparam("registrar", "method_filtering", 1) modparam("registrar", "max_expires", 3600) modparam("registrar", "gruu_enabled", 0) # ----- usrloc params ----- modparam("usrloc", "db_url", "DBURL") modparam("usrloc", "db_mode", 0) # ----- auth params ----- modparam("auth", "nonce_count", 1) modparam("auth", "qop", "auth") # ----- auth_db params ----- modparam("auth_db", "db_url", "DBURL") modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") modparam("auth_db", "load_credentials", "id") # ----- corex params ----- modparam("corex", "alias_subdomains", "MY_DOMAIN") #!ifdef WITH_TLS # ----- tls params ----- modparam("tls", "tls_method", "SSLv23") modparam("tls", "certificate", "/etc/pki/CA/ser1_cert.pem") modparam("tls", "private_key", "/etc/pki/CA/privkey.pem") modparam("tls", "ca_list", "/etc/pki/CA/calist.pem") #!endif #!ifdef WITH_WEBSOCKETS # ----- nathelper params ----- modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)") # Note: leaving NAT pings turned off here as nathelper is _only_ being used for # WebSocket connections. NAT pings are not needed as WebSockets have # their own keep-alives. #!endif #!ifdef WITH_MSRP # ----- htable params ----- modparam("htable", "htable", "msrp=>size=8;autoexpire=MSRP_MAX_EXPIRES;") #!endif ####### Routing Logic ######## # Main SIP request routing logic # - processing of any incoming SIP request starts with this route # - note: this is the same as route { ... } request_route { if ((($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT) && !(proto == WS || proto == WSS)) || $Rp == MY_MSRP_PORT) { xlog("L_WARN", "SIP request received on $Rp\n"); sl_send_reply("403", "Forbidden"); exit; } # per request initial checks route(REQINIT); #!ifdef WITH_WEBSOCKETS if (nat_uac_test(64)) { # Do NAT traversal stuff for requests from a WebSocket # connection - even if it is not behind a NAT! # This won't be needed in the future if Kamailio and the # WebSocket client support Outbound and Path. force_rport(); if (is_method("REGISTER")) { fix_nated_register(); } else { if (!add_contact_alias()) { xlog("L_ERR", "Error aliasing contact <$ct>\n"); sl_send_reply("400", "Bad Request"); exit; } } } #!endif # handle requests within SIP dialogs route(WITHINDLG); ### only initial requests (no To tag) # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) { t_relay(); } exit; } t_check_trans(); # authentication route(AUTH); # record routing for dialog forming requests (in case they are routed) # - remove preloaded route headers remove_hf("Route"); if (is_method("INVITE")) { record_route(); } # handle registrations route(REGISTRAR); if ($rU==$null) { # request with no Username in RURI sl_send_reply("484", "Address Incomplete"); exit; } # user location service route(LOCATION); route(RELAY); } route[RELAY] { if (!t_relay()) { sl_reply_error(); } exit; } # Per SIP request initial checks route[REQINIT] { if (!mf_process_maxfwd_header("10")) { sl_send_reply("483", "Too Many Hops"); exit; } if (!sanity_check("1511", "7")) { xlog("Malformed SIP message from $si:$sp\n"); exit; } if (uri == myself && is_method("OPTIONS") && !(uri=~"sip:.*[@]+.*")) { options_reply(); exit; } } # Handle requests within SIP dialogs route[WITHINDLG] { if (has_totag()) { # sequential request withing a dialog should # take the path determined by record-routing if (loose_route()) { #!ifdef WITH_WEBSOCKETS if ($du == "") { if (!handle_ruri_alias()) { xlog("L_ERR", "Bad alias <$ru>\n"); sl_send_reply("400", "Bad Request"); exit; } } #!endif route(RELAY); } else { if ( is_method("ACK") ) { if ( t_check_trans() ) { # no loose-route, but stateful ACK; # must be an ACK after a 487 # or e.g. 404 from upstream server t_relay(); exit; } else { # ACK without matching transaction... # ignore and discard exit; } } sl_send_reply("404", "Not Found"); } exit; } } # Handle SIP registrations route[REGISTRAR] { if (is_method("REGISTER")) { if (!save("location")) { sl_reply_error(); } exit; } } # USER location service route[LOCATION] { if (!is_subscriber("$ru", "subscriber", "1")) { t_newtran(); send_reply("404", "Not Found"); exit; } if (!lookup("location")) { $var(rc) = $rc; t_newtran(); switch ($var(rc)) { case -1: send_reply("480", "Temporarily Unavailable"); exit; case -2: send_reply("405", "Method Not Allowed"); exit; case -3: send_reply("500", "Server Internal Error"); exit; } } } # Authentication route route[AUTH] { if (is_method("REGISTER") || from_uri==myself) { # authenticate requests if (!auth_check("$fd", "subscriber", "1")) { auth_challenge("$fd", "0"); exit; } # user authenticated - remove auth header if(!is_method("REGISTER")) { consume_credentials(); } } # if caller is not local subscriber, then check if it calls # a local destination, otherwise deny, not an open relay here if (from_uri!=myself && uri!=myself) { sl_send_reply("403", "Forbidden"); exit; } } #!ifdef WITH_WEBSOCKETS onreply_route { if ((($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT) && !(proto == WS || proto == WSS)) || $Rp == MY_MSRP_PORT) { xlog("L_WARN", "SIP response received on $Rp\n"); drop; exit; } if (nat_uac_test(64)) { # Do NAT traversal stuff for replies to a WebSocket connection # - even if it is not behind a NAT! # This won't be needed in the future if Kamailio and the # WebSocket client support Outbound and Path. add_contact_alias(); } } event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($Rp != MY_WS_PORT #!ifdef WITH_TLS && $Rp != MY_WSS_PORT #!endif ) { xlog("L_WARN", "HTTP request received on $Rp\n"); xhttp_reply("403", "Forbidden", "", ""); exit; } xlog("L_DBG", "HTTP Request Received\n"); if ($hdr(Upgrade)=~"websocket" && $hdr(Connection)=~"Upgrade" && $rm=~"GET") { # Validate Host - make sure the client is using the correct # alias for WebSockets if ($hdr(Host) == $null || !is_myself("sip:" + $hdr(Host))) { xlog("L_WARN", "Bad host $hdr(Host)\n"); xhttp_reply("403", "Forbidden", "", ""); exit; } # Optional... validate Origin - make sure the client is from an # authorised website. For example, # # if ($hdr(Origin) != "http://communicator.MY_DOMAIN" # && $hdr(Origin) != "https://communicator.MY_DOMAIN") { # xlog("L_WARN", "Unauthorised client $hdr(Origin)\n"); # xhttp_reply("403", "Forbidden", "", ""); # exit; # } # Optional... perform HTTP authentication # ws_handle_handshake() exits (no further configuration file # processing of the request) when complete. if (ws_handle_handshake()) { # Optional... cache some information about the # successful connection exit; } } xhttp_reply("404", "Not Found", "", ""); } event_route[websocket:closed] { xlog("L_INFO", "WebSocket connection from $si:$sp has closed\n"); } #!endif #!ifdef WITH_MSRP event_route[msrp:frame-in] { msrp_reply_flags("1"); if ((($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT) && !(proto == WS || proto == WSS)) && $Rp != MY_MSRP_PORT) { xlog("L_WARN", "MSRP request received on $Rp\n"); msrp_reply("403", "Action-not-allowed"); exit; } if (msrp_is_reply()) { msrp_relay(); } else if($msrp(method)=="AUTH") { if($msrp(nexthops)>0) { msrp_relay(); exit; } if (!www_authenticate("MY_DOMAIN", "subscriber", "$msrp(method)")) { if (auth_get_www_authenticate("MY_DOMAIN", "1", "$var(wauth)")) { msrp_reply("401", "Unauthorized", "$var(wauth)"); } else { msrp_reply("500", "Server Error"); } exit; } if ($hdr(Expires) != $null) { $var(expires) = (int) $hdr(Expires); if ($var(expires) < MSRP_MIN_EXPIRES) { msrp_reply("423", "Interval Out-of-Bounds", "Min-Expires: MSRP_MIN_EXPIRES\r\n"); exit; } else if ($var(expires) > MSRP_MAX_EXPIRES) { msrp_reply("423", "Interval Out-of-Bounds", "Max-Expires: MSRP_MAX_EXPIRES\r\n"); exit; } } else { $var(expires) = MSRP_MAX_EXPIRES; } $var(cnt) = $var(cnt) + 1; pv_printf("$var(sessid)", "s.$(pp).$(var(cnt)).$(RANDOM)"); $sht(msrp=>$var(sessid)::srcaddr) = $msrp(srcaddr); $sht(msrp=>$var(sessid)::srcsock) = $msrp(srcsock); $shtex(msrp=>$var(sessid)) = $var(expires) + 5; # - Use-Path: the MSRP address for server + session id $var(hdrs) = "Use-Path: msrps://MY_IP_ADDR:MY_MSRP_PORT/" + $var(sessid) + ";tcp\r\n" + "Expires: " + $var(expires) + "\r\n"; msrp_reply("200", "OK", "$var(hdrs)"); } else if ($msrp(method)=="SEND" || $msrp(method)=="REPORT") { if ($msrp(nexthops)>1) { if ($msrp(method)!="REPORT") { msrp_reply("200", "OK"); } msrp_relay(); exit; } $var(sessid) = $msrp(sessid); if ($sht(msrp=>$var(sessid)::srcaddr) == $null) { # one more hop, but we don't have address in htable msrp_reply("481", "Session-does-not-exist"); exit; } else if ($msrp(method)!="REPORT") { msrp_reply("200", "OK"); } msrp_relay_flags("1"); msrp_set_dst("$sht(msrp=>$var(sessid)::srcaddr)", "$sht(msrp=>$var(sessid)::srcsock)"); msrp_relay(); } else { msrp_reply("501", "Request-method-not-understood"); } } #!endif kamailio-4.0.4/examples/redirect.cfg0000644000000000000000000000123512223032460016076 0ustar rootroot# # $Id$ # # this example shows use of ser as stateless redirect server # # ------------------ module loading ---------------------------------- loadmodule "modules/sl/sl.so" # ------------------------- request routing logic ------------------- # main routing logic route{ # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { log("REGISTER"); sl_send_reply("200", "ok"); break; }; # rewrite current URI, which is always part of destination ser rewriteuri("sip:parallel@iptel.org:9"); # append one more URI to the destination ser append_branch("sip:redirect@iptel.org:9"); # redirect now sl_send_reply("300", "Redirect"); } kamailio-4.0.4/examples/kamailio/0000755000000000000000000000000012223032460015401 5ustar rootrootkamailio-4.0.4/examples/kamailio/nathelper.cfg0000644000000000000000000001444312223032460020052 0ustar rootroot# # $Id$ # # simple quick-start config script including nathelper support # This default script includes nathelper support. To make it work # you will also have to install Maxim's RTP proxy. The proxy is enforced # if one of the parties is behind a NAT. # # If you have an endpoing in the public internet which is known to # support symmetric RTP (Cisco PSTN gateway or voicemail, for example), # then you don't have to force RTP proxy. If you don't want to enforce # RTP proxy for some destinations than simply use t_relay() instead of # route(1) # # Sections marked with !! Nathelper contain modifications for nathelper # # NOTE !! This config is EXPERIMENTAL ! # # ----------- global configuration parameters ------------------------ debug=3 # debug level (cmd line: -dddddddddd) fork=yes log_stderror=no # (cmd line: -E) /* Uncomment these lines to enter debugging mode fork=no log_stderror=yes */ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/kamailio/modules/" # Uncomment this if you want to use SQL database #loadmodule "mysql.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "mi_fifo.so" # Uncomment this if you want digest authentication # db_mysql.so must be loaded ! #loadmodule "auth.so" #loadmodule "auth_db.so" # !! Nathelper loadmodule "nathelper.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") # -- usrloc params -- modparam("usrloc", "db_mode", 0) # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line #modparam("usrloc", "db_mode", 2) # -- auth params -- # Uncomment if you are using auth module # #modparam("auth_db", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) # #modparam("auth_db", "password_column", "password") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # !! Nathelper modparam("usrloc","nat_bflag",6) modparam("nathelper","sipping_bflag",8) modparam("nathelper", "ping_nated_only", 1) # Ping only clients behind NAT # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; # !! Nathelper # Special handling for NATed clients; first, NAT test is # executed: it looks for via!=received and RFC1918 addresses # in Contact (may fail if line-folding is used); also, # the received test should, if completed, should check all # vias for rpesence of received if (nat_uac_test("3")) { # Allow RR-ed requests, as these may indicate that # a NAT-enabled proxy takes care of it; unless it is # a REGISTER if (is_method("REGISTER") || !is_present_hf("Record-Route")) { log("LOG: Someone trying to register from private IP, rewriting\n"); # This will work only for user agents that support symmetric # communication. We tested quite many of them and majority is # smart enough to be symmetric. In some phones it takes a configuration # option. With Cisco 7960, it is called NAT_Enable=Yes, with kphone it is # called "symmetric media" and "symmetric signalling". fix_nated_contact(); # Rewrite contact with source IP of signalling if ( is_method("INVITE") ) { fix_nated_sdp("1"); # Add direction=active to SDP }; force_rport(); # Add rport parameter to topmost Via setbflag(6); # Mark as NATed # if you want sip nat pinging # setbflag(8); }; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!is_method("REGISTER")) record_route(); # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); exit; }; if (!uri==myself) { # mark routing logic in request append_hf("P-hint: outbound\r\n"); route(1); exit; }; # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (method=="REGISTER") { # Uncomment this if you want to use digest authentication # if (!www_authorize("siphub.org", "subscriber")) { # www_challenge("siphub.org", "0"); # return; # }; save("location"); exit; }; lookup("aliases"); if (!uri==myself) { append_hf("P-hint: outbound alias\r\n"); route(1); exit; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; }; append_hf("P-hint: usrloc applied\r\n"); route(1); } route[1] { # !! Nathelper if (uri=~"[@:](192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)" && !search("^Route:")){ sl_send_reply("479", "We don't forward to private IP addresses"); exit; }; # if client or server know to be behind a NAT, enable relay if (isbflagset(6)) { force_rtp_proxy(); }; # NAT processing of replies; apply to all transactions (for example, # re-INVITEs from public to private UA are hard to identify as # NATed at the moment of request processing); look at replies t_on_reply("1"); # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; } # !! Nathelper onreply_route[1] { # NATed transaction ? if (isbflagset(6) && status =~ "(183)|2[0-9][0-9]") { fix_nated_contact(); force_rtp_proxy(); # otherwise, is it a transaction behind a NAT and we did not # know at time of request processing ? (RFC1918 contacts) } else if (nat_uac_test("1")) { fix_nated_contact(); }; } kamailio-4.0.4/examples/kamailio/redirect.cfg0000644000000000000000000000132012223032460017657 0ustar rootroot# # $Id$ # # this example shows use of ser as stateless redirect server # # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/kamailio/modules/" loadmodule "sl.so" # ------------------------- request routing logic ------------------- # main routing logic route{ # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { log("REGISTER"); sl_send_reply("200", "ok"); return; }; # rewrite current URI, which is always part of destination ser rewriteuri("sip:parallel@siphub.net:9"); # append one more URI to the destination ser append_branch("sip:redirect@siphub.net:9"); # redirect now sl_send_reply("300", "Redirect"); } kamailio-4.0.4/examples/kamailio/acc.cfg0000644000000000000000000000313312223032460016610 0ustar rootroot# # $Id$ # # example: accounting calls to nummerical destinations # # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/kamailio/modules/" loadmodule "tm.so" loadmodule "acc.so" loadmodule "sl.so" loadmodule "maxfwd.so" loadmodule "rr.so" # ----------------- setting module-specific parameters --------------- # -- acc params -- # set the reporting log level modparam("acc", "log_level", 1) # number of flag, which will be used for accounting; if a message is # labeled with this flag, its completion status will be reported modparam("acc", "log_flag", 1 ) # ------------------------- request routing logic ------------------- # main routing logic route{ /* ********* ROUTINE CHECKS ********************************** */ # filter too old messages if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; # Process record-routing if (loose_route()) { # label BYEs for accounting if (method=="BYE") setflag(1); t_relay(); exit; }; # labeled all transaction for accounting setflag(1); # record-route INVITES to make sure BYEs will visit our server too if (method=="INVITE") record_route(); # forward the request statefuly now; (we need *stateful* forwarding, # because the stateful mode correlates requests with replies and # drops retranmissions; otherwise, we would have to report on # every single message received) if (!t_relay()) { sl_reply_error(); exit; }; } kamailio-4.0.4/examples/kamailio/acc-mysql.cfg0000644000000000000000000001470012223032460017755 0ustar rootroot# $Id$ # # Sample config for MySQL accouting with Kamailio 1.2.0 # # - mysql module must be compiled and installed # # - new columns have to be added since by default only few are recorded # - here are full SQL statements to create acc and missed_calls tables # # CREATE TABLE `acc` ( # `id` int(10) unsigned NOT NULL auto_increment, # `method` varchar(16) NOT NULL default '', # `from_tag` varchar(64) NOT NULL default '', # `to_tag` varchar(64) NOT NULL default '', # `callid` varchar(128) NOT NULL default '', # `sip_code` char(3) NOT NULL default '', # `sip_reason` varchar(32) NOT NULL default '', # `time` datetime NOT NULL default '0000-00-00 00:00:00', # `src_ip` varchar(64) NOT NULL default '', # `dst_user` varchar(64) NOT NULL default '', # `dst_domain` varchar(128) NOT NULL default '', # `src_user` varchar(64) NOT NULL default '', # `src_domain` varchar(128) NOT NULL default '', # INDEX acc_callid (`callid`), # PRIMARY KEY (`id`) # ); # # CREATE TABLE `missed_calls` ( # `id` int(10) unsigned NOT NULL auto_increment, # `method` varchar(16) NOT NULL default '', # `from_tag` varchar(64) NOT NULL default '', # `to_tag` varchar(64) NOT NULL default '', # `callid` varchar(128) NOT NULL default '', # `sip_code` char(3) NOT NULL default '', # `sip_reason` varchar(32) NOT NULL default '', # `time` datetime NOT NULL default '0000-00-00 00:00:00', # `src_ip` varchar(64) NOT NULL default '', # `dst_user` varchar(64) NOT NULL default '', # `dst_domain` varchar(128) NOT NULL default '', # `src_user` varchar(64) NOT NULL default '', # `src_domain` varchar(128) NOT NULL default '', # INDEX acc_callid (`callid`), # PRIMARY KEY (`id`) # ); # # # ----------- global configuration parameters ------------------------ debug=3 # debug level (cmd line: -dddddddddd) fork=yes log_stderror=no # (cmd line: -E) /* Uncomment these lines to enter debugging mode fork=no log_stderror=yes */ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 # # uncomment the following lines for TLS support #disable_tls = 0 #listen = tls:your_IP:5061 #tls_verify_server = 1 #tls_verify_client = 1 #tls_require_client_certificate = 0 #tls_method = TLSv1 #tls_certificate = "/usr/local/etc/kamailio/tls/user/user-cert.pem" #tls_private_key = "/usr/local/etc/kamailio/tls/user/user-privkey.pem" #tls_ca_list = "/usr/local/etc/kamailio/tls/user/user-calist.pem" # ------------------ module loading ---------------------------------- # set module path mpath="/usr/local/lib/kamailio/modules/" # Uncomment this if you want to use SQL database # - MySQL loaded for accounting as well loadmodule "db_mysql.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "acc.so" loadmodule "mi_fifo.so" # Uncomment this if you want digest authentication # db_mysql.so must be loaded ! #loadmodule "auth.so" #loadmodule "auth_db.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") # -- usrloc params -- #modparam("usrloc", "db_mode", 0) # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line modparam("usrloc", "db_mode", 2) # -- auth params -- # Uncomment if you are using auth module # #modparam("auth_db", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) # #modparam("auth_db", "password_column", "password") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # -- acc params -- modparam("acc", "db_url", "mysql://kamailio:kamailiorw@localhost/kamailio") # flag to record to db modparam("acc", "db_flag", 1) modparam("acc", "db_missed_flag", 2) # flag to log to syslog modparam("acc", "log_flag", 1) modparam("acc", "log_missed_flag", 2) # use extra accounting to record caller and callee username/domain # - take them from From URI and R-URI modparam("acc", "log_extra", "src_user=$fU;src_domain=$fd;dst_user=$rU;dst_domain=$rd") modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;dst_user=$rU;dst_domain=$rd") # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!is_method("REGISTER")) record_route(); # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); if(is_method("BYE")) { # account BYE for STOP record setflag(1); } route(1); }; # account all calls if(is_method("INVITE")) { # set accounting on for INVITE (success or missed call) setflag(1); setflag(2); } if (!uri==myself) { # mark routing logic in request append_hf("P-hint: outbound\r\n"); # if you have some interdomain connections via TLS #if(uri=~"@tls_domain1.net") { # t_relay("tls:domain1.net"); # exit; #} else if(uri=~"@tls_domain2.net") { # t_relay("tls:domain2.net"); # exit; #} route(1); }; # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (is_method("REGISTER")) { # Uncomment this if you want to use digest authentication #if (!www_authorize("kamailio.org", "subscriber")) { # www_challenge("kamailio.org", "0"); # exit; #}; save("location"); exit; }; if (!uri==myself) { append_hf("P-hint: outbound alias\r\n"); route(1); }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; append_hf("P-hint: usrloc applied\r\n"); }; route(1); } route[1] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; exit; } kamailio-4.0.4/examples/kamailio/exec_s5.cfg0000644000000000000000000000274112223032460017421 0ustar rootroot# # $Id$ # # simple quick-start config script # # ----------- global configuration parameters ------------------------ #set module path mpath="/usr/local/lib/kamailio/modules/" loadmodule "sl.so" loadmodule "tm.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "exec.so" # ----------------- setting module-specific parameters --------------- route{ # uri for my domain ? if (uri==myself) { if (method=="REGISTER") { save("location"); return; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { # proceed to email notification if (method=="INVITE") route(1) else sl_send_reply("404", "Not Found"); exit; }; }; # user found, forward to his current uri now if (!t_relay()) { sl_reply_error(); }; } /* handling of missed calls */ route[1] { # don't continue if it is a retransmission if ( !t_newtran()) { sl_reply_error(); exit; }; # external script: lookup user, if user exists, send # an email notification to him if (!exec_msg(' QUERY="select email_address from subscriber where user=\"$$SIP_OUSER\""; EMAIL=`mysql -Bsuser -pheslo -e "$$QUERY" ser`; if [ -z "$$EMAIL" ] ; then exit 1; fi ; echo "SIP request received from $$SIP_HF_FROM for $$SIP_OUSER" | mail -s "request for you" $$EMAIL ')) { # exec returned error ... user does not exist # send a stateful reply t_reply("404", "User does not exist"); } else { t_reply("600", "No messages for this user"); }; exit; } kamailio-4.0.4/examples/kamailio/exec_s4.cfg0000644000000000000000000000171512223032460017420 0ustar rootroot# # $Id$ # # email notification to email address from mysql database # # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/kamailio/modules/" loadmodule "exec.so" loadmodule "sl.so" loadmodule "tm.so" # send email if a request arrives; process statefully # to avoid multiple execution on request retransmissions route[0] { # stop script processing if transaction exists if ( !t_newtran()) { sl_reply_error(); return; }; if (!exec_msg(' QUERY="select email_address from subscriber where user=\"$$SIP_OUSER\""; EMAIL=`mysql -Bsuser -pheslo -e "$$QUERY" ser`; if [ -z "$EMAIL" ] ; then exit 1; fi ; echo "SIP request received from $$SIP_HF_FROM for $$SIP_OUSER" | mail -s "request for you" $$EMAIL ')) { # exec returned error ... user does not exist # send a stateful reply t_reply("404", "User does not exist"); } else { t_reply("600", "No messages for this user"); }; } kamailio-4.0.4/examples/kamailio/flag_reply.cfg0000644000000000000000000000405612223032460020213 0ustar rootroot# # $Id$ # # simple quick-start config script # # ----------- global configuration parameters ------------------------ debug=3 # debug level (cmd line: -dddddddddd) fork=yes log_stderror=no # (cmd line: -E) fork=no log_stderror=yes check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) children=4 port=5060 # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/kamailio/modules/" # Uncomment this if you want to use SQL database #loadmodule "db_mysql.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "mi_fifo.so" # Uncomment this if you want digest authentication # mysql.so must be loaded ! #loadmodule "auth.so" #loadmodule "auth_db.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") # -- usrloc params -- modparam("usrloc", "db_mode", 0) # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line #modparam("usrloc", "db_mode", 2) # -- auth params -- # Uncomment if you are using auth module # #modparam("auth_db", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) # #modparam("auth_db", "password_column", "password") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # ------------------------- request routing logic ------------------- # main routing logic route{ setflag(1); t_on_failure("1"); t_on_reply("1"); log(1, "message received\n"); t_relay("udp:kamailio.org:5060"); } onreply_route[1] { if (isflagset(1)) { log(1, "onreply: flag set\n"); } else { log(1, "onreply: flag unset\n"); }; } failure_route[1] { if (isflagset(1)) { log(1, "failure: flag set\n"); } else { log(1, "failure: flag unset\n"); }; } kamailio-4.0.4/examples/kamailio/logging.cfg0000644000000000000000000000106012223032460017505 0ustar rootroot# # $Id$ # # logging example # # ------------------ module loading ---------------------------------- fork=no port=5060 log_stderror=yes debug=3 # ------------------------- request routing logic ------------------- # main routing logic route{ # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { log(1, "REGISTER received\n"); } else { log(1, "non-REGISTER received\n"); }; if (uri=~"sip:.*[@:]siphub.net") { log(1, "request for siphub.net received\n"); } else { log(1, "request for other domain received\n"); }; } kamailio-4.0.4/examples/kamailio/serial_183.cfg0000644000000000000000000000261512223032460017740 0ustar rootroot# # $Id$ # # this example shows how to use forking on failure # log_stderror=1 fork=no listen=192.168.2.16 debug=3 # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/kamailio/modules/" # Uncomment this if you want to use SQL database loadmodule "tm.so" loadmodule "sl.so" loadmodule "maxfwd.so" # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; /* skip register for testing purposes */ if (method=="REGISTER") { sl_send_reply("200", "ok"); exit; }; if (!method=="ACK") log(1, "forwarding now to primary destination\n"); if (method=="INVITE") { rewriteuri("sip:xxx@192.168.2.16:5064"); # if transaction broken, try other an alternative # route t_on_failure("1"); # if a provisional came, stop alternating t_on_reply("1"); }; t_relay(); } failure_route[1] { log(1, "trying at alternate destination\n"); append_branch("sip:yyy@192.168.2.16:5064"); t_relay(); } onreply_route[1] { log(1, "reply came in\n"); if (status=~"18[0-9]") { log(1, "provisional -- resetting negative failure\n"); t_on_failure("0"); }; } kamailio-4.0.4/examples/kamailio/msilo.cfg0000644000000000000000000000562312223032460017213 0ustar rootroot# # MSILO usage example # # $ID: daniel $ # children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/kamailio/modules/" loadmodule "textops.so" loadmodule "sl.so" loadmodule "db_mysql.so" loadmodule "maxfwd.so" loadmodule "tm.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "msilo.so" # ----------------- setting module-specific parameters --------------- # -- registrar params -- modparam("registrar", "default_expires", 120) # -- registrar params -- modparam("usrloc", "db_mode", 0) # -- msilo params -- modparam("msilo", "db_url", "mysql://kamailio:kamailiorw@localhost/kamailio") # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 15 ) modparam("tm", "wt_timer", 10 ) route{ if ( !mf_process_maxfwd_header("10") ) { sl_send_reply("483","To Many Hops"); exit; }; if (uri==myself) { # for testing purposes, simply okay all REGISTERs # is_method("XYZ") is faster than (method=="XYZ") # but requires textops module if (is_method("REGISTER")) { save("location"); log("REGISTER received -> dumping messages with MSILO\n"); # MSILO - dumping user's offline messages if (m_dump()) { log("MSILO: offline messages dumped - if they were\n"); }else{ log("MSILO: no offline messages dumped\n"); }; exit; }; # backup r-uri for m_dump() in case of delivery failure $avp(i:11) = $ru; # domestic SIP destinations are handled using our USRLOC DB if(!lookup("location")) { if (! t_newtran()) { sl_reply_error(); exit; }; # we do not care about anything else but MESSAGEs if (!is_method("MESSAGE")) { if (!t_reply("404", "Not found")) { sl_reply_error(); }; exit; }; log("MESSAGE received -> storing using MSILO\n"); # MSILO - storing as offline message if (m_store("$ru")) { log("MSILO: offline message stored\n"); if (!t_reply("202", "Accepted")) { sl_reply_error(); }; }else{ log("MSILO: offline message NOT stored\n"); if (!t_reply("503", "Service Unavailable")) { sl_reply_error(); }; }; exit; }; # if the downstream UA does not support MESSAGE requests # go to failure_route[1] t_on_failure("1"); t_relay(); exit; }; # forward anything else t_relay(); } failure_route[1] { # forwarding failed -- check if the request was a MESSAGE if (!is_method("MESSAGE")) { exit; }; log(1,"MSILO: the downstream UA does not support MESSAGE requests ...\n"); # we have changed the R-URI with the contact address -- ignore it now if (m_store("$avp(i:11)")) { log("MSILO: offline message stored\n"); t_reply("202", "Accepted"); }else{ log("MSILO: offline message NOT stored\n"); t_reply("503", "Service Unavailable"); }; } kamailio-4.0.4/examples/kamailio/ctd.sh0000644000000000000000000001247012223032460016513 0ustar rootroot#!/bin/sh # # $Id$ # # Usage: ctd.sh $FROM $TARGET # # click-to-dial example using REFER #---------------------------------- # # About: # ------ # this script initiates a call from SIP user $FROM to SIP # user $TARGET; it works as follows: a dummy user invites # $FROM to a dummy "call on hold"; as soon as it is set up, the # dummy user transfers $FROM to $TARGET (REFER transaction) # and terminates the dummy session established previously # (BYE transaction). Note: the "dummy call" is used to # make $FROM accept $REFER -- most of SIP phones do not # accept REFER if no call has not been established yet. # # Requirements: # ------------- # - SER with FIFO server turned on and TM module loaded # # Limitations: # ------------ # it only works with UAs supporting REFER; it has been tested # with Cisco 7960, Mitel 5055, Grandstream and Pingtel; Windows # Messenger does not support REFER. Never tested on solaris. # Some cisco 7960 images don't work (in particular, POS30202 # doesnt, POS3-03-8-21 does) # # History: # -------- # 2003-03-01 bug_fix: route set reversed # 2003-02-27 dialog support completed (jiri) # 2003-04-28 dialog info precomputed in SER (jiri) # 2007-04-06 updated for Kamailio 1.2.0+ (daniel) #-------------------------------- # config: who with whom # address of the final destination to which we want to transfer # initial CSeq and CallId if [ -z "$2" ]; then TARGET="sip:23@192.168.2.16" echo "destination unspecified -- taking default value $TARGET" else TARGET="$2" fi # address of user wishing to initiate conversation if [ -z "$1" ] ; then URI="sip:44@192.168.2.16" echo "caller unspecified -- taking default value $URI" else URI="$1" fi #--------------------------------- # fixed config data FIFO="/tmp/kamailio_fifo" # address of controller FROM="" CSEQ="1" CALLIDNR=`date '+%s'`$$ CALLID="${CALLIDNR}.fifouacctd" name="ctd_fifo_$$" fifo_reply="/tmp/$name" dlg="/tmp/$CALLID.dlg" FIXED_DLG="From: $FROM;tag=$CALLIDNR\r\nCall-ID: $CALLID\r\nContact: \r\n" #---------------------------------- # generate parts of FIFO-request essential to forming # subsequent in-dialog reuqests # # limitations: parsing broken if <> in display names or # line-folding used filter_fl() { awk -F ' ' ' BEGIN { IGNORECASE=1; line=0; eoh=0;ret=1 } END { exit ret; } {line++; } # line 2: status code line==2 && /^2[0-9][0-9] / { ret=0;next; } line==2 && /^[3-6][0-9][0-9] / { print; print $0 > "/dev/stderr"; next; } line==2 { print "reply error"; print; next; } # skip body /^$/ { eoh=1 } eoh==1 { next } # uri and outbound uri at line 2,3: copy and paste line==3 { print $0; next; } line==4 { print $0; print "."; printf("\""); next; } # line 5: Route; empty if ".", copy and paste otherwise line==5 && /^\.$/ { next; } # if non-empty, copy and paste it line==5 { printf("%s\n", $0); next; } # filter out to header field for use in next requests /^(To|t):/ { printf("%s\n", $0); next; } # anything else will be ignored {next} ' # end of awk script } # end of filter_fl #--------------------------- # main # set up exit cleaner trap "rm -f $dlg $fifo_reply; exit 1" 0 # set up FIFO communication if [ ! -w $FIFO ] ; then # can I write to FIFO server? echo "Error opening ser's FIFO $FIFO" exit 1 fi mkfifo $fifo_reply # create a reply FIFO if [ $? -ne 0 ] ; then echo "error opening reply fifo $fifo_reply" exit 1 fi chmod a+w $fifo_reply # start reader now so that it is ready for replies # immediately after a request is out cat < $fifo_reply | filter_fl > $dlg & fifo_job="$!" # initiate dummy INVITE with pre-3261 "on-hold" # (note the dots -- they mean in order of appearance: # outbound uri, end of headers, end of body; eventualy # the FIFO request must be terminated with an empty line) #cat < $FIFO <\r\nCSeq: $CSEQ INVITE\r\nContent-Type: application/sdp\r\n"` " "`printf "v=0\r\no=click-to-dial 0 0 IN IP4 0.0.0.0\r\ns=session\r\nc=IN IP4 0.0.0.0\r\nb=CT:1000\r\nt=0 0\r\nm=audio 9 RTP/AVP 8 0\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\n"` " EOF #exit # wait for reply wait $fifo_job # returns completion status of filter_fl if [ "$?" -ne "0" ] ; then echo "invitation failed" exit 1 fi echo "invitation succeeded" # proceed to REFER now if [ \! -r $dlg ] ; then echo "dialog broken" exit 1 fi CSEQ=`expr $CSEQ + 1` # start reader now so that it is ready for replies # immediately after a request is out cat < $fifo_reply | filter_fl > /dev/null & fifo_job="$!" # dump the REFER request to FIFO server cat > $FIFO < /dev/null & fifo_job="$!" cat > $FIFO <= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (method=="REGISTER") { # verify credentials if (!www_authorize("foo.bar", "subscriber")) { www_challenge("foo.bar", "0"); exit; }; # if ok, update contacts and ... save("location"); # ... if this REGISTER is not a replica from our # peer server, replicate to the peer server if (!src_ip==backup.foo.bar) { t_replicate("sip:backup.foo.bar:5060"); }; exit; }; # do whatever else appropriate for your domain log("non-REGISTER\n"); }; } kamailio-4.0.4/examples/kamailio/web_im/0000755000000000000000000000000012223032460016643 5ustar rootrootkamailio-4.0.4/examples/kamailio/web_im/README0000644000000000000000000000041312223032460017521 0ustar rootroot# # $Id$ # This examle illustrate how to use ser's FIFO interface to initate sending an instant message from a webpage. To enable this example, you need - web server with PHP support - install the example webpages on the server - have running ser with enabled fifo kamailio-4.0.4/examples/kamailio/web_im/click_to_dial.html0000644000000000000000000000113312223032460022307 0ustar rootroot
Click-To-Dial

Click-To-Dial (using REFER)

Unfortunately, this example does not work. The reason is use of REFER for third-party call-control has not been standardized due to resistance of proponents of B2BUA use (which is somewhat bloated and not always operational).
Caller's SIP Address
Callee's SIP Address
Click to dial
kamailio-4.0.4/examples/kamailio/web_im/send_im.html0000644000000000000000000000060712223032460021152 0ustar rootroot
Send IM

Send IM

SIP Address
Example: john@siphub.net
Message
Click to send
kamailio-4.0.4/examples/kamailio/web_im/send_im.php0000644000000000000000000000260612223032460020776 0ustar rootroot
Send IM Status

Send IM Status

"; /* open fifo now */ $fifo_handle=fopen( $fifo, "w" ); if (!$fifo_handle) { exit ("Sorry -- cannot open fifo: ".$fifo); } /* construct FIFO command */ $fifo_cmd=":t_uac_dlg:".$myfilename."\n". "MESSAGE\n". $sip_address."\n". $outbound_proxy."\n". ".\n". "\"From: ".$web_contact."\r\n". "To: ".$sip_address."\r\n". "p-version: ".$signature."\r\n". "Contact: ".$web_contact."\r\n". "Content-Type: text/plain; charset=UTF-8\r\n". "\"\n". "\"".$instant_message."\"". "\n\n"; /* create fifo for replies */ system("mkfifo -m 666 ".$mypath ); /* write fifo command */ if (fwrite( $fifo_handle, $fifo_cmd)==-1) { unlink($mypath); fclose($fifo_handle); exit("Sorry -- fifo writing error"); } fclose($fifo_handle); /* read output now */ if (readfile( $mypath )==-1) { unlink($mypath); exit("Sorry -- fifo reading error"); } unlink($mypath); echo "

Thank you for using IM

"; ?> kamailio-4.0.4/examples/kamailio/web_im/click_to_dial.php0000644000000000000000000000253712223032460022143 0ustar rootroot

Click-To-Dial

Click-To-Dial

"; /* open fifo now */ $fifo_handle=fopen( $fifo, "w" ); if (!$fifo_handle) { exit ("Sorry -- cannot open fifo: ".$fifo); } /* construct FIFO command */ $fifo_cmd=":t_uac_dlg:".$myfilename."\n". "REFER\n". $caller."\n". $outbound_proxy."\n". ".\n". "\"From: ".$web_contact."\r\n". "To: ".$callee."\r\n". "p-version: ".$signature."\r\n". "Contact: ".$web_contact."\r\n". "Referred-By: ".$web_contact."\r\n". "Refer-To: ".$callee."\r\n". "\"\n\n"; /* create fifo for replies */ system("mkfifo -m 666 ".$mypath ); /* write fifo command */ if (fwrite( $fifo_handle, $fifo_cmd)==-1) { unlink($mypath); fclose($fifo_handle); exit("Sorry -- fifo writing error"); } fclose($fifo_handle); /* read output now */ if (readfile( $mypath )==-1) { unlink($mypath); exit("Sorry -- fifo reading error"); } unlink($mypath); echo "

Thank you for using click-to-dial

"; ?> kamailio-4.0.4/examples/kamailio/pstn.cfg0000644000000000000000000001006312223032460017046 0ustar rootroot# # $Id$ # # example: ser configured as PSTN gateway guard; PSTN gateway is located # at 192.168.0.10 # # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/kamailio/modules/" loadmodule "sl.so" loadmodule "tm.so" loadmodule "acc.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "db_mysql.so" loadmodule "auth.so" loadmodule "auth_db.so" loadmodule "group.so" loadmodule "uri.so" # ----------------- setting module-specific parameters --------------- modparam("auth_db", "db_url","mysql://kamailio:kamailiorw@localhost/kamailio") modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") # -- acc params -- modparam("acc", "log_level", 1) # that is the flag for which we will account -- don't forget to # set the same one :-) modparam("acc", "log_flag", 1 ) # ------------------------- request routing logic ------------------- # main routing logic route{ /* ********* ROUTINE CHECKS ********************************** */ # filter too old messages if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; /* ********* RR ********************************** */ /* grant Route routing if route headers present */ if (loose_route()) { t_relay(); exit; }; /* record-route INVITEs -- all subsequent requests must visit us */ if (method=="INVITE") { record_route(); }; # now check if it really is a PSTN destination which should be handled # by our gateway; if not, and the request is an invitation, drop it -- # we cannot terminate it in PSTN; relay non-INVITE requests -- it may # be for example BYEs sent by gateway to call originator if (!uri=~"sip:\+?[0-9]+@.*") { if (method=="INVITE") { sl_send_reply("403", "Call cannot be served here"); } else { forward(); }; exit; }; # account completed transactions via syslog setflag(1); # free call destinations ... no authentication needed if ( is_user_in("Request-URI", "free-pstn") /* free destinations */ || uri=~"sip:[79][0-9][0-9][0-9]@.*" /* local PBX */ || uri=~"sip:98[0-9][0-9][0-9][0-9]") { log("free call"); } else if (src_ip==192.168.0.10) { # our gateway doesn't support digest authentication; # verify that a request is coming from it by source # address log("gateway-originated request"); } else { # in all other cases, we need to check the request against # access control lists; first of all, verify request # originator's identity if (!proxy_authorize( "gateway" /* realm */, "subscriber" /* table name */)) { proxy_challenge( "gateway" /* realm */, "0" /* no qop */ ); exit; }; # authorize only for INVITEs -- RR/Contact may result in weird # things showing up in d-uri that would break our logic; our # major concern is INVITE which causes PSTN costs if (method=="INVITE") { # does the authenticated user have a permission for local # calls (destinations beginning with a single zero)? # (i.e., is he in the "local" group?) if (uri=~"sip:0[1-9][0-9]+@.*") { if (!is_user_in("credentials", "local")) { sl_send_reply("403", "No permission for local calls"); exit; }; # the same for long-distance (destinations begin with two zeros") } else if (uri=~"sip:00[1-9][0-9]+@.*") { if (!is_user_in("credentials", "ld")) { sl_send_reply("403", " no permission for LD "); exit; }; # the same for international calls (three zeros) } else if (uri=~"sip:000[1-9][0-9]+@.*") { if (!is_user_in("credentials", "int")) { sl_send_reply("403", "International permissions needed"); exit; }; # everything else (e.g., interplanetary calls) is denied } else { sl_send_reply("403", "Forbidden"); exit; }; }; # INVITE to authorized PSTN }; # authorized PSTN # if you have passed through all the checks, let your call go to GW! rewritehostport("192.168.0.10:5060"); # forward the request now if (!t_relay()) { sl_reply_error(); exit; }; } kamailio-4.0.4/examples/kamailio/exec_s3.cfg0000644000000000000000000000136512223032460017420 0ustar rootroot# # $Id$ # # email notification to email address from mysql database # # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/kamailio/modules/" loadmodule "exec.so" loadmodule "sl.so" # send email if a request arrives route[0] { if (!exec_msg(' QUERY="select email_address from subscriber where user=\"$$SIP_OUSER\""; EMAIL=`mysql -Bsuser -pheslo -e "$$QUERY" kamailio`; if [ -z "$$EMAIL" ] ; then exit 1; fi ; echo "SIP request received from $$SIP_HF_FROM for $$SIP_OUSER" | mail -s "request for you" $$EMAIL ')) { # exec returned error ... user does not exist sl_send_reply("404", "User does not exist"); } else { sl_send_reply("600", "No messages for this user"); }; } kamailio-4.0.4/examples/kamailio/fork.cfg0000644000000000000000000000354012223032460017025 0ustar rootroot# # $Id$ # # example script showing both types of forking; # incoming message is forked in parallel to # 'nobody' and 'parallel', if no positive reply # appears with final_response timer, nonsense # is retried (serial forking); than, destination # 'foo' is given last chance # ------------------ module loading ---------------------------------- #set module path mpath="/usr/local/lib/kamailio/modules/" loadmodule "sl.so" loadmodule "tm.so" # ----------------- setting module-specific parameters --------------- # -- tm params -- # set time for which ser will be waiting for a final response; # fr_inv_timer sets value for INVITE transactions, fr_timer # for all others modparam("tm", "fr_inv_timer", 15 ) modparam("tm", "fr_timer", 10 ) # ------------------------- request routing logic ------------------- # main routing logic route{ # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { log("REGISTER"); sl_send_reply("200", "ok"); exit; }; # try these two destinations first in parallel; the second # destination is targeted to sink port -- that will make ser # wait until timer hits seturi("sip:nobody@kamailio.org"); append_branch("sip:parallel@kamailio.org:9"); # if we do not get a positive reply, continue at reply_route[1] t_on_failure("1"); # forward the request to all destinations in destination set now t_relay(); } failure_route[1] { # forwarding failed -- try again at another destination append_branch("sip:nonsense@kamailio.org"); log(1,"first redirection\n"); # if this alternative destination fails too, proceed to reply_route[2] t_on_failure("2"); t_relay(); } failure_route[2] { # try out the last resort destination append_branch("sip:foo@kamailio.org"); log(1, "second redirection\n"); # we no more call t_on_negative here; if this destination # fails too, transaction will complete t_relay(); } kamailio-4.0.4/examples/acc.cfg0000644000000000000000000000304012223032460015017 0ustar rootroot# # $Id$ # # example: accounting calls to nummerical destinations # # ------------------ module loading ---------------------------------- loadmodule "modules/tm/tm.so" loadmodule "modules/acc/acc.so" loadmodule "modules/sl/sl.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/rr/rr.so" # ----------------- setting module-specific parameters --------------- # -- acc params -- # set the reporting log level modparam("acc", "log_level", 1) # number of flag, which will be used for accounting; if a message is # labeled with this flag, its completion status will be reported modparam("acc", "log_flag", 1 ) # ------------------------- request routing logic ------------------- # main routing logic route{ /* ********* ROUTINE CHECKS ********************************** */ # filter too old messages if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; if (len_gt( max_len )) { sl_send_reply("513", "Wow -- Message too large"); break; }; # Process record-routing if (loose_route()) { t_relay(); break; }; # labeled all transaction for accounting setflag(1); # record-route INVITES to make sure BYEs will visit our server too if (method=="INVITE") record_route(); # forward the request statefuly now; (we need *stateful* forwarding, # because the stateful mode correlates requests with replies and # drops retranmissions; otherwise, we would have to report on # every single message received) if (!t_relay()) { sl_reply_error(); break; }; } kamailio-4.0.4/examples/xhttp.cfg0000644000000000000000000000216612223032460015450 0ustar rootrootdebug=3 fork=yes log_stderror=no auto_aliases=no tcp_connection_lifetime=3605 tcp_accept_no_cl=yes mpath="/usr/local/lib64/kamailio/modules_k/:/usr/local/lib64/kamailio/modules/" loadmodule "/usr/local/lib64/kamailio/modules_k/xlog.so" loadmodule "/usr/local/lib64/kamailio/modules/db_mysql.so" loadmodule "/usr/local/lib64/kamailio/modules/sl.so" loadmodule "/usr/local/lib64/kamailio/modules_k/pv.so" loadmodule "/usr/local/lib64/kamailio/modules/xhttp.so" loadmodule "/usr/local/lib64/kamailio/modules/xhttp_rpc.so" modparam("xhttp_rpc", "xhttp_rpc_root", "http_rpc") loadmodule "/usr/local/lib64/kamailio/modules/xhttp_pi.so" modparam("xhttp_pi", "xhttp_pi_root", "http_pi") modparam("xhttp_pi", "framework", "/usr/local/etc/kamailio/pi_framework.xml") route{ exit; } event_route[xhttp:request] { $var(xhttp_rpc_root) = $(hu{s.substr,0,9}); if ($var(xhttp_rpc_root) == "/http_rpc") { dispatch_xhttp_rpc(); } $var(xhttp_rpc_root) = $(hu{s.substr,0,8}); if ($var(xhttp_rpc_root) == "/http_pi") { dispatch_xhttp_pi(); } else xhttp_reply("200", "OK", "text/html", "Wrong URL $hu"); } kamailio-4.0.4/examples/welcome.cfg0000644000000000000000000000633612223032460015737 0ustar rootroot# # $Id$ # # welcome message for new subscribers; based on exec # # ----------- global configuration parameters ------------------------ debug=3 # debug level (cmd line: -dddddddddd) # must be yes since REGISTER processing causes an INVITE to be sent, # which needs to be processed by another process fork=yes children=4 # debugging log_stderror=yes # (cmd line: -E) mhomed=yes fifo="/tmp/ser_fifo" # ------------------ module loading ---------------------------------- # Uncomment this if you want to use SQL database loadmodule "modules/mysql/mysql.so" loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" loadmodule "modules/rr/rr.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/exec/exec.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- modparam("usrloc", "db_mode", 2) modparam("usrloc", "db_url", "mysql://ser:heslo@192.168.2.16/ser" ) # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (len_gt( max_len )) { sl_send_reply("513", "Message too big"); break; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (method=="INVITE") record_route(); # 1=loose routing # loose-route processing if (loose_route()) { t_relay(); break; }; log(1, "RR processing completed\n"); # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (method=="REGISTER") { # save location before initiating welcome save("location"); # welcome message route(3); break; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); break; }; }; t_relay(); } # welcome message -- if a REGISTER succeeded, look if it is the first-time; # if so, initiate click-to-dial transaction to set up call to an announcement # server; edit the config values first to make it work route[3] { if (!exec_msg(' # config: # --announcement server URI ANS="sip:7170@iptel.org" # --SIP domain DOMAIN="192.168.2.16" # ctd CTD=${HOME}/sip_router/examples/ctd.sh # ------------------------------------ # check if first time ... SIP_UID=`echo $SIP_HF_TO | sed -e "s/^.*sip:\([a-zA-Z0-9_\.]*\)@.*$/\1/g"` QUERY="select flag from subscriber where username=\"$SIP_UID\"; update subscriber set flag=\"x\" where username=\"$SIP_UID\" "; mysql -Bsuser -pheslo -e "$QUERY" ser| grep "^x$" > /dev/null # ... if so, c-t-d to announcement server if [ "$?" -ne 0 ] ; then # flag was not set to x yet -- first-time registration; # initiate a call from telephone of the user to an announcement server $CTD "sip:$SIP_UID@$DOMAIN" "$ANS" > /dev/null 2>&1 fi ')) { log(1, "welcome exec failed\n"); } } kamailio-4.0.4/examples/icscf/0000755000000000000000000000000012223032461014703 5ustar rootrootkamailio-4.0.4/examples/icscf/icscf.xml0000644000000000000000000000116112223032460016512 0ustar rootroot kamailio-4.0.4/examples/icscf/icscf.cfg0000644000000000000000000000240112223032461016450 0ustar rootroot# IP-Adress for incoming SIP-Traffic, in the following format: #!define NETWORK_INTERFACE "192.168.0.2" # Port, where we listen to Traffic #!define PORT 5060 #!define NETWORKNAME "kamailio-ims.org" #!define HOSTNAME "icscf.kamailio-ims.org" # SIP-Address of capturing node, if not set, capturing is disabled. ##!define CAPTURE_NODE "sip:10.0.6.1" # Connection URL for the database: #!define DB_MODULE "db_mysql" #!define DB_URL "mysql://icscf:heslo@192.168.0.5/icscf" # Set a forced CX/DX-Peer, do not try to find one #!define CXDX_FORCED_PEER "hss.kamailio-ims.org" # *** To run in debug mode: # - define WITH_DEBUG # # *** To enable TLS support execute: # - adjust CFGDIR/tls.cfg as needed # - define WITH_TLS # # *** To enable XMLRPC support execute: # - define WITH_XMLRPC # - adjust route[XMLRPC] for access policy # # *** To enable a Homer SIP-Capter-Node: # - define CAPTURE_NODE with a proper address # # *** To forwarding to PSTN for unknown users: # - define PEERING # # *** User not found? Try to forward request to a S-CSCF # and see, if it get's challenged. # - define FALLBACK_AUTH # # Enabled Features for this host: ##!define WITH_DEBUG ##!define WITH_TLS ##!define WITH_XMLRPC ##!define PEERING ##!define FALLBACK_AUTH kamailio-4.0.4/examples/icscf/icscf.postgres.sql0000644000000000000000000000454012223032460020362 0ustar rootroot-- -- Generated from mysql2pgsql.perl -- http://gborg.postgresql.org/project/mysql2psql/ -- (c) 2001 - 2007 Jose M. Duarte, Joseph Speigle -- -- warnings are printed for drop tables if they do not exist -- please see http://archives.postgresql.org/pgsql-novice/2004-10/msg00158.php -- ############################################################## /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; DROP TABLE IF EXISTS "nds_trusted_domains" CASCADE\g DROP SEQUENCE IF EXISTS "nds_trusted_domains_id_seq" CASCADE ; CREATE SEQUENCE "nds_trusted_domains_id_seq" ; CREATE TABLE "nds_trusted_domains" ( "id" integer DEFAULT nextval('"nds_trusted_domains_id_seq"') NOT NULL, "trusted_domain" varchar(83) NOT NULL DEFAULT '', primary key ("id") ) ; INSERT INTO "nds_trusted_domains" VALUES (1, E'ims.ng-voice.com'); DROP TABLE IF EXISTS "s_cscf" CASCADE\g DROP SEQUENCE IF EXISTS "s_cscf_id_seq" CASCADE ; CREATE SEQUENCE "s_cscf_id_seq" ; CREATE TABLE "s_cscf" ( "id" integer DEFAULT nextval('"s_cscf_id_seq"') NOT NULL, "name" varchar(83) NOT NULL DEFAULT '', "s_cscf_uri" varchar(83) NOT NULL DEFAULT '', primary key ("id") ) ; INSERT INTO "s_cscf" VALUES (1, E'First and only S-CSCF', E'sip:scscf.ims.ng-voice.com:5060'); DROP TABLE IF EXISTS "s_cscf_capabilities" CASCADE\g DROP SEQUENCE IF EXISTS "s_cscf_capabilities_id_seq" CASCADE ; CREATE SEQUENCE "s_cscf_capabilities_id_seq" ; CREATE TABLE "s_cscf_capabilities" ( "id" integer DEFAULT nextval('"s_cscf_capabilities_id_seq"') NOT NULL, "id_s_cscf" int NOT NULL DEFAULT '0', "capability" int NOT NULL DEFAULT '0', primary key ("id") ) ; INSERT INTO "s_cscf_capabilities" VALUES (1, 1, 0); INSERT INTO "s_cscf_capabilities" VALUES (2, 1, 1); INSERT INTO "s_cscf_capabilities" VALUES (4, 1, 2); /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; CREATE INDEX "s_cscf_capabilities_capability_idx" ON "s_cscf_capabilities" USING btree ("capability"); CREATE INDEX "s_cscf_capabilities_id_s_cscf_idx" ON "s_cscf_capabilities" USING btree ("id_s_cscf"); kamailio-4.0.4/examples/icscf/kamailio.cfg0000644000000000000000000003376312223032461017166 0ustar rootroot#!KAMAILIO # # This config file implements the basic I-CSCF functionality # - web: http://www.kamailio.org # - git: http://sip-router.org # # Refer to the Core CookBook at http://www.kamailio.org/dokuwiki/doku.php # for an explanation of possible statements, functions and parameters. # # Direct your questions about this file to: . # # For more information about the various parameters, functions and statements # try http://sip-router.org/wiki/ . # include_file "icscf.cfg" ####### Defined Values ######### # *** Value defines - IDs used later in config # - flags # FLT_ - per transaction (message) flags # FLB_ - per branch flags #!define FLT_CAPTURE 1 system.shutdownmode = 0 desc "System shutdown mode" ####### Global Parameters ######### #!ifdef WITH_DEBUG debug=5 log_stderror=yes sip_warning=yes #!else debug=2 log_stderror=no sip_warning=no #!endif /* uncomment and configure the following line if you want Kamailio to bind on a specific interface/port/proto (default bind on all available) */ #!ifdef NETWORK_INTERFACE listen=NETWORK_INTERFACE #!endif /* port to listen to * - can be specified more than once if needed to listen on many ports */ port=PORT alias=HOSTNAME alias=NETWORKNAME user_agent_header="User-Agent: Kamailio I-CSCF" server_header="Server: Kamailio I-CSCF" /* comment the next line to enable the auto discovery of local aliases based on reverse DNS on IPs (default on) */ auto_aliases=no # Do SRV-Loadbalancing: dns_srv_lb=yes # Always: Also try IPv6: dns_try_ipv6=yes #!ifdef WITH_TLS #!define WITH_TCP enable_tls=yes #!endif /* uncomment the next line to disable TCP (default on) */ #!ifdef WITH_TCP # life time of TCP connection when there is no traffic # - a bit higher than registration expires to cope with UA behind NAT tcp_connection_lifetime=3615 #!else disable_tcp=yes #!endif check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) children=64 # ------------------ module loading ---------------------------------- mpath="/usr/lib64/kamailio/modules_k/:/usr/lib64/kamailio/modules/:/usr/lib/kamailio/modules_k/:/usr/lib/kamailio/modules/" # (we try both the lib64 and the lib directory) loadmodule "tm" loadmodule "sl" loadmodule "rr" loadmodule "pv" loadmodule "textops" loadmodule "maxfwd" loadmodule "sanity" loadmodule "siputils" loadmodule "kex" loadmodule "corex" # Control interfaces: loadmodule "ctl" loadmodule "cfg_rpc" loadmodule "mi_rpc" loadmodule "mi_fifo" #!ifdef WITH_XMLRPC loadmodule "xmlrpc" #!endif # Load the according DB-Module: loadmodule DB_MODULE loadmodule "cdp.so" loadmodule "cdp_avp.so" loadmodule "xlog.so" loadmodule "ims_icscf.so" #!ifdef CAPTURE_NODE loadmodule "siptrace.so" #!endif #!ifdef WITH_DEBUG loadmodule "debugger.so" #!endif #!ifdef WITH_TLS loadmodule "tls.so" #!endif #!ifdef PEERING loadmodule "enum" #!endif # ----------------- setting module-specific parameters --------------- # ----- mi_fifo params ----- modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") modparam("mi_fifo", "fifo_mode", 0666) modparam("mi_fifo", "fifo_user", "kamailio") modparam("mi_fifo", "fifo_group", "kamailio") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # -- cdp params -- modparam("cdp","config_file","/etc/kamailio/icscf.xml") # ----- icscf params ----- # Comment the following line to enable realm routing #!ifdef CXDX_FORCED_PEER modparam("ims_icscf", "cxdx_forced_peer", CXDX_FORCED_PEER) #!endif modparam("ims_icscf","cxdx_dest_realm", NETWORKNAME) # DB-URL, where information about S-CSCF-Server can be found: modparam("ims_icscf","db_url", DB_URL) modparam("ims_icscf","cxdx_dest_realm", NETWORKNAME) #!ifdef PEERING # Route which is executed, in case HSS returned "User-Unknown" on LIR request modparam("ims_icscf","route_lir_user_unknown", "lir_term_user_unknown") #!endif #!ifdef FALLBACK_AUTH # Route which is executed, in case HSS returned "User-Unknown" on UAR request modparam("ims_icscf","route_uar_user_unknown", "uar_term_user_unknown") #!endif #!ifdef WITH_TLS # ----- tls params ----- modparam("tls", "config", "/etc/kamailio/tls.cfg") #!endif #!ifdef WITH_XMLRPC # ----- xmlrpc params ----- modparam("xmlrpc", "route", "XMLRPC"); modparam("xmlrpc", "url_match", "^/RPC") #!endif #!ifdef WITH_DEBUG # ----- debugger params ----- modparam("debugger", "cfgtrace", 1) #!endif #!ifdef CAPTURE_NODE # Destination, where to send the traffic modparam("siptrace", "duplicate_uri", CAPTURE_NODE) # Trace all traffic modparam("siptrace", "trace_on", 1) modparam("siptrace", "trace_to_database", 0) modparam("siptrace", "trace_flag", FLT_CAPTURE) modparam("siptrace", "hep_mode_on", 1) #!endif #!ifdef PEERING # ----- enum params ----- modparam("enum", "domain_suffix", ENUM_SUFFIX) #!endif # ------------------------- request routing logic ------------------- # main routing logic route{ xlog("L_DBG", "$rm ($fu ($si:$sp) to $tu, $ci)\n"); # per request initial checks route(REQINIT); if (is_method("REGISTER")) { route(register); break; } if (is_method("INVITE|SUBSCRIBE|MESSAGE|INFO|PUBLISH|CANCEL")) { route(initial_request); break; } else { # Shouldn't get here unless missconfigured (add more methods as initial) or # somebody is routing unknown messages append_to_reply("Allow: INVITE,SUBSCRIBE,MESSAGE,INFO,PUBLISH,CANCEL\r\n"); t_reply("406","Initial Request Method not allowed at the I-CSCF"); break; } } ###################################################################### # Helper routes (Basic-Checks, NAT-Handling/RTP-Control, XML-RPC) ###################################################################### # Per SIP request initial checks route[REQINIT] { # Trace this message #!ifdef CAPTURE_NODE sip_trace(); setflag(FLT_CAPTURE); #!endif if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if(!sanity_check("1511", "7")) { xlog("Malformed SIP message from $si:$sp\n"); exit; } # Check for shutdown mode: if (!has_totag() && ($sel(cfg_get.system.shutdownmode) > 0)) { send_reply("503", "Server shutting down"); exit; } # Reply to OPTIONS: if (is_method("OPTIONS") && (uri==myself)) { options_reply(); exit; } # Ignore Re-Transmits: if (t_lookup_request()) { exit; } } ###################################################################### # XMLRPC routing ###################################################################### #!ifdef WITH_XMLRPC route[XMLRPC] { # allow XMLRPC from localhost if ((method=="POST" || method=="GET") && (src_ip==127.0.0.1)) { # close connection only for xmlrpclib user agents (there is a bug in # xmlrpclib: it waits for EOF before interpreting the response). if ($hdr(User-Agent) =~ "xmlrpclib") set_reply_close(); set_reply_no_connect(); dispatch_rpc(); exit; } send_reply("403", "Forbidden"); exit; } #!endif ###################################################################### # Handling of REGISTER requests ###################################################################### route[register] { #first check if we have an S-CSCF list if (I_scscf_select("0")) { #there is an S-CSCF list - no need to do a UAR t_on_reply("register_reply"); t_on_failure("register_failure"); if (!t_relay()) { t_reply("500","Error forwarding towards S-CSCF"); break; } break; } else { #no S-CSCF list therefore must do UAR #free this from the failed I_scscf_select call I_scscf_drop(); # Do an asynchronous UAR: I_perform_user_authorization_request("0"); if ($avp(uaa_return_code) == 1) { if (I_scscf_select("0")) { t_on_reply("register_reply"); t_on_failure("register_failure"); if (!t_relay()) { t_reply("500","Error forwarding towards S-CSCF"); break; } break; } else { I_scscf_drop(); t_reply("500", "Server error on UAR select S-CSCF"); break; } } else { t_reply("500", "Server error on UAR select S-CSCF"); break; } } break; } ###################################################################### # Replies to REGISTER requests, ###################################################################### onreply_route[register_reply] { xlog("L_DBG", "Enter register reply block"); if (!t_check_status("(408)|(480)")){ if (!t_check_status("(401)")){ xlog("L_DBG", "dropping scscf list on register failure"); I_scscf_drop(); } else { xlog("L_DBG", "This is a 401 - keep scscf list to do optimisation"); } } break; } ###################################################################### # Failed REGISTERs ###################################################################### failure_route[register_failure] { xlog("L_DBG", "Enter register failure block"); if (t_check_status("(408)|(480)")){ xlog("L_DBG", "Got a failure on register"); if (I_scscf_select("1")) { t_on_reply("register_reply"); t_on_failure("register_failure"); if (!t_relay()) { t_reply("500","Error forwarding towards next S-CSCF"); break; } break; } else { t_reply("500", "Server error on UAR select next S-CSCF"); break; } } else { if (!t_check_status("(401)")){ xlog("L_DBG", "dropping scscf list on register failure"); I_scscf_drop(); } else { xlog("L_DBG", "This is a 401 - keep scscf list to do optimisation"); } } break; } ###################################################################### # Initial requests ###################################################################### route[initial_request] { I_perform_location_information_request("0"); if ($avp(lia_return_code) == 1) { if (I_scscf_select("0")) { append_branch(); xlog("L_DBG", "ru = $ru, du = $du\n"); t_on_reply("initial_request_reply"); t_on_failure("initial_request_failure"); if (!t_relay()) { t_reply("500","Error forwarding towards S-CSCF"); break; } break; } else { xlog("L_DBG", "dropping scscf list on initial request"); I_scscf_drop(); t_reply("500", "Server error on LIR select S-CSCF"); break; } } else { t_reply("500", "Server error on LIR"); break; } break; } ###################################################################### # Replies to initial requests ###################################################################### onreply_route[initial_request_reply] { xlog("L_DBG", "Enter initial request request block"); if (!t_check_status("(408)")){ xlog("L_DBG", "dropping scscf list on initial request reply"); I_scscf_drop(); } break; } ###################################################################### # Failed initial requests ###################################################################### failure_route[initial_request_failure] { xlog("L_DBG", "Enter initial request failure block"); if (t_check_status("(408)")){ xlog("L_DBG", "Got a failure for initial request"); if (I_scscf_select("1")) { t_on_reply("initial_request_reply"); t_on_failure("initial_request_failure"); if (!t_relay()) { t_reply("500","Error forwarding towards next S-CSCF"); break; } break; } else { t_reply("500", "Server error on LIR select next S-CSCF"); break; } } else { xlog("L_DBG", "dropping scscf list on initial request failure"); I_scscf_drop(); } break; } #!ifdef PEERING ###################################################################### # HSS returned "User-Unknown" on LIR request ###################################################################### route[lir_term_user_unknown] { if (uri =~ "tel:.*") { # Let's check, if the number can be found in ENUM: if(!enum_query()) { # ENUM failed, send it to the PSTN-Gateway: route(PSTN); break; } # ENUM resolved to another domain if ($rd != NETWORKNAME) { t_on_reply("initial_request_reply"); t_on_failure("initial_request_failure"); if (!t_relay()) { t_reply("500","Error forwarding to external domain"); exit; }; exit; } else { t_reply("604","Does not exist anywhere - HSS User Unknown"); exit; }; } else { # we received a request for our domain (non-tel), but HSS said "User Unknown" if ($rd != NETWORKNAME) { t_reply("604","Does not exist anywhere - HSS User Unknown"); exit; } else { # try to forward non-tel request to other domain t_on_reply("Initial_Request_reply"); t_on_failure("Initial_Request_failure"); if (!t_relay()) { t_reply("500","Error forwarding to external domain"); exit; }; exit; }; }; } } ###################################################################### # Send calls to the PSTN-Gateways: ###################################################################### route[PSTN] { t_on_failure("PSTN_failure"); # Relay the request towards the PSTN-Gateway: if (!ds_select_dst("1", "4")) { send_reply("503", "Service not available"); exit; } # Relay the request: if (!t_relay()) { send_reply("503", "Service not available"); exit; }; exit; } ###################################################################### # manage failure routing cases, perform failover ###################################################################### failure_route[PSTN_failure] { # Choose another gateway, in case we # - get a local generated "408" # - receive a 5xx or 6xx reply from the proxy. if (t_branch_timeout() || t_check_status("[5-6]..")) { if (ds_next_dst()) { # Do Failover in case problems: t_on_failure("PSTN_failure"); # Relay the request: if (!t_relay()) { send_reply("503", "Service not available"); exit; }; } else { # Add a header, to indicate the phone should try again in 30 seconds. append_hf("Retry-After: 30\r\n"); send_reply("503", "Service not available"); } exit; } } #!endif #!ifdef FALLBACK_AUTH ###################################################################### # HSS returned "User-Unknown" on UAR request, # try to send it to any S-CSCF for authentication ###################################################################### route[uar_term_user_unknown] { $rd = "scscf."+NETWORKNAME; t_on_reply("register_reply"); t_on_failure("register_failure"); if (!t_relay()) { t_reply("500","Error forwarding towards S-CSCF"); break; } break; } #!endif kamailio-4.0.4/examples/icscf/icscf.mysql.sql0000644000000000000000000001061112223032460017655 0ustar rootroot-- MySQL dump 10.9 -- -- Host: localhost Database: icscf -- ------------------------------------------------------ -- Server version 4.1.20-log /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Current Database: `icscf` -- /*!40000 DROP DATABASE IF EXISTS `icscf`*/; CREATE DATABASE /*!32312 IF NOT EXISTS*/ `icscf` /*!40100 DEFAULT CHARACTER SET utf8 */; USE `icscf`; -- -- Table structure for table `nds_trusted_domains` -- DROP TABLE IF EXISTS `nds_trusted_domains`; CREATE TABLE `nds_trusted_domains` ( `id` int(11) NOT NULL auto_increment, `trusted_domain` varchar(83) NOT NULL default '', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- -- Table structure for table `s_cscf` -- DROP TABLE IF EXISTS `s_cscf`; CREATE TABLE `s_cscf` ( `id` int(11) NOT NULL auto_increment, `name` varchar(83) NOT NULL default '', `s_cscf_uri` varchar(83) NOT NULL default '', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- -- Table structure for table `s_cscf_capabilities` -- DROP TABLE IF EXISTS `s_cscf_capabilities`; CREATE TABLE `s_cscf_capabilities` ( `id` int(11) NOT NULL auto_increment, `id_s_cscf` int(11) NOT NULL default '0', `capability` int(11) NOT NULL default '0', PRIMARY KEY (`id`), KEY `idx_capability` (`capability`), KEY `idx_id_s_cscf` (`id_s_cscf`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- MySQL dump 10.9 -- -- Host: localhost Database: icscf -- ------------------------------------------------------ -- Server version 4.1.20-log /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Current Database: `icscf` -- CREATE DATABASE /*!32312 IF NOT EXISTS*/ `icscf` /*!40100 DEFAULT CHARACTER SET utf8 */; USE `icscf`; -- -- Dumping data for table `nds_trusted_domains` -- /*!40000 ALTER TABLE `nds_trusted_domains` DISABLE KEYS */; LOCK TABLES `nds_trusted_domains` WRITE; INSERT INTO `nds_trusted_domains` VALUES (1,'intern.ng-voice.com'); UNLOCK TABLES; /*!40000 ALTER TABLE `nds_trusted_domains` ENABLE KEYS */; -- -- Dumping data for table `s_cscf` -- /*!40000 ALTER TABLE `s_cscf` DISABLE KEYS */; LOCK TABLES `s_cscf` WRITE; INSERT INTO `s_cscf` VALUES (1,'First and only S-CSCF','sip:scscf.intern.ng-voice.com:5060'); UNLOCK TABLES; /*!40000 ALTER TABLE `s_cscf` ENABLE KEYS */; -- -- Dumping data for table `s_cscf_capabilities` -- /*!40000 ALTER TABLE `s_cscf_capabilities` DISABLE KEYS */; LOCK TABLES `s_cscf_capabilities` WRITE; INSERT INTO `s_cscf_capabilities` VALUES (1,1,0),(2,1,1); UNLOCK TABLES; /*!40000 ALTER TABLE `s_cscf_capabilities` ENABLE KEYS */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; # DB access rights grant delete,insert,select,update on icscf.* to icscf@192.168.178.210 identified by 'heslo'; /* grant delete,insert,select,update on icscf.* to provisioning@localhost identified by 'provi'; */ kamailio-4.0.4/examples/scscf/0000755000000000000000000000000012223032461014715 5ustar rootrootkamailio-4.0.4/examples/scscf/scscf.cfg0000644000000000000000000000311712223032461016501 0ustar rootroot# IP-Adress for incoming SIP-Traffic, in the following format: #!define NETWORK_INTERFACE "192.168.0.3" # Port, where we listen to Traffic #!define PORT 5060 #!define NETWORKNAME "kamailio-ims.org" #!define NETWORKNAME_ESC "kamailio-ims\.org" #!define HOSTNAME "scscf.kamailio-ims.org" #!define URI "sip:scscf.kamailio-ims.org:5060" #!define HOSTNAME_ESC "scscf\.kamailio-ims\.org" # ENUM-Server to query: #!define ENUM_SUFFIX "ng-voice.com." # SIP-Address of capturing node, if not set, capturing is disabled. ##!define CAPTURE_NODE "sip:10.0.6.1" # Select Authorization Algorhithm: ##!define REG_AUTH_DEFAULT_ALG "AKAv1-MD5" ##!define REG_AUTH_DEFAULT_ALG "AKAv2-MD5" #!define REG_AUTH_DEFAULT_ALG "MD5" ##!define REG_AUTH_DEFAULT_ALG "CableLabs-Digest" ##!define REG_AUTH_DEFAULT_ALG "3GPP-Digest" ##!define REG_AUTH_DEFAULT_ALG "TISPAN-HTTP_DIGEST_MD5" # Let the HSS decide ##!define REG_AUTH_DEFAULT_ALG "HSS-Selected" # Several features can be enabled using '#!define WITH_FEATURE' directives: # # *** To run in debug mode: # - define WITH_DEBUG # # *** To enable XMLRPC support execute: # - define WITH_XMLRPC # - adjust route[XMLRPC] for access policy # # *** To enable basic dialplan support: # - define WITH_DIALPLAN # - adjust dialplan in the database # # *** To enable the Ro-Interface: # - Configure Ro-Diameter-Interface in scscf.xml # - define WITH_RO # # *** To enable a Homer SIP-Capter-Node: # - define CAPTURE_NODE with a proper address # # Enabled Features for this host: ##!define WITH_DEBUG #!define WITH_XMLRPC ##!define WITH_RO #!define WITH_DIALPLAN kamailio-4.0.4/examples/scscf/dispatcher.list0000644000000000000000000000011412223032460017733 0ustar rootroot# Interconnection Gateways 1 sip:sbc-1.ng-voice.com 1 sip:sbc-2.ng-voice.comkamailio-4.0.4/examples/scscf/scscf.xml0000644000000000000000000000127512223032460016544 0ustar rootroot kamailio-4.0.4/examples/scscf/kamailio.cfg0000644000000000000000000005030112223032461017163 0ustar rootroot#!KAMAILIO # # This config file implements the basic P-CSCF functionality # - web: http://www.kamailio.org # - git: http://sip-router.org # # Refer to the Core CookBook at http://www.kamailio.org/dokuwiki/doku.php # for an explanation of possible statements, functions and parameters. # # Direct your questions about this file to: . # # For more information about the various parameters, functions and statements # try http://sip-router.org/wiki/ . # include_file "scscf.cfg" ####### Defined Values ######### # *** Value defines - IDs used later in config # - flags # FLT_ - per transaction (message) flags # FLB_ - per branch flags #!define FLT_CAPTURE 1 #!define FLT_DIALOG 2 #!define DLG_TIMEOUT_AVP "i:1" #!define RR_CUSTOM_USER_AVP "i:2" #!define DISPATCHER_DST_AVP "i:3" #!define DISPATCHER_GRP_AVP "i:4" #!define DISPATCHER_CNT_AVP "i:5" system.shutdownmode = 0 desc "System shutdown mode" ####### Global Parameters ######### #!ifdef WITH_DEBUG debug=5 log_stderror=no sip_warning=yes #!else debug=2 log_stderror=no sip_warning=no #!endif /* uncomment and configure the following line if you want Kamailio to bind on a specific interface/port/proto (default bind on all available) */ #!ifdef NETWORK_INTERFACE listen=NETWORK_INTERFACE #!endif /* port to listen to * - can be specified more than once if needed to listen on many ports */ port=PORT alias=HOSTNAME user_agent_header="User-Agent: Kamailio S-CSCF" server_header="Server: Kamailio S-CSCF" /* comment the next line to enable the auto discovery of local aliases based on reverse DNS on IPs (default on) */ auto_aliases=no check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) # Do SRV-Loadbalancing: dns_srv_lb=yes # Always: Also try IPv6: dns_try_ipv6=yes # Try onle IPv6: dns_cache_flags=6 #!ifdef WITH_TLS #!define WITH_TCP enable_tls=yes #!endif #!ifdef WITH_TCP # life time of TCP connection when there is no traffic # - a bit higher than registration expires to cope with UA behind NAT tcp_connection_lifetime=3615 #!else disable_tcp=yes #!endif children=64 # ------------------ module loading ---------------------------------- mpath="/usr/lib64/kamailio/modules_k/:/usr/lib64/kamailio/modules/:/usr/lib/kamailio/modules_k/:/usr/lib/kamailio/modules/" # (we try both the lib64 and the lib directory) loadmodule "tm.so" loadmodule "pv.so" loadmodule "sl.so" loadmodule "rr.so" loadmodule "dialog_ng.so" loadmodule "textops.so" loadmodule "maxfwd.so" loadmodule "xlog.so" loadmodule "sanity.so" loadmodule "siputils.so" loadmodule "kex.so" loadmodule "dispatcher" loadmodule "enum" loadmodule "uac" # Control interfaces: loadmodule "ctl" loadmodule "cfg_rpc" loadmodule "mi_rpc" loadmodule "mi_fifo" #!ifdef WITH_XMLRPC loadmodule "xmlrpc" #!endif loadmodule "cdp.so" loadmodule "cdp_avp.so" #!ifdef WITH_RO loadmodule "ims_diameter_ro.so" #!endif loadmodule "ims_usrloc_scscf.so" loadmodule "ims_registrar_scscf.so" loadmodule "ims_auth.so" loadmodule "ims_isc.so" #!ifdef CAPTURE_NODE loadmodule "siptrace.so" #!endif #!ifdef WITH_DEBUG loadmodule "debugger.so" #!endif #!ifdef WITH_DIALPLAN loadmodule "db_text.so" loadmodule "dialplan.so" #!endif # ----------------- setting module-specific parameters --------------- # ----- mi_fifo params ----- modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") modparam("mi_fifo", "fifo_mode", 0666) modparam("mi_fifo", "fifo_user", "kamailio") modparam("mi_fifo", "fifo_group", "kamailio") #!ifdef WITH_DIALPLAN # ----- db_text params ----- # Non-Caching-Mode, Caching is done by the dialplan module modparam("db_text", "db_mode", 1) # ----- dialplan params ----- # Comment the following line to enable realm routing # Database files can be found in CFG_DIR/db (e.g. /etc/kamailio/db/) modparam("dialplan","db_url", "text:///etc/kamailio/db/") #!endif # ----- mi_fifo params ----- modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") modparam("mi_fifo", "fifo_mode", 0666) modparam("mi_fifo", "fifo_user", "kamailio") modparam("mi_fifo", "fifo_group", "kamailio") # ----- tm params ----- # auto-discard branches from previous serial forking leg modparam("tm", "failure_reply_mode", 3) # default retransmission timeout: 10sec modparam("tm", "fr_timer", 10000) # default invite retransmission timeout after 1xx: 120sec modparam("tm", "fr_inv_timer", 120000) # Don't reply automatically with "100 Trying" modparam("tm", "auto_inv_100", 1) # ----- rr params ----- # add value to ;lr param to cope with most of the UAs modparam("rr", "enable_full_lr", 1) # append from tag to the RR modparam("rr", "append_fromtag", 1) # add a Username to RR-Header modparam("rr", "add_username", 1) # Take User from a custom AVP modparam("rr", "custom_user_avp", "$avp(RR_CUSTOM_USER_AVP)") # -- usrloc params -- modparam("ims_usrloc_scscf", "enable_debug_file", 1) modparam("ims_usrloc_scscf", "usrloc_debug_file", "/var/log/usrloc_debug") modparam("ims_usrloc_scscf", "matching_mode", 0) # -- CDP params -- modparam("cdp","config_file","/etc/kamailio/scscf.xml") # -- dialog_ng params -- modparam("dialog_ng", "dlg_flag", FLT_DIALOG) modparam("dialog_ng", "timeout_avp", "$avp(DLG_TIMEOUT_AVP)") modparam("dialog_ng", "detect_spirals", 1) modparam("dialog_ng", "profiles_no_value", "orig ; term") #!ifdef WITH_XMLRPC # ----- xmlrpc params ----- modparam("xmlrpc", "route", "XMLRPC"); modparam("xmlrpc", "url_match", "^/RPC") #!endif #!ifdef WITH_DEBUG # ----- debugger params ----- modparam("debugger", "cfgtrace", 1) #!endif #!ifdef CAPTURE_NODE # Destination, where to send the traffic modparam("siptrace", "duplicate_uri", CAPTURE_NODE) # Trace all traffic modparam("siptrace", "trace_on", 1) modparam("siptrace", "trace_to_database", 0) modparam("siptrace", "trace_flag", FLT_CAPTURE) modparam("siptrace", "hep_mode_on", 1) #!endif # -- ims_auth params -- modparam("ims_auth", "name", URI) modparam("ims_auth", "registration_default_algorithm", REG_AUTH_DEFAULT_ALG) modparam("ims_auth","ignore_failed_auth",1) #!ifdef CXDX_FORCED_PEER modparam("ims_auth", "cxdx_forced_peer", CXDX_FORCED_PEER) #!endif modparam("ims_auth", "cxdx_dest_realm", NETWORKNAME) # -- ims_registrar_scscf params -- #!ifdef WITH_DEBUG modparam("ims_registrar_scscf", "default_expires", 60) modparam("ims_registrar_scscf", "min_expires", 60) modparam("ims_registrar_scscf", "max_expires", 60) #!else modparam("ims_registrar_scscf", "default_expires", 604800) modparam("ims_registrar_scscf", "min_expires", 3600) modparam("ims_registrar_scscf", "max_expires", 604800) #!endif modparam("ims_registrar_scscf", "use_path", 1) modparam("ims_registrar_scscf", "support_wildcardPSI",1) modparam("ims_registrar_scscf", "user_data_xsd","/etc/kamailio/CxDataType_Rel7.xsd") modparam("ims_registrar_scscf", "scscf_name", URI) modparam("ims_registrar_scscf", "cxdx_dest_realm", NETWORKNAME) # ----- ims_isc params ----- modparam("ims_isc", "my_uri", HOSTNAME) #!ifdef WITH_RO # ----- ims_diameter_ro params ----- modparam("ims_diameter_ro", "ro_origin_host", HOSTNAME); #!ifdef RO_FORCED_PEER modparam("ims_diameter_ro", "ro_forced_peer", RO_FORCED_PEER); #!endif modparam("ims_diameter_ro", "ro_dest_realm", NETWORKNAME); modparam("ims_diameter_ro","interim_request_credits",30); modparam("ims_diameter_ro","timer_buffer",5); #!endif # ----- enum params ----- modparam("enum", "domain_suffix", ENUM_SUFFIX) # ----- sanity params ----- modparam("sanity", "autodrop", 0) # ----------------- Settings for Dispatcher --------------- modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list") # Dispatcher: Enable Failover-Support modparam("dispatcher", "flags", 2) # Dispatcher: Overwrite Destination address, if required. modparam("dispatcher", "force_dst", 1) # AVP's required for Fail-Over-Support: modparam("dispatcher", "dst_avp", "$avp(DISPATCHER_DST_AVP)") modparam("dispatcher", "grp_avp", "$avp(DISPATCHER_GRP_AVP)") modparam("dispatcher", "cnt_avp", "$avp(DISPATCHER_CNT_AVP)") # Try to recover disabled destinations every 15 seconds. modparam("dispatcher", "ds_ping_interval", 15) # Actively query the gateways: modparam("dispatcher", "ds_probing_mode", 1) ####### Routing Logic ######## # Main SIP request routing logic # - processing of any incoming SIP request starts with this route route { #!ifdef WITH_DEBUG xlog("L_ERR", "$rm ($fu ($si:$sp) to $tu, $ci)\n"); #!endif # per request initial checks route(REQINIT); # Handle Registrations: if (is_method("REGISTER")) { route(REGISTER); exit; } # we need to support subscription to reg event if (is_method("SUBSCRIBE") && search("^(Event|o)([ \t]*):([ \t]*)reg")) { route(subscribe); break; } #Set DLG flag to track dialogs using dialog2 if (!is_method("REGISTER|SUBSCRIBE")) setflag(FLT_DIALOG); # Evaluate Route-Header and set $route_uri loose_route(); if (is_method("CANCEL|ACK")) { t_relay(); exit; } if (($route_uri =~ "sip:orig@"+HOSTNAME_ESC+".*") || isc_from_as("orig")) { # we need something like this to assign SCSCF to unregistered user for services # support for AS origination on behalf of unregistered useri # can use the registrar is_registered methods - must see if we need to check orig or term? # Originating route(orig); break; } else { isc_from_as("term"); if ($retcode == -2) { # Treat as originating, since it was retargeted: route(orig); break; } if ((is_in_profile("orig") || has_totag()) && ($route_uri =~ "sip:mo@"+".*")) { route(orig_subsequent); break; } if ((is_in_profile("term") || has_totag()) && ($route_uri =~ "sip:mt@"+".*")) { route(term_subsequent); break; } # Terminating if (uri=~"sip:(.*)@"+NETWORKNAME_ESC +"(.*)" || uri=~"tel:.*") { if (!term_impu_registered("location")) { xlog("L_ERR", "We need to do an UNREG server SAR assignemnt"); assign_server_unreg("location", "term"); } } else { sl_send_reply("403","Forbidden - Dialog not found on S-CSCF or Terminating user not suitable for unregistered services"); exit(); } route(term); break; } } ###################################################################### # Helper routes (Basic-Checks, NAT-Handling/RTP-Control, XML-RPC) ###################################################################### # Per SIP request initial checks route[REQINIT] { # Trace this message #!ifdef CAPTURE_NODE sip_trace(); setflag(FLT_CAPTURE); #!endif if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if(!sanity_check("1511", "7")) { xlog("Malformed SIP message from $si:$sp\n"); exit; } # Check for shutdown mode: if (!has_totag() && ($sel(cfg_get.system.shutdownmode) > 0)) { send_reply("503", "Server shutting down"); exit; } # Reply to OPTIONS: if (is_method("OPTIONS") && (uri==myself)) { options_reply(); exit; } # Ignore Re-Transmits: if (t_lookup_request()) { exit; } } ###################################################################### # Handle Subscribes to the REG-INFO event ###################################################################### route[subscribe] { #we need to deal with subs to reg event! # handle_subscribe(); sl_send_reply("403", "Forbidden... for now"); exit; } ###################################################################### # XMLRPC routing ###################################################################### #!ifdef WITH_XMLRPC route[XMLRPC] { # allow XMLRPC from localhost if ((method=="POST" || method=="GET") && (src_ip==127.0.0.1)) { # close connection only for xmlrpclib user agents (there is a bug in # xmlrpclib: it waits for EOF before interpreting the response). if ($hdr(User-Agent) =~ "xmlrpclib") set_reply_close(); set_reply_no_connect(); dispatch_rpc(); exit; } send_reply("403", "Forbidden"); exit; } #!endif ###################################################################### # Route for handling Registrations: ###################################################################### route[REGISTER] { xlog("L_ERR", "Enter register block"); t_newtran(); ims_www_authenticate(NETWORKNAME); #check to see if user is authenticated - ie sip header has auth information - (already challenged) if ($avp(maa_return_code) == 1) { # user has not been authenticated. Lets send a challenge via 401 Unauthorized ims_www_challenge("$td"); exit; } else { # We need to check if this user is registered or not if (!impu_registered("location")) { save("location"); if ($avp(saa_return_code) == 1) { isc_match_filter_reg("0","location"); exit; } } else { save("location"); if($avp(saa_return_code) == 1) { isc_match_filter_reg("1","location"); exit; } } } } ###################################################################### # Apply privacy, if requested ###################################################################### route[apply_privacy] { if (is_present_hf("Privacy") && ($hdr(Privacy)=="id")) { remove_hf("P-Asserted-Identity"); } } ###################################################################### # Originating, Intial Requests ###################################################################### route[orig] { xlog("L_DBG","Enter orig route\n"); set_dlg_profile("orig"); # we MAYBE need something like this to check if a user is barred # if (S_originating_barred()){ # sl_send_reply("403","Forbidden - Originating Public Identity barred"); # exit; # } if (is_method("INVITE|SUBSCRIBE")) { $avp(RR_CUSTOM_USER_AVP)="mo"; record_route(); } #!ifdef WITH_RO # before we allow call - lets check credit if (is_method("INVITE")) { xlog("L_DBG","Sending initial CCR Request for call\n"); $var(cc_ret) = Ro_send_ccr(10); if ($var(cc_ret) < 0) { xlog("L_DBG","CCR Request failure\n"); sl_send_reply("402","Payment required"); exit; } xlog("L_DBG","CCR Request success\n"); } #!endif # check if dialog saved as fwded to AS if (isc_match_filter("orig", "location")) { t_on_failure("isc_orig_failure"); xlog("Orig - msg was fwded to AS\n"); exit; } if (!isc_from_as("orig")) { remove_hf("P-Asserted-Identity"); append_hf("P-Asserted-Identity: \r\n"); } # Check for PSTN destinations: if (is_method("INVITE")) { route(PSTN_handling); } t_on_reply("orig_reply"); t_relay(); } ###################################################################### # Replies to the Initial Requests ###################################################################### onreply_route[orig_reply] { xlog("L_DBG","Orig reply\n"); route(apply_privacy); #!ifdef WITH_RO # before we allow call - lets check credit if (t_check_status("183")){ xlog("L_DBG","Sending initial CCR Request for call\n"); $var(cc_ret) = Ro_send_ccr(10); commented by Jason 5/4/2012 if ($var(cc_ret) < 0) { xlog("L_DBG","CCR Request failure\n"); dlg_terminate("all", "Sorry no funds available"); } xlog("L_DBG", "IMS: Received 183 inside orig_initial_reply\n"); } #!endif break; } ###################################################################### # Originating, subsequent requests ###################################################################### route[orig_subsequent] { xlog("L_DBG","Orig_Subsequent\n"); if (!is_method("ACK")) { t_on_reply("orig_subsequent_reply"); } t_relay(); } ###################################################################### # Replies for originating, subsequent requests ###################################################################### onreply_route[orig_subsequent_reply] { xlog("L_DBG","Orig_Subsequent_reply\n"); route(apply_privacy); break; } ###################################################################### # Failure-Route for Requests to an AS ###################################################################### failure_route[isc_orig_failure] { xlog("L_DBG","ISC_Orig_failure\n"); if (t_check_status("(408)|(5..)")){ t_on_failure("isc_orig_failure"); if (isc_match_filter("orig","location")){ xlog("L_DBG","ISC_Orig_failure - msg was fwded to AS\n"); exit; } if (isc_from_as("origfail")) { remove_hf("P-Asserted-Identity"); append_hf("P-Asserted-Identity: \r\n"); } t_on_reply("orig_reply"); t_relay(); } } ###################################################################### # Terminating requests ###################################################################### route[term] { xlog("L_DBG","Term\n"); set_dlg_profile("term"); #we need something like this to check if a user is barred # if (S_terminating_barred()){ # sl_send_reply("404","Not Found - Terminating user barred"); # exit; # } if (is_method("INVITE|SUBSCRIBE")) { $avp(RR_CUSTOM_USER_AVP)="mt"; $avp(i:20)="mt"; record_route(); } # check if dialog saved as fwded to AS if (isc_match_filter("term","location")){ t_on_failure("isc_term_failure"); xlog("L_DBG","Term - msg was fwded to AS\n"); exit; } if (lookup("location")) { if (uri=~"sip:(.*)@"+NETWORKNAME_ESC+"(.*)") { if (!t_newtran()) { sl_reply_error(); exit; } t_reply("404","Not Found - destination user not found on this S-CSCF"); exit; } } else { # User not registered? Reply with 404. if (!t_newtran()) { sl_reply_error(); exit; } t_reply("404","Not Found - destination user not found on this S-CSCF"); exit; } route(apply_privacy); t_relay(); } ###################################################################### # Failure Route for Terminating requests ###################################################################### failure_route[isc_term_failure] { xlog("L_DBG","ISC_term_failure\n"); if (t_check_status("(408)|(5..)")){ t_on_failure("isc_term_failure"); if (isc_match_filter("term","location")){ xlog("L_DBG","Term - msg was fwded to AS\n"); exit; } if (lookup("location")) { if (uri=~"sip:(.*)"+NETWORKNAME_ESC+"(.*)"){ t_reply("404","Not Found - destination user not found on this S-CSCF"); exit; } } else { t_reply("404","Not Found - destination user not found on this S-CSCF"); exit; } t_relay(); } } ###################################################################### # Terminating, subsequent requests ###################################################################### route[term_subsequent] { xlog("L_DBG","term_subsequent\n"); route(apply_privacy); t_relay(); } ###################################################################### # Check for PSTN destinations: ###################################################################### route[PSTN_handling] { # First, we translate "tel:"-URI's to SIP-URI's: # $ru: tel:+(34)-999-888-777 # $fu: sip:test@foo.com # becomes $ru: sip:+34999888777@foo.com;user=phone if (!tel2sip("$ru", "$fd", "$ru")) xlog("L_WARN","Failed to convert $ru to a sip:-URI - M=$rm R=$ru F=$fu T=$tu IP=$si:$sp ID=$ci\n\n"); #!ifdef WITH_DIALPLAN # Check for numeric Request-Username if ($rU =~ "[0-9]+") { if (dp_translate("1")) { xlog("L_INFO", "R-URI rewritten to $rU - M=$rm R=$ru F=$fu T=$tu IP=$si:$sp ID=$ci\n"); } } #!endif if ($rU =~ "\+[0-9]+") { # Now let's check, if the number can be found in ENUM: if(!enum_query()) { # ENUM failed, send it to the PSTN-Gateway: route(PSTN); break; } } } ###################################################################### # Send calls to the PSTN-Gateways: ###################################################################### route[PSTN] { t_on_failure("PSTN_failure"); # Relay the request towards the PSTN-Gateway: if (!ds_select_dst("1", "4")) { send_reply("503", "Service not available"); exit; } # Relay the request: t_relay(); exit; } ###################################################################### # manage failure routing cases, perform failover ###################################################################### failure_route[PSTN_failure] { # Choose another gateway, in case we # - get a local generated "408" # - receive a 5xx or 6xx reply from the proxy. if (t_branch_timeout() || t_check_status("[5-6]..")) { if (ds_next_dst()) { # Do Failover in case problems: t_on_failure("PSTN_failure"); t_relay(); } else { # Add a header, to indicate the phone should try again in 30 seconds. append_hf("Retry-After: 30\r\n"); send_reply("503", "Service not available"); } exit; } } kamailio-4.0.4/examples/scscf/db/0000755000000000000000000000000012223032460015301 5ustar rootrootkamailio-4.0.4/examples/scscf/db/dialplan0000644000000000000000000000052112223032460017006 0ustar rootrootid(int,auto) dpid(int) pr(int) match_op(int) match_exp(string) match_len(int) subst_exp(string) repl_exp(string) attrs(string) 1:1:4:1:^00([1-9][0-9]+)$:0:^00([1-9][0-9]+)$:+\\1:"" 2:1:3:1:^0([1-9][0-9]+)$:0:^0([1-9][0-9]+)$:+49\\1:"" 3:1:2:1:^11([0-9]+)$:0:^11([0-9]+)$:+4911\\1:"" 4:1:1:1:^([1-9][0-9]+)$:0:^([1-9][0-9]+)$:+4940\\1:"" kamailio-4.0.4/examples/scscf/db/version0000644000000000000000000000006212223032460016707 0ustar rootroottable_name(string) table_version(int) dialplan:1 kamailio-4.0.4/examples/scscf/CxDataType_Rel7.xsd0000644000000000000000000003022012223032460020330 0ustar rootroot iFC is part of the registered profile iFC is part of the unregistered profile Matches to REGISTER messages that are related to initial registration Matches to REGISTER messages that are related to re-registration Matches to REGISTER messages that are related to de-registration Session Continued Session Terminated Originating Session Terminating Session for registered user Terminating Session for unregistered user Originating Session for an unregistered user Identity is a Public User Identity. Identity is a distinct Public Service Identity. Identity matches a wildcarded Public Service Identity. kamailio-4.0.4/examples/scscf/CxDataType_Rel6.xsd0000644000000000000000000002710112223032460020333 0ustar rootroot iFC is part of the registered profile iFC is part of the unregistered profile Matches to REGISTER messages that are related to initial registration Matches to REGISTER messages that are related to re-registration Matches to REGISTER messages that are related to de-registration Session Continued Session Terminated Originating Session Terminating Session for registered user Terminating Session for unregistered user Identity is a Public User Identity. Identity is a distinct Public Service Identity. Identity matches a wildcarded Public Service Identity. kamailio-4.0.4/examples/exec.cfg0000644000000000000000000000164112223032460015222 0ustar rootroot# # $Id$ # # this example shows use of ser as stateless redirect server # which rewrites URIs using an exernal utility # # ------------------ module loading ---------------------------------- loadmodule "modules/exec/exec.so" loadmodule "modules/sl/sl.so" # ------------------------- request routing logic ------------------- # main routing logic route{ # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { log("REGISTER"); sl_send_reply("200", "ok"); break; }; # first dump the message to a file using cat command exec_msg("printenv SRCIP > /tmp/exectest.txt; cat >> /tmp/exectest.txt"); # and then rewrite URI using external utility # note that the last echo command trashes input parameter if (exec_dset("echo sip:mra@iptel.org;echo sip:mrb@iptel.org;echo>/dev/null")) { sl_send_reply("300", "Redirect"); } else { sl_reply_error(); log(1, "alas, rewriting failed\n"); }; } kamailio-4.0.4/examples/voicemail.cfg0000644000000000000000000000506112223032460016246 0ustar rootroot# # $Id$ # # this script is configured for use as voicemail UAS; it processes # INVITEs and BYEs and asks SEMS to record media via "vm"; in this # script, all record-routing and other constructs known from proxy # scripts are not present -- it is a simple UAS # # ----------- global configuration parameters ------------------------ #debug= # debug level (cmd line: -dddddddddd) #fork=no #log_stderror=yes # (cmd line: -E) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5090 children=4 fifo="/tmp/vm_ser_fifo" # ------------------ module loading ---------------------------------- loadmodule "/home/srouter/sip_router/modules/sl/sl.so" loadmodule "/home/srouter/sip_router/modules/tm/tm.so" loadmodule "/home/srouter/sip_router/modules/maxfwd/maxfwd.so" loadmodule "/home/srouter/sip_router/modules/mysql/mysql.so" loadmodule "/home/srouter/sip_router/modules/vm/vm.so" # ----------------- setting module-specific parameters --------------- modparam("vm", "db_url","mysql://ser:heslo@dbhost/ser") # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwars==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (len_gt( max_len )) { sl_send_reply("513", "Message too big"); break; }; if (!uri==myself) { sl_send_reply("404", "not reponsible for host in r-uri"); break; }; # Voicemail specific configuration - begin if(method=="ACK" || method=="INVITE" || method=="BYE"){ if (!t_newtran()) { log("could not create new transaction\n"); sl_send_reply("500","could not create new transaction"); break; }; t_reply("100","Trying -- just wait a minute !"); if(method=="INVITE"){ log("**************** vm start - begin ******************\n"); if(!vm("/tmp/am_fifo","voicemail")){ log("could not contact the answer machine\n"); t_reply("500","could not contact the answer machine"); }; log("**************** vm start - end ******************\n"); } else if(method=="BYE"){ log("**************** vm end - begin ******************\n"); if(!vm("/tmp/am_fifo","bye")){ log("could not contact the answer machine\n"); t_reply("500","could not contact the answer machine"); }; log("**************** vm end - end ******************\n"); }; break; }; if (method=="CANCEL") { sl_send_reply("200", "cancels are junked here"); break; }; sl_send_reply("501", "method not understood here"); } kamailio-4.0.4/examples/exec_s5.cfg0000644000000000000000000000277712223032460015644 0ustar rootroot# # $Id$ # # simple quick-start config script # fork=no log_stderror=yes # ----------- global configuration parameters ------------------------ loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/exec/exec.so" # ----------------- setting module-specific parameters --------------- route{ # uri for my domain ? if (uri==myself) { if (method=="REGISTER") { save("location"); break; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { # proceed to email notification if (method=="INVITE") route(1) else sl_send_reply("404", "Not Found"); break; }; }; # user found, forward to his current uri now if (!t_relay()) { sl_reply_error(); }; } /* handling of missed calls */ route[1] { # don't continue if it is a retransmission if ( !t_newtran()) { sl_reply_error(); break; }; # external script: lookup user, if user exists, send # an email notification to him if (!exec_msg(' QUERY="select email_address from subscriber where user=\"$SIP_OUSER\""; EMAIL=`mysql -Bsuser -pheslo -e "$QUERY" ser`; if [ -z "$EMAIL" ] ; then exit 1; fi ; echo "SIP request received from $SIP_HF_FROM for $SIP_OUSER" | mail -s "request for you" $EMAIL ')) { # exec returned error ... user does not exist # send a stateful reply t_reply("404", "User does not exist"); } else { t_reply("600", "No messages for this user"); }; break; } kamailio-4.0.4/examples/exec_s4.cfg0000644000000000000000000000167212223032460015634 0ustar rootroot# # $Id$ # # email notification to email address from mysql database # fork=no # ------------------ module loading ---------------------------------- loadmodule "modules/exec/exec.so" loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" # send email if a request arrives; process statefully # to avoid multiple execution on request retransmissions route[0] { # stop script processing if transaction exists if ( !t_newtran()) { sl_reply_error(); break; }; if (!exec_msg(' QUERY="select email_address from subscriber where user=\"$SIP_OUSER\""; EMAIL=`mysql -Bsuser -pheslo -e "$QUERY" ser`; if [ -z "$EMAIL" ] ; then exit 1; fi ; echo "SIP request received from $SIP_HF_FROM for $SIP_OUSER" | mail -s "request for you" $EMAIL ')) { # exec returned error ... user does not exist # send a stateful reply t_reply("404", "User does not exist"); } else { t_reply("600", "No messages for this user"); }; } kamailio-4.0.4/examples/flag_reply.cfg0000644000000000000000000000431512223032460016423 0ustar rootroot# # $Id$ # # simple quick-start config script # # ----------- global configuration parameters ------------------------ debug=3 # debug level (cmd line: -dddddddddd) fork=yes log_stderror=no # (cmd line: -E) fork=no log_stderror=yes check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) children=4 fifo="/tmp/ser_fifo" listen=195.37.77.100 port=5068 # ------------------ module loading ---------------------------------- # Uncomment this if you want to use SQL database #loadmodule "/usr/local/lib/ser/modules/mysql.so" loadmodule "/usr/local/lib/ser/modules/sl.so" loadmodule "/usr/local/lib/ser/modules/tm.so" loadmodule "/usr/local/lib/ser/modules/rr.so" loadmodule "/usr/local/lib/ser/modules/maxfwd.so" loadmodule "/usr/local/lib/ser/modules/usrloc.so" loadmodule "/usr/local/lib/ser/modules/registrar.so" loadmodule "/usr/local/lib/ser/modules/textops.so" # Uncomment this if you want digest authentication # mysql.so must be loaded ! #loadmodule "/usr/local/lib/ser/modules/auth.so" #loadmodule "/usr/local/lib/ser/modules/auth_db.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- modparam("usrloc", "db_mode", 0) # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line #modparam("usrloc", "db_mode", 2) # -- auth params -- # Uncomment if you are using auth module # #modparam("auth_db", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) # #modparam("auth_db", "password_column", "password") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # ------------------------- request routing logic ------------------- # main routing logic route{ setflag(1); t_on_failure("1"); t_on_reply("1"); log(1, "message received\n"); t_relay_to_udp("iptel.org", "5060"); break; } onreply_route[1] { if (isflagset(1)) { log(1, "onreply: flag set\n"); } else { log(1, "onreply: flag unset\n"); }; } failure_route[1] { if (isflagset(1)) { log(1, "failure: flag set\n"); } else { log(1, "failure: flag unset\n"); }; } kamailio-4.0.4/examples/geo_split.sh0000755000000000000000000000210712223032460016137 0ustar rootroot#!/bin/sh # # $Id$ # # utility for displaying geographical break-down of usrloc population # it takes functional netgeo support for ser (currently, an # experimental unavailable feature) # DB_HOST=dbhost DB_USER=ser DB_PW=heslo # --- TMP=/tmp/geo_split.$$ stats() { DOMAIN_CNT=`grep $1 $TMP | wc | awk ' { print $1 } '` if [ "$DOMAIN_CNT" -eq "0" ] ; then PC="0" else PC=`expr $DOMAIN_CNT \* 100 / $2` fi printf "$1: $DOMAIN_CNT $PC %%\n" grep -v $1 $TMP > $TMP.2 mv $TMP.2 $TMP } mysql -h $DB_HOST --batch -u $DB_USER -p$DB_PW ser -e "select location from netgeo_cache" | awk -F '/' ' BEGIN { line=0 } { line++ } line==1 { next; } # skip heading length()==0 { next; } # skip empty lines /^[A-Z][A-Z ]*\/[A-Z ]+/ { print $2; next;} /^[A-Z]+/ { print " " $1; next;} /^ [A-Z]+/ { print $1; next;} { print "error" > "/dev/stderr" }' | sort -b > $TMP export TOTAL_CNT=`wc $TMP|awk '{print $1}'` printf "Total: $TOTAL_CNT\n" (for i in AU DE US NL CZ UK RO RU TR TW SW JP CA HK IT AR BE CN FI GL IN KR SE UY; do stats $i $TOTAL_CNT done) #| sort cat $TMP rm $TMP* kamailio-4.0.4/examples/logging.cfg0000644000000000000000000000107012223032460015720 0ustar rootroot# # $Id$ # # logging example # # ------------------ module loading ---------------------------------- fork=no listen=192.168.2.16 log_stderror=yes debug=3 # ------------------------- request routing logic ------------------- # main routing logic route{ # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { log(1, "REGISTER received\n"); } else { log(1, "non-REGISTER received\n"); }; if (uri=~"sip:.*[@:]iptel.org") { log(1, "request for iptel.org received\n"); } else { log(1, "request for other domain received\n"); }; } kamailio-4.0.4/examples/exec_s5b.cfg0000644000000000000000000000376012223032460015777 0ustar rootroot# # $Id$ # # simple quick-start config script # fork=no log_stderror=yes # ----------- global configuration parameters ------------------------ loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/registrar/registrar.so" loadmodule "modules/exec/exec.so" # ----------------- setting module-specific parameters --------------- route{ # uri for my domain ? if (uri==myself) { if (method=="REGISTER") { save("location"); break; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { # proceed to email notification if (method=="INVITE") route(1) else sl_send_reply("404", "Not Found"); break; }; }; # user found, forward to his current uri now; if any # forwarding error occurs (e.g., busy or cancelled recevied # from downstream), proceed to reply_route[1] t_on_failure("1"); if (!t_relay()) { sl_reply_error(); }; } /* handling of missed calls */ route[1] { # don't continue if it is a retransmission if ( !t_newtran()) { sl_reply_error(); break; }; # external script: lookup user, if user exists, send # an email notification to him if (!exec_msg(' QUERY="select email_address from subscriber where username=\"$SIP_OUSER\""; EMAIL=`mysql -Bsuser -pheslo -e "$QUERY" ser`; if [ -z "$EMAIL" ] ; then exit 1; fi ; echo "SIP request received from $SIP_HF_FROM for $SIP_OUSER" | mail -s "request for you" $EMAIL ')) { # exec returned error ... user does not exist # send a stateful reply t_reply("404", "User does not exist"); } else { t_reply("600", "No messages for this user"); }; break; } failure_route[1] { # just call exec, that's it exec_msg(' QUERY="select email_address from subscriber where username=\"$SIP_OUSER\""; EMAIL=`mysql -Bsuser -pheslo -e "$QUERY" ser`; if [ -z "$EMAIL" ] ; then exit 1; fi ; echo "SIP request received from $SIP_HF_FROM for $SIP_OUSER" | mail -s "request for you" $EMAIL ') ; t_relay(); } kamailio-4.0.4/examples/exec_s2.cfg0000644000000000000000000000075012223032460015626 0ustar rootroot# # $Id$ # # send a notificiation if a request for user jiri arrives # ------------------ module loading ---------------------------------- loadmodule "modules/exec/exec.so" loadmodule "modules/sl/sl.so" # ------------------------- request routing logic ------------------- # main routing logic route{ # send email if a request for jiri arrives if (uri=~"^sip:jiri@") { exec_msg("(egrep -i '^(From|f):'; echo 'request received')| mail -s 'request for you' jiri"); }; } kamailio-4.0.4/examples/serial_183.cfg0000644000000000000000000000257212223032460016154 0ustar rootroot# # $Id$ # # this example shows how to use forking on failure # log_stderror=1 fork=no listen=192.168.2.16 debug=3 # ------------------ module loading ---------------------------------- # Uncomment this if you want to use SQL database loadmodule "modules/tm/tm.so" loadmodule "modules/sl/sl.so" loadmodule "modules/maxfwd/maxfwd.so" # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (len_gt( max_len )) { sl_send_reply("513", "Message too big"); break; }; /* skip register for testing purposes */ if (method=="REGISTER") { sl_send_reply("200", "ok"); break; }; if (!method=="ACK") log(1, "forwarding now to primary destination\n"); if (method=="INVITE") { rewriteuri("sip:xxx@192.168.2.16:5064"); # if transaction broken, try other an alternative # route t_on_failure("1"); # if a provisional came, stop alternating t_on_reply("1"); }; t_relay(); } failure_route[1] { log(1, "trying at alternate destination\n"); append_branch("sip:yyy@192.168.2.16:5064"); t_relay(); } onreply_route[1] { log(1, "reply came in\n"); if (status=~"18[0-9]") { log(1, "provisional -- resetting negative failure\n"); t_on_failure("0"); }; } kamailio-4.0.4/examples/msilo.cfg0000644000000000000000000000576112223032460015430 0ustar rootroot# # MSILO usage example # # $ID: daniel $ # children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) # ------------------ module loading ---------------------------------- loadmodule "../sip_router/modules/print/print.so" loadmodule "../sip_router/modules/textops/textops.so" loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/mysql/mysql.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/msilo/msilo.so" loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/registrar/registrar.so" loadmodule "../sip_router/modules/usrloc/usrloc.so" # ----------------- setting module-specific parameters --------------- # -- registrar params -- modparam("registrar", "default_expires", 120) # -- registrar params -- modparam("usrloc", "db_mode", 0) # -- msilo params -- modparam("msilo","db_url","mysql://user:xxx@127.0.0.1/msilo") modparam("msilo","registrar","sip:registrar@mydomain.com") # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 15 ) modparam("tm", "wt_timer", 10 ) route{ if ( !mf_process_maxfwd_header("10") ) { sl_send_reply("483","To Many Hops"); drop(); }; if (uri==myself) { # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { save("location"); log("REGISTER received -> dumping messages with MSILO\n"); # MSILO - dumping user's offline messages if (m_dump()) { log("MSILO: offline messages dumped - if they were\n"); }else{ log("MSILO: no offline messages dumped\n"); }; break; }; # domestic SIP destinations are handled using our USRLOC DB if(!lookup("location")) { if (! t_newtran()) { sl_reply_error(); break; }; # we do not care about anything else but MESSAGEs if (!method=="MESSAGE") { if (!t_reply("404", "Not found")) { sl_reply_error(); }; break; }; log("MESSAGE received -> storing using MSILO\n"); # MSILO - storing as offline message if (m_store("0")) { log("MSILO: offline message stored\n"); if (!t_reply("202", "Accepted")) { sl_reply_error(); }; }else{ log("MSILO: offline message NOT stored\n"); if (!t_reply("503", "Service Unavailable")) { sl_reply_error(); }; }; break; }; # if the downstream UA does not support MESSAGE requests # go to failure_route[1] t_on_failure("1"); t_relay(); break; }; # forward anything else t_relay(); } failure_route[1] { # forwarding failed -- check if the request was a MESSAGE if (!method=="MESSAGE") { break; }; log(1,"MSILO: the downstream UA does not support MESSAGE requests ...\n"); # we have changed the R-URI with the contact address -- ignore it now if (m_store("1")) { log("MSILO: offline message stored\n"); t_reply("202", "Accepted"); }else{ log("MSILO: offline message NOT stored\n"); t_reply("503", "Service Unavailable"); }; } kamailio-4.0.4/examples/ctd.sh0000755000000000000000000001207412223032460014730 0ustar rootroot#!/bin/sh # # $Id$ # # Usage: ctd.sh $FROM $TARGET # # click-to-dial example using REFER #---------------------------------- # # About: # ------ # this script initiates a call from SIP user $FROM to SIP # user $TARGET; it works as follows: a dummy user invites # $FROM to a dummy "call on hold"; as soon as it is set up, the # dummy user transfers $FROM to $TARGET (REFER transaction) # and terminates the dummy session established previously # (BYE transaction). Note: the "dummy call" is used to # make $FROM accept $REFER -- most of SIP phones do not # accept REFER if no call has not been established yet. # # Requirements: # ------------- # - SER with FIFO server turned on and TM module loaded # # Limitations: # ------------ # it only works with UAs supporting REFER; it has been tested # with Cisco 7960, Mitel 5055, Grandstream and Pingtel; Windows # Messenger does not support REFER. Never tested on solaris. # Some cisco 7960 images don't work (in particular, POS30202 # doesnt, POS3-03-8-21 does) # # History: # -------- # 2003-03-01 bug_fix: route set reversed # 2003-02-27 dialog support completed (jiri) # 2003-04-28 dialog info precomputed in SER (jiri) #-------------------------------- # config: who with whom # address of the final destination to which we want to transfer # initial CSeq and CallId if [ -z "$2" ]; then TARGET="sip:23@192.168.2.16" echo "destination unspecified -- taking default value $TARGET" else TARGET="$2" fi # address of user wishing to initiate conversation if [ -z "$1" ] ; then URI="sip:44@192.168.2.16" echo "caller unspecified -- taking default value $URI" else URI="$1" fi #--------------------------------- # fixed config data FIFO="/tmp/ser_fifo" # address of controller FROM="" CSEQ="1" CALLIDNR=`date '+%s'`$$ CALLID="${CALLIDNR}.fifouacctd" name="ctd_fifo_$$" fifo_reply="/tmp/$name" dlg="/tmp/$CALLID.dlg" FIXED_DLG=`printf "From: $FROM;tag=$CALLIDNR\nCall-ID: $CALLID\nContact: "` #---------------------------------- # generate parts of FIFO-request essential to forming # subsequent in-dialog reuqests # # limitations: parsing broken if <> in display names or # line-folding used filter_fl() { awk -F ' ' ' BEGIN { IGNORECASE=1; line=0; eoh=0;ret=1 } END { exit ret; } {line++; } # line 1: status code line==1 && /^2[0-9][0-9] / { ret=0;next; } line==1 && /^[3-6][0-9][0-9] / { print; print $0 > "/dev/stderr"; next; } line==1 { print "reply error"; print; next; } # skip body /^$/ { eoh=1 } eoh==1 { next } # uri and outbound uri at line 2,3: copy and paste line==2 || line==3 { print $0; next; } # line 4: Route; empty if ".", copy and paste otherwise line==4 && /^\.$/ { next; } # if non-empty, copy and paste it line==4 { print $0; next; } # filter out to header field for use in next requests /^(To|t):/ { print $0; next; } # anything else will be ignored {next} ' # end of awk script } # end of filter_fl #--------------------------- # main # set up exit cleaner trap "rm -f $dlg $fifo_reply; exit 1" 0 # set up FIFO communication if [ ! -w $FIFO ] ; then # can I write to FIFO server? echo "Error opening ser's FIFO $FIFO" exit 1 fi mkfifo $fifo_reply # create a reply FIFO if [ $? -ne 0 ] ; then echo "error opening reply fifo $fifo_reply" exit 1 fi chmod a+w $fifo_reply # start reader now so that it is ready for replies # immediately after a request is out cat < $fifo_reply | filter_fl > $dlg & fifo_job="$!" # initiate dummy INVITE with pre-3261 "on-hold" # (note the dots -- they mean in order of appearance: # outbound uri, end of headers, end of body; eventualy # the FIFO request must be terminated with an empty line) cat > $FIFO < CSeq: $CSEQ INVITE Content-Type: application/sdp . v=0 o=click-to-dial 0 0 IN IP4 0.0.0.0 s=session c=IN IP4 0.0.0.0 b=CT:1000 t=0 0 m=audio 9 RTP/AVP 0 a=rtpmap:0 PCMU/8000 . EOF # wait for reply wait $fifo_job # returns completion status of filter_fl if [ "$?" -ne "0" ] ; then echo "invitation failed" exit 1 fi echo "invitation succeeded" # proceed to REFER now if [ \! -r $dlg ] ; then echo "dialog broken" exit 1 fi CSEQ=`expr $CSEQ + 1` # start reader now so that it is ready for replies # immediately after a request is out cat < $fifo_reply | filter_fl > /dev/null & fifo_job="$!" # dump the REFER request to FIFO server cat > $FIFO < /dev/null & fifo_job="$!" cat > $FIFO <. # # For more information about the various parameters, functions and statements # try http://sip-router.org/wiki/ . # include_file "pcscf.cfg" ####### Defined Values ######### # *** Value defines - IDs used later in config # - flags # FLT_ - per transaction (message) flags # FLB_ - per branch flags #!define FLT_CAPTURE 1 #!define FLT_DIALOG 2 #!define FLT_NAT 3 #!define FLT_IPV4 4 #!define DLG_TIMEOUT_AVP "i:1" #!define RR_CUSTOM_USER_AVP "i:2" #!define NATHELPER_RECEIVED_AVP "i:3" system.shutdownmode = 0 desc "System shutdown mode" ####### Global Parameters ######### #!ifdef WITH_DEBUG debug=5 log_stderror=no sip_warning=yes children=4 #!else debug=2 log_stderror=no sip_warning=no children=64 #!endif syn_branch=0 # Locks all ser pages into memory making it unswappable (in general one # doesn't want his sip proxy swapped out ) mlock_pages=yes # Tries to pre-fault all the shared memory, before starting. When "on", start # time will increase, but combined with mlock_pages will guarantee ser will get # all its memory from the beginning (no more kswapd slow downs) shm_force_alloc=yes # Do SRV-Loadbalancing: dns_srv_lb=yes # Always: Also try IPv6: dns_try_ipv6=yes /* uncomment and configure the following line if you want Kamailio to bind on a specific interface/port/proto (default bind on all available) */ #!ifdef NETWORK_INTERFACE listen=NETWORK_INTERFACE #!endif /* Second interface to use, e.g. for IPv6 */ #!ifdef NETWORK_INTERFACE_2 listen=NETWORK_INTERFACE_2 #!endif /* port to listen to * - can be specified more than once if needed to listen on many ports */ port=PORT alias=HOSTNAME_IP user_agent_header="User-Agent: Kamailio P-CSCF" server_header="Server: Kamailio P-CSCF" /* comment the next line to enable the auto discovery of local aliases based on reverse DNS on IPs (default on) */ auto_aliases=no #!ifdef WITH_TLS # Check, if TCP is enabled: #!ifndef WITH_TCP #!define WITH_TCP #!endif enable_tls=yes #!endif # Check, if NAT is enabled (in case you want to Force all calls through the RTPProxy) #!ifdef FORCE_RTPRELAY #!ifndef WITH_NAT #!define WITH_NAT #!endif #!endif #!ifdef WITH_TCP # life time of TCP connection when there is no traffic # - a bit higher than registration expires to cope with UA behind NAT tcp_connection_lifetime=3615 # If a message received over a tcp connection has "alias" in its via a new tcp # alias port will be created for the connection the message came from (the # alias port will be set to the via one). # # Note: For NAT traversal of TCP clients it is better to not use # tcp_accept_aliases but just use nathelper module and # fix_nated_[contact|register] functions. tcp_accept_aliases=no # Enable SIP outbound TCP keep-alive using PING-PONG (CRLFCRLF - CRLF). tcp_crlf_ping=yes #!else disable_tcp=yes #!endif check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) # ------------------ module loading ---------------------------------- mpath="/usr/lib64/kamailio/modules_k/:/usr/lib64/kamailio/modules/:/usr/lib/kamailio/modules_k/:/usr/lib/kamailio/modules/" # (we try both the lib64 and the lib directory) loadmodule "tm" loadmodule "pv" loadmodule "sl" loadmodule "rr" loadmodule "dialog_ng" loadmodule "ims_usrloc_pcscf" loadmodule "textops" loadmodule "textopsx" loadmodule "maxfwd" loadmodule "xlog" loadmodule "pua" loadmodule "db_sqlite" loadmodule "ims_registrar_pcscf" loadmodule "sanity" loadmodule "siputils" loadmodule "kex" # Control interfaces: loadmodule "ctl" loadmodule "cfg_rpc" loadmodule "mi_rpc" loadmodule "mi_fifo" #!ifdef WITH_XMLRPC loadmodule "xmlrpc" #!endif #!ifdef WITH_RX loadmodule "cdp" loadmodule "cdp_avp" loadmodule "ims_qos" #!endif #!ifdef CAPTURE_NODE loadmodule "siptrace" #!endif #!ifdef WITH_DEBUG loadmodule "debugger" #!endif #!ifdef WITH_TLS loadmodule "tls" #!endif #!ifdef WITH_ANTIFLOOD loadmodule "pike" loadmodule "htable" loadmodule "dispatcher" #!endif #!ifdef WITH_NAT loadmodule "path" loadmodule "rtpproxy" loadmodule "nat_traversal" loadmodule "nathelper" #!endif # ----------------- setting module-specific parameters --------------- # ----- mi_fifo params ----- modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") modparam("mi_fifo", "fifo_mode", 0666) modparam("mi_fifo", "fifo_user", "kamailio") modparam("mi_fifo", "fifo_group", "kamailio") # ----- pua params ----- # Database is disabled (for now) modparam("pua", "db_mode", 0) modparam("pua", "db_url", "sqlite:///etc/kamailio/kamailio.db") # ----- tm params ----- # auto-discard branches from previous serial forking leg modparam("tm", "failure_reply_mode", 3) # default retransmission timeout: 10sec modparam("tm", "fr_timer", 10000) # default invite retransmission timeout after 1xx: 120sec modparam("tm", "fr_inv_timer", 120000) # Don't reply automatically with "100 Trying" modparam("tm", "auto_inv_100", 1) # ----- rr params ----- # add value to ;lr param to cope with most of the UAs modparam("rr", "enable_full_lr", 1) # append from tag to the RR modparam("rr", "append_fromtag", 1) # add a Username to RR-Header modparam("rr", "add_username", 1) # Take User from a custom AVP modparam("rr", "custom_user_avp", "$avp(RR_CUSTOM_USER_AVP)") #!ifdef WITH_NAT # ----- path params ----- # Evaluate the received info in the route-header: modparam("path", "use_received", 1) #!endif # -- usrloc params -- #!ifdef WITH_DEBUG modparam("ims_usrloc_pcscf", "enable_debug_file", 1) modparam("ims_usrloc_pcscf", "usrloc_debug_file", "/var/log/usrloc_debug") #!else modparam("ims_usrloc_pcscf", "enable_debug_file", 0) #!endif #!ifdef WITH_RX # -- CDP params -- modparam("cdp","config_file","/etc/kamailio/pcscf.xml") # -- diameter_rx params -- modparam("ims_qos", "rx_dest_realm", "NETWORKNAME") #!endif # -- dialog_ng params -- modparam("dialog_ng", "dlg_flag", FLT_DIALOG) modparam("dialog_ng", "timeout_avp", "$avp(DLG_TIMEOUT_AVP)") modparam("dialog_ng", "detect_spirals", 0) modparam("dialog_ng", "profiles_no_value", "orig ; term") #!ifdef WITH_TLS # ----- tls params ----- modparam("tls", "config", "/etc/kamailio/tls.cfg") #!endif #!ifdef WITH_ANTIFLOOD # ----- pike params ----- modparam("pike", "sampling_time_unit", 2) modparam("pike", "reqs_density_per_unit", 16) modparam("pike", "remove_latency", 4) # ----- htable params ----- # ip ban htable with autoexpire after 5 minutes modparam("htable", "htable", "ipban=>size=8;autoexpire=300;") # ----------------- Settings for Dispatcher --------------- modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list") # Actively query the gateways: modparam("dispatcher", "ds_probing_mode", 1) #!endif #!ifdef WITH_XMLRPC # ----- xmlrpc params ----- modparam("xmlrpc", "route", "XMLRPC"); modparam("xmlrpc", "url_match", "^/RPC") #!endif #!ifdef WITH_DEBUG # ----- debugger params ----- modparam("debugger", "cfgtrace", 1) #!endif #!ifdef CAPTURE_NODE # Destination, where to send the traffic modparam("siptrace", "duplicate_uri", CAPTURE_NODE) # Trace all traffic modparam("siptrace", "trace_on", 1) modparam("siptrace", "trace_to_database", 0) modparam("siptrace", "trace_flag", FLT_CAPTURE) modparam("siptrace", "hep_mode_on", 1) #!endif #!ifdef WITH_NAT # ----- rtpproxy params ----- modparam("rtpproxy", "rtpproxy_sock", RTPPROXY_ADDRESS) # ----- nat_traversal params ----- # If another keepalive is wanted, this is the place modparam("nat_traversal", "keepalive_interval", 10) # If another method than NOTIFY is wanted: modparam("nat_traversal", "keepalive_method", "OPTIONS") # From? modparam("nat_traversal", "keepalive_from", "sip:keepalive@HOSTNAME") # Where we store information about keep-alives: modparam("nat_traversal", "keepalive_state_file", "/var/run/kamailio/keepalive_state") #!endif ####### Routing Logic ######## # Main SIP request routing logic # - processing of any incoming SIP request starts with this route route { #!ifdef WITH_DEBUG xlog("L_ERR", "$rm ($fu ($si:$sp) to $tu, $ci)\n"); #!endif # per request initial checks route(REQINIT); # Check for NAT, if enabled. route(NAT); # Handle Registrations: if (is_method("REGISTER")) { route(REGISTER); exit; } #Set DLG flag to track dialogs using dialog2 if (!is_method("SUBSCRIBE")) setflag(FLT_DIALOG); # Evaluate Route-Header and set $route_uri loose_route(); if !(($route_uri =~ "sip:term@"+HOSTNAME_ESC+".*")) { if ((is_in_profile("orig") || has_totag()) && ($route_uri =~ "sip:mo@"+".*")) { if (!is_method("CANCEL")) route(Orig_Subsequent); else route(Orig_Standalone); break; } if ((is_in_profile("term") || has_totag()) && ($route_uri =~ "sip:mt@"+".*")) { if (!is_method("CANCEL")) route(Term_Subsequent); else route(Term_Standalone); break; } # No dialog yet - ACK not relayed as hop-to-hop if (is_method("ACK")) break; if (is_method("INVITE|SUBSCRIBE")) { route(Orig_Initial); break; } else { if (is_method("UPDATE")) { send_reply("403","Forbidden - Target refresh outside dialog not allowed"); break; } if (is_method("BYE|PRACK")) { send_reply("403","Forbidden - Originating subsequent requests outside dialog not allowed"); break; } route(Orig_Standalone); break; } } else { # TODO - check if this does come from an UE and that UE is unregistered if(is_known_dlg()) { xlog("L_DBG", "TERM: Request $rm from $si is in-dialog\n"); } if (!is_in_profile("term") && is_method("INVITE|SUBSCRIBE")){ route(Term_Initial); break; } else { if (is_in_profile("term")){ if (!is_method("CANCEL")) route(Term_Subsequent); else route(Term_Standalone); break; } else { if (is_method("UPDATE")) { send_reply("403","Forbidden - Target refresh outside dialog not allowed"); break; } if (is_method("BYE|ACK|PRACK")) { send_reply("403","Forbidden - Terminating subsequent requests outside dialog not allowed"); break; } route(Term_Standalone); break; } } break; } } ###################################################################### # Helper routes (Basic-Checks, NAT-Handling/RTP-Control, XML-RPC) ###################################################################### # Per SIP request initial checks route[REQINIT] { #!ifdef WITH_ANTIFLOOD # flood dection from same IP and traffic ban for a while # be sure you exclude checking trusted peers, such as pstn gateways # - local host excluded (e.g., loop to self) if (!has_totag() && (src_ip!=myself) && !ds_is_from_list()) { if($sht(ipban=>$si)!=$null) { # ip is already blocked xdbg("request from blocked IP - $rm from $fu (IP:$si:$sp)\n"); exit; } if (!pike_check_req()) { xlog("L_ALERT","ALERT: pike blocking $rm from $fu (IP:$si:$sp)\n"); $sht(ipban=>$si) = 1; exit; } } #!endif # Trace this message #!ifdef CAPTURE_NODE sip_trace(); setflag(FLT_CAPTURE); #!endif if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if(!sanity_check("1511", "7")) { xlog("Malformed SIP message from $si:$sp\n"); exit; } # Check for shutdown mode: if (!has_totag() && ($sel(cfg_get.system.shutdownmode) > 0)) { send_reply("503", "Server shutting down"); exit; } # Reply to OPTIONS: if (is_method("OPTIONS") && (uri==myself)) { options_reply(); exit; } # Ignore Re-Transmits: if (t_lookup_request()) { exit; } } ###################################################################### # XMLRPC routing ###################################################################### #!ifdef WITH_XMLRPC route[XMLRPC] { # allow XMLRPC from localhost if ((method=="POST" || method=="GET") && (src_ip==127.0.0.1)) { # close connection only for xmlrpclib user agents (there is a bug in # xmlrpclib: it waits for EOF before interpreting the response). if ($hdr(User-Agent) =~ "xmlrpclib") set_reply_close(); set_reply_no_connect(); dispatch_rpc(); exit; } send_reply("403", "Forbidden"); exit; } #!endif ###################################################################### # Caller NAT detection route ###################################################################### route[NAT] { #!ifdef WITH_NAT if (client_nat_test("7")) { force_rport(); if (is_method("REGISTER")) { force_rport(); xlog("L_DBG", "Enabling keep-alive\n"); nat_keepalive(); } else { fix_contact(); } setflag(FLT_NAT); } #!ifdef WITH_RTPIPV4 if (is_request() && (af==INET)) { setflag(FLT_IPV4); } #!endif #!endif return; } ###################################################################### # Route for RTPProxy control (Originating Requests) ###################################################################### route[RTPPROXY_ORIG] { #!ifdef WITH_NAT #!ifndef FORCE_RTPRELAY if (is_request() && has_totag()) { if(check_route_param("nat=yes")) { setflag(FLT_NAT); #!ifdef WITH_RTPIPV4 } else { if(check_route_param("nat=v4")) { setflag(FLT_IPV4); } #!endif } } if !(isflagset(FLT_NAT) || isflagset(FLT_IPV4)) return; #!endif /* ## P-RTP-Stats snippet for Kamailio/RTPProxy if (is_method("BYE")) { $var(xrtpstat) = $rtpstat; xlog("L_ERR", "$$rtpstat = $rtpstat\n"); # Work the stats $var(rtp0) = $(var(xrtpstat){s.select,1, }); $var(rtp1) = $(var(xrtpstat){s.select,2, }); $var(rtp2) = $(var(xrtpstat){s.select,3, }); $var(rtp3) = $(var(xrtpstat){s.select,4, }); $var(rtp4) = $(var(xrtpstat){s.select,5, }); if ($var(rtp0) != "" || $var(rtp1) != "") { append_hf("P-RTP-Stat: EX=RTPProxy,PS=$var(rtp0),PR=$var(rtp1),PL=$var(rtp3)\r\n"); } } */ #!ifdef WITH_RTPIPV4 # I = IPv4 # E = IPv6 if(isflagset(FLT_IPV4)) { if (is_request()) { if (is_direction("downstream")) rtpproxy_manage("1FOX"); else rtpproxy_manage("1FOXR"); } else { if (is_direction("downstream")) rtpproxy_manage("2FOXR"); else rtpproxy_manage("2FOX"); } } else { if (is_request()) { if (is_direction("downstream")) rtpproxy_manage("1FOEE"); else rtpproxy_manage("1FOEER"); } else { if (is_direction("downstream")) rtpproxy_manage("2FOEER"); else rtpproxy_manage("2FOEE"); } } #!else # No Bridging if (is_request()) { if (is_direction("downstream")) rtpproxy_manage("1FOII"); else rtpproxy_manage("1FOIIR"); } else { if (is_direction("downstream")) rtpproxy_manage("2FOIIR"); else rtpproxy_manage("2FOII"); } #!endif #!ifndef FORCE_RTPRELAY if (is_request()) { if (!has_totag()) { if(isflagset(FLT_IPV4)) { add_rr_param(";nat=v4"); } else { add_rr_param(";nat=yes"); } } } #!endif #!ifdef WITH_MOH if (is_request()) { if (is_method("INVITE")) { if (search_body("^a=sendonly")) { rtpproxy_stream2uas("/etc/kamailio/moh/moh.sln16", "-1"); } else { rtpproxy_stop_stream2uas(); } } } else { if (status=="200") { if (search_body("^a=sendonly")) { rtpproxy_stream2uac("/etc/kamailio/moh/moh.sln16", "-1"); } else { rtpproxy_stop_stream2uac(); } } } #!endif #!endif return; } ###################################################################### # Route for RTPProxy control (Terminating Requests) ###################################################################### route[RTPPROXY_TERM] { #!ifdef WITH_NAT #!ifndef FORCE_RTPRELAY if (is_request()) { if(check_route_param("nat=yes")) { setflag(FLT_NAT); #!ifdef WITH_RTPIPV4 } else { if(check_route_param("nat=v4")) { setflag(FLT_IPV4); } #!endif } } if !(isflagset(FLT_NAT) || isflagset(FLT_IPV4)) return; #!endif #!ifdef WITH_RTPIPV4 # I = IPv4 # E = IPv6 if(isflagset(FLT_IPV4)) { # xlog("L_ERR", "IPv6-to-4 Bridge\n"); if (is_request()) { if (is_direction("downstream")) rtpproxy_manage("1FOXR"); else rtpproxy_manage("1FOX"); } else { if (is_direction("downstream")) rtpproxy_manage("2FOX"); else rtpproxy_manage("2FOXR"); } } else { if (is_request()) { if (is_direction("downstream")) rtpproxy_manage("1FOEER"); else rtpproxy_manage("1FOEE"); } else { if (is_direction("downstream")) rtpproxy_manage("2FOEE"); else rtpproxy_manage("2FOEER"); } } #!else # No Bridging if (is_request()) { if (is_direction("downstream")) rtpproxy_manage("1FOIIR"); else rtpproxy_manage("1FOII"); } else { if (is_direction("downstream")) rtpproxy_manage("2FORII"); else rtpproxy_manage("2FORIIR"); } #!endif #!ifndef FORCE_RTPRELAY if (is_request()) { if (!has_totag()) { if(isflagset(FLT_IPV4)) { add_rr_param(";nat=v4"); } else { add_rr_param(";nat=yes"); } } } #!endif #!ifdef WITH_MOH if (is_request()) { if (is_method("INVITE")) { if (search_body("^a=sendonly")) { rtpproxy_stream2uas("/etc/kamailio/moh/moh.sln16", "-1"); } else { rtpproxy_stop_stream2uas(); } } } #!endif if (is_reply()) { fix_contact(); #!ifdef WITH_MOH if (status=="200") { if (search_body("^a=sendonly")) { rtpproxy_stream2uac("/etc/kamailio/moh/moh.sln16", "-1"); } else { rtpproxy_stop_stream2uac(); } } #!endif } #!endif return; } ###################################################################### # Route for handling Registrations: ###################################################################### route[REGISTER] { #!ifdef WITH_RX xlog("L_DBG","Subscribing to signalling bearer status\n"); Rx_AAR_Register("location"); switch ($avp(s:aar_return_code)) { case 1: xlog("L_DBG", "Diameter: AAR success on subscription to signalling\n"); break; default: xlog("L_ERR", "Diameter: AAR failed on subscription to signalling\n"); send_reply("403", "Can't register to QoS for signalling"); exit; } #!endif #!ifdef WITH_NAT if (isflagset(FLT_NAT) || isflagset(FLT_IPV4)) { if (isflagset(FLT_IPV4)) $var(nat) = "v4"; else $var(nat) = "yes"; if ($pr == "tls") { append_hf("Path: \r\n"); } else if ($pr == "tcp") { append_hf("Path: \r\n"); } else { append_hf("Path: \r\n"); } } else #!endif append_hf("Path: \r\n"); append_hf("Supported: path\r\n"); append_hf("Require: path\r\n"); # Add a visited Network-ID-Header: if (is_present_hf("P-Visited-Network-ID")) { $var(new_hdr) = "NETWORKNAME, "+$hdr(P-Visited-Network-ID); append_hf("P-Visited-Network-ID: $var(new_hdr)\r\n"); } else { append_hf("P-Visited-Network-ID: NETWORKNAME\r\n"); } pcscf_save_pending("location"); t_on_reply("REGISTER_reply"); t_on_failure("REGISTER_failure"); t_relay(); exit; } ###################################################################### # Replies for REGISTER requests: ###################################################################### onreply_route[REGISTER_reply] { if (t_check_status("200")) { xlog("L_DBG","Saving location\n"); pcscf_save("location"); } exit; } ###################################################################### # Negative replies to REGISTER requests: ###################################################################### failure_route[REGISTER_failure] { if (t_check_status("408")) send_reply("504","Server Time-Out"); } ###################################################################### # Originating, Intial Requests ###################################################################### route[Orig_Initial] { xlog("L_DBG", "IMS: INSIDE ORIG_INITIAL\n"); loose_route(); t_newtran(); if (!pcscf_is_registered("location")) { send_reply("403","Forbidden - You must register first with a S-CSCF"); break; } if (!pcscf_follows_service_routes("location")){ #Variant 1 - deny access to the network #send_reply("400","Bad Request - Not following indicated service routes"); #break; #Variant 2 - enforce routes and let the dialog continue pcscf_force_service_routes("location"); } #!ifdef WITH_RX xlog("L_DBG","Diameter: Orig authorizing media via Rx\n"); Rx_AAR("location"); if ($avp(s:aar_return_code) != 1) { xlog("L_ERR", "Diameter: AAR failed\n"); send_reply("403", "QoS not authorized"); exit; } #!endif #prepend mo as user for record route $avp(RR_CUSTOM_USER_AVP)="mo"; record_route(); if (is_present_hf("P-Preferred-Identity") && pcscf_assert_identity("location", "$hdr(P-Preferred-Identity)")) { remove_hf("P-Preferred-Identity"); append_hf("P-Asserted-Identity: $hdr(P-Preferred-Identity)\r\n"); } else if (pcscf_assert_identity("location", "$fu")) { append_hf("P-Asserted-Identity: <$fu>\r\n"); } else { append_hf("P-Asserted-Identity: <$pcscf_asserted_identity>\r\n"); } # Do RTP-Relaying, if necessary: route(RTPPROXY_ORIG); set_dlg_profile("orig"); t_on_reply("Orig_Initial_reply"); if (!t_relay()) { send_reply("500","Error forwarding originating initial request"); break; }; break; } ###################################################################### # Replies to the Initial Requests ###################################################################### onreply_route[Orig_Initial_reply] { xlog("L_DBG", "route(RTPIMS: INSIDE ORIG_INITIAL_REPLY\n"); #!ifdef WITH_RX if (t_check_status("180|183|200")){ xlog("L_DBG","Diameter: Orig authorizing media via Rx\n"); Rx_AAR("orig"); if ($avp(s:aar_return_code) != 1) { xlog("L_ERR", "IMS: AAR failed Orig\n"); dlg_terminate("all", "Sorry no QoS available"); } else { xlog("L_DBG", "Diameter: Orig AAR success on media authorization\n"); } xlog("L_DBG", "IMS: Received 183/200 inside orig_initial_reply\n"); } #!endif # Note: We only do the RTP-Update for the successful case, # the others simply time-out (if we would do otherwise, RTP-Relaying # would fail for forked requests) # Do RTP-Relaying, if necessary: route(RTPPROXY_ORIG); } ###################################################################### # Originating, Subsequent requests ###################################################################### route[Orig_Subsequent] { xlog("L_DBG", "IMS: INSIDE ORIG_SUBSEQUENT\n"); #TODO: need to check we are registered before we send request loose_route(); #TODO: check we are following enforeced routes # Check for "sec-agree" in the Require header: if (is_present_hf("Require") && $hdr(Require) =~ ".*sec-agree.*") { # Remove the old Require-Header: remove_hf("Require"); # Replace ", sec-agree" with "" $var(new_hdr) = $(hdr(Require){re.subst,/[, ]*sec-agree//gi}); if ($(var(new_hdr){s.len}) > 0) { append_hf("Require: $var(new_hdr)\r\n"); } } # Check for "sec-agree" in the Proxy-Require header: if (is_present_hf("Proxy-Require") && $hdr(Proxy-Require) =~ ".*sec-agree.*") { # Remove the old Proxy-Require-Header: remove_hf("Proxy-Require"); # Replace ", sec-agree" with "" $var(new_hdr) = $(hdr(Proxy-Require){re.subst,/[, ]*sec-agree//gi}); if ($(var(new_hdr){s.len}) > 0) { append_hf("Proxy-Require: $var(new_hdr)\r\n"); } } # Do RTP-Relaying, if necessary: route(RTPPROXY_ORIG); #TODO: remove security verification t_on_reply("Orig_Subsequent_reply"); if (!t_relay()) { xlog("L_ERR", "Could not forward\n"); send_reply("500","Error forwarding originating subsequent request"); break; }; } ###################################################################### # Replies to Originating, Subsequent requests ###################################################################### onreply_route[Orig_Subsequent_reply] { xlog("L_DBG", "IMS: INSIDE ORIG_SUBSEQUENT_reply and request is $rm from IP $si\n"); # Do RTP-Relaying, if necessary: route(RTPPROXY_ORIG); } ###################################################################### # Originating, Standalone requests ###################################################################### route[Orig_Standalone] { xlog("L_DBG", "IMS: INSIDE ORIG_STANDALONE\n"); loose_route(); t_newtran(); if (!pcscf_is_registered("location")) { send_reply("403","Forbidden - You must register first with a S-CSCF"); break; } if (!pcscf_follows_service_routes("location")){ #Variant 1 - deny access to the network #send_reply("400","Bad Request - Not following indicated service routes"); #break; #Variant 2 - enforce routes and let the dialog continue pcscf_force_service_routes("location"); } # add IBCF/THIG route here if required # Check for "sec-agree" in the Require header: if (is_present_hf("Require") && $hdr(Require) =~ ".*sec-agree.*") { # Remove the old Require-Header: remove_hf("Require"); # Replace ", sec-agree" with "" $var(new_hdr) = $(hdr(Require){re.subst,/[, ]*sec-agree//gi}); if ($(var(new_hdr){s.len}) > 0) { append_hf("Require: $var(new_hdr)\r\n"); } } # Check for "sec-agree" in the Proxy-Require header: if (is_present_hf("Proxy-Require") && $hdr(Proxy-Require) =~ ".*sec-agree.*") { # Remove the old Proxy-Require-Header: remove_hf("Proxy-Require"); # Replace ", sec-agree" with "" $var(new_hdr) = $(hdr(Proxy-Require){re.subst,/[, ]*sec-agree//gi}); if ($(var(new_hdr){s.len}) > 0) { append_hf("Proxy-Require: $var(new_hdr)\r\n"); } } remove_hf("Security-Verify"); #route(add_charging_vector); if (!t_relay()) { send_reply("500","Error forwarding originating standalone request"); break; }; break; } ###################################################################### # Terminating, Initial requests ###################################################################### route[Term_Initial] { xlog("L_DBG", "IMS: INSIDE TERM_INITIAL\n"); loose_route(); $avp(RR_CUSTOM_USER_AVP)="mt"; record_route(); set_dlg_profile("term"); t_on_reply("Term_Initial_reply"); t_on_failure("Term_Initial_failure"); #!ifdef WITH_RX xlog("L_DBG","Diameter: Orig authorizing media via Rx\n"); Rx_AAR("location"); if ($avp(s:aar_return_code) != 1) { xlog("L_ERR", "Diameter: AAR failed\n"); send_reply("403", "QoS not authorized"); exit; } #!endif # Do RTP-Relaying, if necessary: route(RTPPROXY_TERM); if (!t_relay()) { xlog("L_ERR", "Failure\n"); send_reply("500","Error forwarding terminating initial request"); break; }; } ###################################################################### # Replies to terminating, initial requests ###################################################################### onreply_route[Term_Initial_reply] { #!ifdef WITH_RX if (t_check_status("180|183|200")){ xlog("L_DBG","Diameter Term authorizing media via Rx\n"); if (!Rx_AAR("term")) { xlog("L_ERR", "IMS: AAR failed Term\n"); dlg_terminate("all", "Sorry no QoS available"); } else { xlog("L_DBG", "Diameter: Term AAR success on media authorization\n"); } xlog("L_DBG", "IMS: SENDING EARLY BYE\n"); } #!endif # Do RTP-Relaying, if necessary: route(RTPPROXY_TERM); } ###################################################################### # Replies to terminating, initial requests ###################################################################### failure_route[Term_Initial_failure] { if (t_is_canceled()) { exit; } if (t_check_status("408")) { send_reply("404","User offline"); exit; } } ###################################################################### # Terminating, Standalone requests ###################################################################### route[Term_Standalone] { xlog("L_DBG", "IMS: INSIDE TERM_STANDALONE\n"); # loose_route(); if (!t_relay()) { sl_send_reply("500","Error forwarding terminating standalone request"); break; }; } ###################################################################### # Terminating, Subsequent requests ###################################################################### route[Term_Subsequent] { xlog("L_DBG", "IMS: INSIDE TERM_SUBSEQUENT and request is $rm from IP $si\n"); loose_route(); # Check for "sec-agree" in the Require header: if (is_present_hf("Require") && $hdr(Require) =~ ".*sec-agree.*") { # Remove the old Require-Header: remove_hf("Require"); # Replace ", sec-agree" with "" $var(new_hdr) = $(hdr(Require){re.subst,/[, ]*sec-agree//gi}); if ($(var(new_hdr){s.len}) > 0) { append_hf("Require: $var(new_hdr)\r\n"); } } # Check for "sec-agree" in the Proxy-Require header: if (is_present_hf("Proxy-Require") && $hdr(Proxy-Require) =~ ".*sec-agree.*") { # Remove the old Proxy-Require-Header: remove_hf("Proxy-Require"); # Replace ", sec-agree" with "" $var(new_hdr) = $(hdr(Proxy-Require){re.subst,/[, ]*sec-agree//gi}); if ($(var(new_hdr){s.len}) > 0) { append_hf("Proxy-Require: $var(new_hdr)\r\n"); } } remove_hf("Security-Verify"); # Do RTP-Relaying, if necessary: route(RTPPROXY_TERM); # Use Reply route for optional RTP-Relay: t_on_reply("Term_Subsequent_reply"); if (!t_relay()) { send_reply("500","Error forwarding terminating subsequent request"); break; }; } ###################################################################### # Replies to terminating, Subsequent requests ###################################################################### onreply_route[Term_Subsequent_reply] { xlog("L_DBG", "IMS: INSIDE TERM_SUBSEQUENT_reply and request is $rm from IP $si\n"); # Do RTP-Relaying, if necessary: route(RTPPROXY_ORIG); } kamailio-4.0.4/examples/pcscf/pcscf.cfg0000644000000000000000000000337512223032461016501 0ustar rootroot# IP-Adress for incoming SIP-Traffic, in the following format: ##!define NETWORK_INTERFACE 192.168.0.1 ##!define NETWORK_INTERFACE_2 [some ipv6 address maybe?] # Port, where we listen to Traffic #!define PORT 5060 #!subst "/NETWORKNAME/kamailio-ims.org/" #!subst "/HOSTNAME/pcscf.kamailio-ims.org/" #!define HOSTNAME_IP pcscf.kamailio-ims.org #!define HOSTNAME_ESC "pcscf\.kamailio-ims\.org" # SIP-Address of capturing node, if not set, capturing is disabled. ##!define CAPTURE_NODE "sip:10.0.6.1" # IP-Adress(es) of the RTP-Proxy #!define RTPPROXY_ADDRESS "udp:127.0.0.1:22222" # # Several features can be enabled using '#!define WITH_FEATURE' directives: # # *** To run in debug mode: # - define WITH_DEBUG # # *** To enable nat traversal execute: # - define WITH_NAT # - define the connection to the RTP-Proxy: RTPPROXY_ADDRESS # - install RTPProxy: http://www.rtpproxy.org # - start RTPProxy: # rtpproxy -l _your_public_ip_ -s udp:localhost:7722 # # *** To enable TLS support execute: # - adjust CFGDIR/tls.cfg as needed # - define WITH_TLS # # *** To enable XMLRPC support execute: # - define WITH_XMLRPC # - adjust route[XMLRPC] for access policy # # *** To enable anti-flood detection execute: # - adjust pike and htable=>ipban settings as needed (default is # block if more than 16 requests in 2 seconds and ban for 300 seconds) # - define WITH_ANTIFLOOD # # *** To enable the Rx-Interface: # - Configure Rx-Diameter-Interface in pcscf.xml # - define WITH_RX # # *** To enable a Homer SIP-Capter-Node: # - define CAPTURE_NODE with a proper address # # Enabled Features for this host: ##!define WITH_DEBUG ##!define WITH_NAT ##!define WITH_TLS #!define WITH_XMLRPC #!define WITH_ANTIFLOOD ##!define WITH_RX kamailio-4.0.4/examples/pcscf/tls.cfg0000644000000000000000000000061512223032460016176 0ustar rootroot# # TLS Configuration File # # This is the default server domain, settings # in this domain will be used for all incoming # connections that do not match any other server # domain in this configuration file. # [server:default] method = SSLv23 verify_certificate = no require_certificate = no private_key = /etc/kamailio/kamailio-selfsigned.key certificate = /etc/kamailio/kamailio-selfsigned.pem kamailio-4.0.4/examples/pcscf/pcscf.xml0000644000000000000000000000123612223032460016533 0ustar rootroot kamailio-4.0.4/examples/web_im/0000755000000000000000000000000012223032460015055 5ustar rootrootkamailio-4.0.4/examples/web_im/README0000644000000000000000000000041312223032460015733 0ustar rootroot# # $Id$ # This examle illustrate how to use ser's FIFO interface to initate sending an instant message from a webpage. To enable this example, you need - web server with PHP support - install the example webpages on the server - have running ser with enabled fifo kamailio-4.0.4/examples/web_im/click_to_dial.html0000644000000000000000000000113312223032460020521 0ustar rootroot

Click-To-Dial

Click-To-Dial (using REFER)

Unfortunately, this example does not work. The reason is use of REFER for third-party call-control has not been standardized due to resistance of proponents of B2BUA use (which is somewhat bloated and not always operational).
Caller's SIP Address
Callee's SIP Address
Click to dial
kamailio-4.0.4/examples/web_im/send_im.html0000644000000000000000000000061112223032460017357 0ustar rootroot
Send IM

Send IM

SIP Address
Example: sip:gwb@iptel.org
Message
Click to send
kamailio-4.0.4/examples/web_im/send_im.php0000644000000000000000000000231612223032460017206 0ustar rootroot
Send IM Status

Send IM Status

"; /* open fifo now */ $fifo_handle=fopen( $fifo, "w" ); if (!$fifo_handle) { exit ("Sorry -- cannot open fifo: ".$fifo); } /* construct FIFO command */ $fifo_cmd=":t_uac_dlg:".$myfilename."\n". "MESSAGE\n".$sip_address."\n.\n". "From: sip:sender@foo.bar\n". "To: ".$sip_address."\n". "p-version: ".$signature."\n". "Contact: ".$web_contact."\n". "Content-Type: text/plain; charset=UTF-8\n.\n". $instant_message."\n.\n"; /* create fifo for replies */ system("mkfifo -m 666 ".$mypath ); /* write fifo command */ if (fwrite( $fifo_handle, $fifo_cmd)==-1) { unlink($mypath); fclose($fifo_handle); exit("Sorry -- fifo writing error"); } fclose($fifo_handle); /* read output now */ if (readfile( $mypath )==-1) { unlink($mypath); exit("Sorry -- fifo reading error"); } unlink($mypath); echo "

Thank you for using IM

"; ?> kamailio-4.0.4/examples/web_im/click_to_dial.php0000644000000000000000000000227212223032460020351 0ustar rootroot

Click-To-Dial

Click-To-Dial

"; /* open fifo now */ $fifo_handle=fopen( $fifo, "w" ); if (!$fifo_handle) { exit ("Sorry -- cannot open fifo: ".$fifo); } /* construct FIFO command */ $fifo_cmd=":t_uac:".$myfilename."\n". "REFER\n".$caller."\n". "p-version: ".$signature."\n". "Contact: ".$web_contact."\n". "Referred-By: ".$web_contact."\n". "Refer-To: ".$callee."\n". "\n". /* EoHeader */ ".\n\n"; /* EoFifoRequest */ /* create fifo for replies */ system("mkfifo -m 666 ".$mypath ); /* write fifo command */ if (fwrite( $fifo_handle, $fifo_cmd)==-1) { unlink($mypath); fclose($fifo_handle); exit("Sorry -- fifo writing error"); } fclose($fifo_handle); /* read output now */ if (readfile( $mypath )==-1) { unlink($mypath); exit("Sorry -- fifo reading error"); } unlink($mypath); echo "

Thank you for using click-to-dial

"; ?> kamailio-4.0.4/examples/pstn.cfg0000644000000000000000000001021212223032460015254 0ustar rootroot# # $Id$ # # example: ser configured as PSTN gateway guard; PSTN gateway is located # at 192.168.0.10 # # ------------------ module loading ---------------------------------- loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" loadmodule "modules/acc/acc.so" loadmodule "modules/rr/rr.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/mysql/mysql.so" loadmodule "modules/auth/auth.so" loadmodule "modules/auth_db/auth_db.so" loadmodule "modules/group/group.so" loadmodule "modules/uri/uri.so" # ----------------- setting module-specific parameters --------------- modparam("auth_db", "db_url","mysql://ser:heslo@localhost/ser") modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") # -- acc params -- modparam("acc", "log_level", 1) # that is the flag for which we will account -- don't forget to # set the same one :-) modparam("acc", "log_flag", 1 ) # ------------------------- request routing logic ------------------- # main routing logic route{ /* ********* ROUTINE CHECKS ********************************** */ # filter too old messages if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; if (len_gt( max_len )) { sl_send_reply("513", "Wow -- Message too large"); break; }; /* ********* RR ********************************** */ /* grant Route routing if route headers present */ if (loose_route()) { t_relay(); break; }; /* record-route INVITEs -- all subsequent requests must visit us */ if (method=="INVITE") { record_route(); }; # now check if it really is a PSTN destination which should be handled # by our gateway; if not, and the request is an invitation, drop it -- # we cannot terminate it in PSTN; relay non-INVITE requests -- it may # be for example BYEs sent by gateway to call originator if (!uri=~"sip:\+?[0-9]+@.*") { if (method=="INVITE") { sl_send_reply("403", "Call cannot be served here"); } else { forward(uri:host, uri:port); }; break; }; # account completed transactions via syslog setflag(1); # free call destinations ... no authentication needed if ( is_user_in("Request-URI", "free-pstn") /* free destinations */ | uri=~"sip:[79][0-9][0-9][0-9]@.*" /* local PBX */ | uri=~"sip:98[0-9][0-9][0-9][0-9]") { log("free call"); } else if (src_ip==192.168.0.10) { # our gateway doesn't support digest authentication; # verify that a request is coming from it by source # address log("gateway-originated request"); } else { # in all other cases, we need to check the request against # access control lists; first of all, verify request # originator's identity if (!proxy_authorize( "gateway" /* realm */, "subscriber" /* table name */)) { proxy_challenge( "gateway" /* realm */, "0" /* no qop */ ); break; }; # authorize only for INVITEs -- RR/Contact may result in weird # things showing up in d-uri that would break our logic; our # major concern is INVITE which causes PSTN costs if (method=="INVITE") { # does the authenticated user have a permission for local # calls (destinations beginning with a single zero)? # (i.e., is he in the "local" group?) if (uri=~"sip:0[1-9][0-9]+@.*") { if (!is_user_in("credentials", "local")) { sl_send_reply("403", "No permission for local calls"); break; }; # the same for long-distance (destinations begin with two zeros") } else if (uri=~"sip:00[1-9][0-9]+@.*") { if (!is_user_in("credentials", "ld")) { sl_send_reply("403", " no permission for LD "); break; }; # the same for international calls (three zeros) } else if (uri=~"sip:000[1-9][0-9]+@.*") { if (!is_user_in("credentials", "int")) { sl_send_reply("403", "International permissions needed"); break; }; # everything else (e.g., interplanetary calls) is denied } else { sl_send_reply("403", "Forbidden"); break; }; }; # INVITE to authorized PSTN }; # authorized PSTN # if you have passed through all the checks, let your call go to GW! rewritehostport("192.168.0.10:5060"); # forward the request now if (!t_relay()) { sl_reply_error(); break; }; } kamailio-4.0.4/examples/exec_s3.cfg0000644000000000000000000000130712223032460015626 0ustar rootroot# # $Id$ # # email notification to email address from mysql database # # ------------------ module loading ---------------------------------- loadmodule "modules/exec/exec.so" loadmodule "modules/sl/sl.so" # send email if a request arrives route[0] { if (!exec_msg(' QUERY="select email_address from subscriber where user=\"$SIP_OUSER\""; EMAIL=`mysql -Bsuser -pheslo -e "$QUERY" ser`; if [ -z "$EMAIL" ] ; then exit 1; fi ; echo "SIP request received from $SIP_HF_FROM for $SIP_OUSER" | mail -s "request for you" $EMAIL ')) { # exec returned error ... user does not exist sl_send_reply("404", "User does not exist"); } else { sl_send_reply("600", "No messages for this user"); }; } kamailio-4.0.4/examples/ccdiversion.cfg0000644000000000000000000000153512223032460016610 0ustar rootroot# $Id$ # ------------------ module loading ---------------------------------- loadmodule "modules/textops/textops.so" loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" # ----------------- setting module-specific parameters --------------- route{ # if we do not get a positive reply, continue at reply_route[2] t_on_failure("2"); # forward the request to all destinations in destination set now t_relay(); } failure_route[2] { # request failed (no reply, busy, whatever) ... forward it again # to pbx's voicemail at phone number 6000 via Cisco gateway at # 192.168.10.10; append proprietary CC-Diversion header field with # original request uri in it, so that the gateway and voicemail # know, whom the request was originally intended for append_branch("sip:6000@192.168.10.10"); append_urihf("CC-Diversion: ", "\r\n"); t_relay(); } kamailio-4.0.4/examples/vm_proxy.cfg0000644000000000000000000000436512223032460016167 0ustar rootroot# # $Id$ # # simple proxy script for forwarding to voicemail server # for unavailable users # loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" loadmodule "modules/rr/rr.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/mysql/mysql.so" loadmodule "modules/group/group.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/registrar/registrar.so" # time to give up on ringing -- global timer, applies to # all transactions modparam("tm", "fr_inv_timer", 90) # database with user group membership modparam("group", "db_url", "mysql://ser:heslo@localhost/ser") # --------------------- request routing logic ------------------- route { if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483", "Alas Too Many Hops"); break; }; if (!(method=="REGISTER")) record_route(); if (loose_route()) { t_relay(); break; }; if (!uri==myself) { t_relay(); break; }; if (method == "REGISTER") { if (!save("location")) { sl_reply_error(); }; break; }; # does the user wish redirection on no availability? (i.e., is he # in the voicemail group?) -- determine it now and store it in # flag 4, before we rewrite the flag using UsrLoc if (is_user_in("Request-URI", "voicemail")) { setflag(4); }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { # handle user which was not found route(4); break; }; # if user is on-line and is in voicemail group, enable redirection if (method == "INVITE" && isflagset(4)) { t_on_failure("1"); }; t_relay(); } # ------------- handling of unavailable user ------------------ route[4] { # non-Voip -- just send "off-line" if (!(method == "INVITE" || method == "ACK" || method == "CANCEL")) { sl_send_reply("404", "Not Found"); break; }; # not voicemail subscriber if (!isflagset(4)) { sl_send_reply("404", "Not Found and no voicemail turned on"); break; }; # forward to voicemail now rewritehostport("bat.iptel.org:5090"); t_relay_to_udp("bat.iptel.org", "5090"); } # if forwarding downstream did not succeed, try voicemail running # at bat.iptel.org:5090 failure_route[1] { revert_uri(); rewritehostport("bat.iptel.org:5090"); append_branch(); t_relay_to_udp("bat.iptel.org", "5090"); } kamailio-4.0.4/examples/pi_framework.xml0000644000000000000000000002454212223032460017031 0ustar rootroot mysql://kamailio:kamailiorw@localhost/kamailio dispatcher mysql id DB1_INT setid DB1_INT destination DB1_STR URI_IPV4HOST flags DB1_INT priority DB1_INT attrs DB1_STR description DB1_STR dialplan mysql id DB1_INT dpid DB1_INT pr DB1_INT match_op DB1_INT match_exp DB1_STR match_len DB1_INT subst_exp DB1_STR repl_exp DB1_STR attrs DB1_STRING dispatcher show_destinations_with_small_setid dispatcher DB1_QUERY setid< id setid destination description show_all dispatcher DB1_QUERY id setid destination flags priority attrs description update_setid dispatcher DB1_UPDATE id= setid update_destination dispatcher DB1_UPDATE id= destination update_attr dispatcher DB1_UPDATE id= attrs update_description dispatcher DB1_UPDATE id= description add_gw dispatcher DB1_INSERT setid destination priority attrs description add_server_with_setid_100 dispatcher DB1_INSERT setid 100 destination priority attrs description delete_by_id dispatcher DB1_DELETE id= dialplan show_all dialplan DB1_QUERY id dpid pr match_op match_exp subst_exp repl_exp attrs id show_dpid dialplan DB1_QUERY dpid= id dpid pr match_op match_exp match_len subst_exp repl_exp attrs id show_exact_matching dialplan DB1_QUERY match_op= 0 id dpid pr match_op match_exp match_len subst_exp repl_exp attrs show_regex_matching dialplan DB1_QUERY match_op= 1 id dpid pr match_op match_exp match_len subst_exp repl_exp attrs add dialplan DB1_INSERT dpid pr match_op 0 1 match_exp match_len attrs delete dialplan DB1_DELETE id= update_attr dialplan DB1_UPDATE id= attrs update_repl_exp dialplan DB1_UPDATE id= repl_exp replace dialplan DB1_REPLACE dpid pr match_op match_exp match_len subst_exp repl_exp attrs kamailio-4.0.4/examples/onr.cfg0000644000000000000000000000345712223032460015103 0ustar rootroot# # $Id$ # # example script showing both types of forking; # incoming message is forked in parallel to # 'nobody' and 'parallel', if no positive reply # appears with final_response timer, nonsense # is retried (serial forking); than, destination # 'foo' is given last chance # ------------------ module loading ---------------------------------- loadmodule "modules/sl/sl.so" loadmodule "modules/tm/tm.so" # ----------------- setting module-specific parameters --------------- # -- tm params -- # set time for which ser will be waiting for a final response; # fr_inv_timer sets value for INVITE transactions, fr_timer # for all others modparam("tm", "fr_inv_timer", 15 ) modparam("tm", "fr_timer", 10 ) # ------------------------- request routing logic ------------------- # main routing logic route{ # for testing purposes, simply okay all REGISTERs if (method=="REGISTER") { log("REGISTER"); sl_send_reply("200", "ok"); break; }; # try these two destinations first in parallel; the second # destination is targeted to sink port -- that will make ser # wait until timer hits seturi("sip:nobody@iptel.org"); append_branch("sip:parallel@iptel.org:9"); # if we do not get a positive reply, continue at reply_route[1] t_on_failure("1"); # forward the request to all destinations in destination set now t_relay(); } failure_route[1] { # forwarding failed -- try again at another destination append_branch("sip:nonsense@iptel.org"); log(1,"first redirection\n"); # if this alternative destination fails too, proceed to reply_route[2] t_on_failure("2"); t_relay(); } failure_route[2] { # try out the last resort destination append_branch("sip:foo@iptel.org"); log(1, "second redirection\n"); # we no more call t_on_negative here; if this destination # fails too, transaction will complete t_relay(); } kamailio-4.0.4/counters.c0000644000000000000000000005413212223032457014016 0ustar rootroot/* * Copyright (C) 2010 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @brief counters/stats * @file * @ingroup: core */ /* * History: * -------- * 2010-08-06 initial version (andrei) * 2010-08-24 counters can be used (inc,add) before prefork_init (andrei) */ #include "counters.h" #include "str_hash.h" #include "str.h" #include "compiler_opt.h" #include "mem/mem.h" #include "mem/shm_mem.h" #define CNT_HASH_SIZE 64 /* group hash size (rpc use) */ #define GRP_HASH_SIZE 16 /* initial sorted groups array size (rpc use) */ #define GRP_SORTED_SIZE 16 /* intial counter id 2 record array size */ #define CNT_ID2RECORD_SIZE 64 #define CACHELINE_PAD 128 /* leave space for one flag */ #define MAX_COUNTER_ID 32767 /* size (number of entries) of the temporary array used for keeping stats pre-prefork init. Note: if more counters are registered then this size, the array will be dynamically increased (doubled each time). The value here is meant only to optimize startup/memory fragmentation. */ #define PREINIT_CNTS_VALS_SIZE 128 struct counter_record { str group; str name; counter_handle_t h; unsigned short flags; void* cbk_param; counter_cbk_f cbk; struct counter_record* grp_next; /* next in group */ str doc; }; struct grp_record { str group; struct counter_record* first; }; /** hash table mapping a counter name to an id */ static struct str_hash_table cnts_hash_table; /** array maping id 2 record */ struct counter_record** cnt_id2record; static int cnt_id2record_size; /** hash table for groups (maps a group name to a counter list) */ static struct str_hash_table grp_hash_table; /** array of groups, sorted */ static struct grp_record** grp_sorted; static int grp_sorted_max_size; static int grp_sorted_crt_size; static int grp_no; /* number of groups */ /** counters array. a[proc_no][counter_id] => _cnst_vals[proc_no*cnts_no+counter_id] */ counter_array_t* _cnts_vals; int _cnts_row_len; /* number of elements per row */ static int cnts_no; /* number of registered counters */ static int cnts_max_rows; /* set to 0 if not yet fully init */ /** init the coutner hash table(s). * @return 0 on success, -1 on error. */ int init_counters() { if (str_hash_alloc(&cnts_hash_table, CNT_HASH_SIZE) < 0) goto error; str_hash_init(&cnts_hash_table); if (str_hash_alloc(&grp_hash_table, GRP_HASH_SIZE) < 0) goto error; str_hash_init(&grp_hash_table); cnts_no = 1; /* start at 1 (0 used only for invalid counters) */ cnts_max_rows = 0; /* 0 initially, !=0 after full init (counters_prefork_init()) */ grp_no = 0; cnt_id2record_size = CNT_ID2RECORD_SIZE; cnt_id2record = pkg_malloc(sizeof(*cnt_id2record) * cnt_id2record_size); if (cnt_id2record == 0) goto error; memset(cnt_id2record, 0, sizeof(*cnt_id2record) * cnt_id2record_size); grp_sorted_max_size = GRP_SORTED_SIZE; grp_sorted_crt_size = 0; grp_sorted = pkg_malloc(sizeof(*grp_sorted) * grp_sorted_max_size); if (grp_sorted == 0) goto error; memset(grp_sorted, 0, sizeof(*grp_sorted) * grp_sorted_max_size); return 0; error: destroy_counters(); return -1; } void destroy_counters() { int r; struct str_hash_entry* e; struct str_hash_entry* bak; if (_cnts_vals) { if (cnts_max_rows) /* fully init => it is in shm */ shm_free(_cnts_vals); else /* partially init (before prefork) => pkg */ pkg_free(_cnts_vals); _cnts_vals = 0; } if (cnts_hash_table.table) { for (r=0; r< cnts_hash_table.size; r++) { clist_foreach_safe(&cnts_hash_table.table[r], e, bak, next) { pkg_free(e); } } pkg_free(cnts_hash_table.table); } if (grp_hash_table.table) { for (r=0; r< grp_hash_table.size; r++) { clist_foreach_safe(&grp_hash_table.table[r], e, bak, next) { pkg_free(e); } } pkg_free(grp_hash_table.table); } if (cnt_id2record) pkg_free(cnt_id2record); if (grp_sorted) pkg_free(grp_sorted); cnts_hash_table.table = 0; cnts_hash_table.size = 0; cnt_id2record = 0; grp_sorted = 0; grp_hash_table.table = 0; grp_hash_table.size = 0; grp_sorted_crt_size = 0; grp_sorted_max_size = 0; cnts_no = 0; _cnts_row_len = 0; cnts_max_rows = 0; grp_no = 0; } /** complete counter intialization, when the number of processes is known. * shm must be available. * @return 0 on success, < 0 on error */ int counters_prefork_init(int max_process_no) { counter_array_t* old; int size, row_size; counter_handle_t h; /* round cnts_no so that cnts_no * sizeof(counter) it's a CACHELINE_PAD multiple */ /* round-up row_size to a CACHELINE_PAD multiple if needed */ row_size = ((sizeof(*_cnts_vals) * cnts_no - 1) / CACHELINE_PAD + 1) * CACHELINE_PAD; /* round-up the resulted row_siue to a sizeof(*_cnts_vals) multiple */ row_size = ((row_size -1) / sizeof(*_cnts_vals) + 1) * sizeof(*_cnts_vals); /* get updated cnts_no (row length) */ _cnts_row_len = row_size / sizeof(*_cnts_vals); size = max_process_no * row_size; /* replace the temporary pre-fork pkg array (with only 1 row) with the final shm version (with max_process_no rows) */ old = _cnts_vals; _cnts_vals = shm_malloc(size); if (_cnts_vals == 0) return -1; memset(_cnts_vals, 0, size); cnts_max_rows = max_process_no; /* copy prefork values into the newly shm array */ if (old) { for (h.id = 0; h.id < cnts_no; h.id++) counter_pprocess_val(process_no, h) = old[h.id].v; pkg_free(old); } return 0; } /** adds new group to the group hash table (no checks, internal version). * @return pointer to new group record on success, 0 on error. */ static struct grp_record* grp_hash_add(str* group) { struct str_hash_entry* g; struct grp_record* grp_rec; struct grp_record** r; /* grp_rec copied at &g->u.data */ g = pkg_malloc(sizeof(struct str_hash_entry) - sizeof(g->u.data) + sizeof(*grp_rec) + group->len + 1); if (g == 0) goto error; grp_rec = (struct grp_record*)&g->u.data[0]; grp_rec->group.s = (char*)(grp_rec + 1); grp_rec->group.len = group->len; grp_rec->first = 0; memcpy(grp_rec->group.s, group->s, group->len + 1); g->key = grp_rec->group; g->flags = 0; /* insert group into the sorted group array */ if (grp_sorted_max_size <= grp_sorted_crt_size) { /* must increase the array */ r = pkg_realloc(grp_sorted, 2 * grp_sorted_max_size * sizeof(*grp_sorted)); if (r == 0) goto error; grp_sorted= r; grp_sorted_max_size *= 2; memset(&grp_sorted[grp_sorted_crt_size], 0, (grp_sorted_max_size - grp_sorted_crt_size) * sizeof(*grp_sorted)); } for (r = grp_sorted; r < (grp_sorted + grp_sorted_crt_size); r++) if (strcmp(grp_rec->group.s, (*r)->group.s) < 0) break; if (r != (grp_sorted + grp_sorted_crt_size)) memmove(r+1, r, (int)(long)((char*)(grp_sorted + grp_sorted_crt_size) - (char*)r)); grp_sorted_crt_size++; *r = grp_rec; /* insert into the hash only on success */ str_hash_add(&grp_hash_table, g); return grp_rec; error: if (g) pkg_free(g); return 0; } /** lookup a group into the group hash (internal version). * @return pointer to grp_record on success, 0 on failure (not found). */ static struct grp_record* grp_hash_lookup(str* group) { struct str_hash_entry* e; e = str_hash_get(&grp_hash_table, group->s, group->len); return e?(struct grp_record*)&e->u.data[0]:0; } /** lookup a group and if not found create a new group record. * @return pointer to grp_record on succes, 0 on failure ( not found and * failed to create new group record). */ static struct grp_record* grp_hash_get_create(str* group) { struct grp_record* ret; ret = grp_hash_lookup(group); if (ret) return ret; return grp_hash_add(group); } /** adds new counter to the hash table (no checks, internal version). * @return pointer to new record on success, 0 on error. */ static struct counter_record* cnt_hash_add( str* group, str* name, int flags, counter_cbk_f cbk, void* param, const char* doc) { struct str_hash_entry* e; struct counter_record* cnt_rec; struct grp_record* grp_rec; struct counter_record** p; counter_array_t* v; int doc_len; int n; e = 0; if (cnts_no >= MAX_COUNTER_ID) /* too many counters */ goto error; grp_rec = grp_hash_get_create(group); if (grp_rec == 0) /* non existing group an no new one could be created */ goto error; doc_len = doc?strlen(doc):0; /* cnt_rec copied at &e->u.data[0] */ e = pkg_malloc(sizeof(struct str_hash_entry) - sizeof(e->u.data) + sizeof(*cnt_rec) + name->len + 1 + group->len + 1 + doc_len + 1); if (e == 0) goto error; cnt_rec = (struct counter_record*)&e->u.data[0]; cnt_rec->group.s = (char*)(cnt_rec + 1); cnt_rec->group.len = group->len; cnt_rec->name.s = cnt_rec->group.s + group->len + 1; cnt_rec->name.len = name->len; cnt_rec->doc.s = cnt_rec->name.s + name->len +1; cnt_rec->doc.len = doc_len; cnt_rec->h.id = cnts_no++; cnt_rec->flags = flags; cnt_rec->cbk_param = param; cnt_rec->cbk = cbk; cnt_rec->grp_next = 0; memcpy(cnt_rec->group.s, group->s, group->len + 1); memcpy(cnt_rec->name.s, name->s, name->len + 1); if (doc) memcpy(cnt_rec->doc.s, doc, doc_len + 1); else cnt_rec->doc.s[0] = 0; e->key = cnt_rec->name; e->flags = 0; /* check to see if it fits in the prefork tmp. vals array. This array contains only one "row", is allocated in pkg and is used only until counters_prefork_init() (after that the array is replaced with a shm version with all the needed rows). */ if (cnt_rec->h.id >= _cnts_row_len || _cnts_vals == 0) { /* array to small or not yet allocated => reallocate/allocate it (min size PREINIT_CNTS_VALS_SIZE, max MAX_COUNTER_ID) */ n = (cnt_rec->h.id < PREINIT_CNTS_VALS_SIZE) ? PREINIT_CNTS_VALS_SIZE : ((2 * (cnt_rec->h.id + (cnt_rec->h.id == 0)) < MAX_COUNTER_ID)? (2 * (cnt_rec->h.id + (cnt_rec->h.id == 0))) : MAX_COUNTER_ID + 1); v = pkg_realloc(_cnts_vals, n * sizeof(*_cnts_vals)); if (v == 0) /* realloc/malloc error */ goto error; _cnts_vals = v; /* zero newly allocated memory */ memset(&_cnts_vals[_cnts_row_len], 0, (n - _cnts_row_len) * sizeof(*_cnts_vals)); _cnts_row_len = n; /* record new length */ } /* add a pointer to it in the records array */ if (cnt_id2record_size <= cnt_rec->h.id) { /* must increase the array */ p = pkg_realloc(cnt_id2record, 2 * cnt_id2record_size * sizeof(*cnt_id2record)); if (p == 0) goto error; cnt_id2record = p; cnt_id2record_size *= 2; memset(&cnt_id2record[cnt_rec->h.id], 0, (cnt_id2record_size - cnt_rec->h.id) * sizeof(*cnt_id2record)); } cnt_id2record[cnt_rec->h.id] = cnt_rec; /* add into the hash */ str_hash_add(&cnts_hash_table, e); /* insert it sorted in the per group list */ for (p = &grp_rec->first; *p; p = &((*p)->grp_next)) if (strcmp(cnt_rec->name.s, (*p)->name.s) < 0) break; cnt_rec->grp_next = *p; *p = cnt_rec; return cnt_rec; error: if (e) pkg_free(e); return 0; } /** lookup a (group, name) pair into the cnts hash (internal version). * @param group - counter group name. If "" the first matching counter with * the given name will be returned (k compat). * @param name * @return pointer to counter_record on success, 0 on failure (not found). */ static struct counter_record* cnt_hash_lookup(str* group, str* name) { struct str_hash_entry* e; struct str_hash_entry* first; struct counter_record* cnt_rec; e = str_hash_get(&cnts_hash_table, name->s, name->len); /* fast path */ if (likely(e)) { cnt_rec = (struct counter_record*)&e->u.data[0]; if (likely( group->len == 0 || (cnt_rec->group.len == group->len && memcmp(cnt_rec->group.s, group->s, group->len) == 0))) return cnt_rec; } else return 0; /* search between records with same name, but different groups */ first = e; do { cnt_rec = (struct counter_record*)&e->u.data[0]; if (cnt_rec->group.len == group->len && cnt_rec->name.len == name->len && memcmp(cnt_rec->group.s, group->s, group->len) == 0 && memcmp(cnt_rec->name.s, name->s, name->len) == 0) /* found */ return cnt_rec; e = e->next; } while(e != first); return 0; } /** lookup a counter and if not found create a new counter record. * @return pointer to counter_record on succes, 0 on failure ( not found and * failed to create new group record). */ static struct counter_record* cnt_hash_get_create( str* group, str* name, int flags, counter_cbk_f cbk, void* param, const char* doc) { struct counter_record* ret; ret = cnt_hash_lookup(group, name); if (ret) return ret; return cnt_hash_add(group, name, flags, cbk, param, doc); } /** register a new counter. * Can be called only before forking (e.g. from mod_init() or * init_child(PROC_INIT)). * @param handle - result parameter, it will be filled with the counter * handle on success (can be null if not needed). * @param group - group name * @param name - counter name (group.name must be unique). * @param flags - counter flags: one of CNT_F_*. * @param cbk - read callback function (if set it will be called each time * someone will call counter_get()). * @param cbk_param - callback param. * @param doc - description/documentation string. * @param reg_flags - register flags: 1 - don't fail if counter already * registered (act like counter_lookup(handle, group, name). * @return 0 on succes, < 0 on error (-1 not init or malloc error, -2 already * registered (and register_flags & 1 == 0). */ int counter_register( counter_handle_t* handle, const char* group, const char* name, int flags, counter_cbk_f cbk, void* cbk_param, const char* doc, int reg_flags) { str grp; str n; struct counter_record* cnt_rec; if (unlikely(cnts_max_rows)) { /* too late */ BUG("late attempt to register counter: %s.%s\n", group, name); goto error; } n.s = (char*)name; n.len = strlen(name); if (unlikely(group == 0 || *group == 0)) { BUG("attempt to register counter %s without a group\n", name); goto error; } grp.s = (char*)group; grp.len = strlen(group); cnt_rec = cnt_hash_lookup(&grp, &n); if (cnt_rec) { if (reg_flags & 1) goto found; else { if (handle) handle->id = 0; return -2; } } else cnt_rec = cnt_hash_get_create(&grp, &n, flags, cbk, cbk_param, doc); if (unlikely(cnt_rec == 0)) goto error; found: if (handle) *handle = cnt_rec->h; return 0; error: if (handle) handle->id = 0; return -1; } /** fill in the handle of an existing counter (str parameters). * @param handle - filled with the corresp. handle on success. * @param group - counter group name. If "" the first matching * counter with the given name will be returned * (k compat). * @param name - counter name. * @return 0 on success, < 0 on error */ int counter_lookup_str(counter_handle_t* handle, str* group, str* name) { struct counter_record* cnt_rec; cnt_rec = cnt_hash_lookup(group, name); if (likely(cnt_rec)) { *handle = cnt_rec->h; return 0; } handle->id = 0; return -1; } /** fill in the handle of an existing counter (asciiz parameters). * @param handle - filled with the corresp. handle on success. * @param group - counter group name. If 0 or "" the first matching * counter with the given name will be returned * (k compat). * @param name - counter name. * @return 0 on success, < 0 on error */ int counter_lookup(counter_handle_t* handle, const char* group, const char* name) { str grp; str n; n.s = (char*)name; n.len = strlen(name); grp.s = (char*)group; grp.len = group?strlen(group):0; return counter_lookup_str(handle, &grp, &n); } /** register all the counters declared in a null-terminated array. * @param group - counters group. * @param defs - null terminated array containing counters definitions. * @return 0 on success, < 0 on error ( - (counter_number+1)) */ int counter_register_array(const char* group, counter_def_t* defs) { int r; for (r=0; defs[r].name; r++) if (counter_register( defs[r].handle, group, defs[r].name, defs[r].flags, defs[r].get_cbk, defs[r].get_cbk_param, defs[r].descr, 0) <0) return -(r+1); /* return - (idx of bad counter + 1) */ return 0; } /** get the value of the counter, bypassing callbacks. * @param handle - counter handle obtained using counter_lookup() or * counter_register(). * @return counter value. */ counter_val_t counter_get_raw_val(counter_handle_t handle) { int r; counter_val_t ret; if (unlikely(_cnts_vals == 0)) { /* not init yet */ BUG("counters not fully initialized yet\n"); return 0; } if (unlikely(handle.id >= cnts_no || (short)handle.id < 0)) { BUG("invalid counter id %d (max %d)\n", handle.id, cnts_no - 1); return 0; } ret = 0; for (r = 0; r < cnts_max_rows; r++) ret += counter_pprocess_val(r, handle); return ret; } /** get the value of the counter, using the callbacks (if defined). * @param handle - counter handle obtained using counter_lookup() or * counter_register(). * @return counter value. */ counter_val_t counter_get_val(counter_handle_t handle) { struct counter_record* cnt_rec; if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) { /* not init yet */ BUG("counters not fully initialized yet\n"); return 0; } cnt_rec = cnt_id2record[handle.id]; if (unlikely(cnt_rec->cbk)) return cnt_rec->cbk(handle, cnt_rec->cbk_param); return counter_get_raw_val(handle); } /** reset the counter. * Reset a counter, unless it has the CNT_F_NO_RESET flag set. * @param handle - counter handle obtained using counter_lookup() or * counter_register(). * Note: it's racy. */ void counter_reset(counter_handle_t handle) { int r; if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) { /* not init yet */ BUG("counters not fully initialized yet\n"); return; } if (unlikely(handle.id >= cnts_no)) { BUG("invalid counter id %d (max %d)\n", handle.id, cnts_no - 1); return; } if (unlikely(cnt_id2record[handle.id]->flags & CNT_F_NO_RESET)) return; for (r=0; r < cnts_max_rows; r++) counter_pprocess_val(r, handle) = 0; return; } /** return the name for counter handle. * @param handle - counter handle obtained using counter_lookup() or * counter_register(). * @return asciiz pointer on success, 0 on error. */ char* counter_get_name(counter_handle_t handle) { if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) { /* not init yet */ BUG("counters not fully initialized yet\n"); goto error; } if (unlikely(handle.id >= cnts_no)) { BUG("invalid counter id %d (max %d)\n", handle.id, cnts_no - 1); goto error; } return cnt_id2record[handle.id]->name.s; error: return 0; } /** return the group name for counter handle. * @param handle - counter handle obtained using counter_lookup() or * counter_register(). * @return asciiz pointer on success, 0 on error. */ char* counter_get_group(counter_handle_t handle) { if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) { /* not init yet */ BUG("counters not fully initialized yet\n"); goto error; } if (unlikely(handle.id >= cnts_no)) { BUG("invalid counter id %d (max %d)\n", handle.id, cnts_no - 1); goto error; } return cnt_id2record[handle.id]->group.s; error: return 0; } /** return the description (doc) string for a given counter. * @param handle - counter handle obtained using counter_lookup() or * counter_register(). * @return asciiz pointer on success, 0 on error. */ char* counter_get_doc(counter_handle_t handle) { if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) { /* not init yet */ BUG("counters not fully initialized yet\n"); goto error; } if (unlikely(handle.id >= cnts_no)) { BUG("invalid counter id %d (max %d)\n", handle.id, cnts_no - 1); goto error; } return cnt_id2record[handle.id]->doc.s; error: return 0; } /** iterate on all the counter group names. * @param cbk - pointer to a callback function that will be called for each * group name. * @param p - parameter that will be passed to the callback function * (along the group name). */ void counter_iterate_grp_names(void (*cbk)(void* p, str* grp_name), void* p) { int r; for (r=0; r < grp_sorted_crt_size; r++) cbk(p, &grp_sorted[r]->group); } /** iterate on all the variable names in a specified group. * @param group - group name. * @param cbk - pointer to a callback function that will be called for each * variable name. * @param p - parameter that will be passed to the callback function * (along the variable name). */ void counter_iterate_grp_var_names( const char* group, void (*cbk)(void* p, str* var_name), void* p) { struct counter_record* r; struct grp_record* g; str grp; grp.s = (char*)group; grp.len = strlen(group); g = grp_hash_lookup(&grp); if (g) for (r = g->first; r; r = r->grp_next) cbk(p, &r->name); } /** iterate on all the variable names and handles in a specified group. * @param group - group name. * @param cbk - pointer to a callback function that will be called for each * [variable name, variable handle] pair. * @param p - parameter that will be passed to the callback function * (along the group name, variable name and variable handle). */ void counter_iterate_grp_vars(const char* group, void (*cbk)(void* p, str* g, str* n, counter_handle_t h), void *p) { struct counter_record* r; struct grp_record* g; str grp; grp.s = (char*)group; grp.len = strlen(group); g = grp_hash_lookup(&grp); if (g) for (r = g->first; r; r = r->grp_next) cbk(p, &r->group, &r->name, r->h); } /* vi: set ts=4 sw=4 tw=79:ai:cindent: */ kamailio-4.0.4/receive.c0000644000000000000000000002166512223032461013576 0ustar rootroot/* *$Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-29 transport-independent message zero-termination in * receive_msg (jiri) * 2003-02-07 undoed jiri's zero term. changes (they break tcp) (andrei) * 2003-02-10 moved zero-term in the calling functions (udp_receive & * tcp_read_req) * 2003-08-13 fixed exec_pre_cb returning 0 (backported from stable) (andrei) * 2004-02-06 added user preferences support - destroy_avps() (bogdan) * 2004-04-30 exec_pre_cb is called after basic sanity checks (at least one * via present & parsed ok) (andrei) * 2004-08-23 avp core changed - destroy_avp-> reset_avps (bogdan) * 2006-11-29 nonsip_msg hooks called for non-sip msg (e.g HTTP) (andrei) */ /*! * \file * \brief SIP-router core :: * \ingroup core * Module: \ref core */ #include #include #include #include "receive.h" #include "globals.h" #include "dprint.h" #include "route.h" #include "parser/msg_parser.h" #include "forward.h" #include "action.h" #include "mem/mem.h" #include "stats.h" #include "ip_addr.h" #include "script_cb.h" #include "nonsip_hooks.h" #include "dset.h" #include "usr_avp.h" #ifdef WITH_XAVP #include "xavp.h" #endif #include "select_buf.h" #include "tcp_server.h" /* for tcpconn_add_alias */ #include "tcp_options.h" /* for access to tcp_accept_aliases*/ #include "cfg/cfg.h" #include "core_stats.h" #ifdef DEBUG_DMALLOC #include #endif unsigned int msg_no=0; /* address preset vars */ str default_global_address={0,0}; str default_global_port={0,0}; str default_via_address={0,0}; str default_via_port={0,0}; /** * increment msg_no and return the new value */ unsigned int inc_msg_no(void) { return ++msg_no; } /* WARNING: buf must be 0 terminated (buf[len]=0) or some things might * break (e.g.: modules/textops) */ int receive_msg(char* buf, unsigned int len, struct receive_info* rcv_info) { struct sip_msg* msg; struct run_act_ctx ctx; int ret; #ifdef STATS int skipped = 1; struct timeval tvb, tve; struct timezone tz; unsigned int diff; #endif str inb; inb.s = buf; inb.len = len; sr_event_exec(SREV_NET_DATA_IN, (void*)&inb); len = inb.len; msg=pkg_malloc(sizeof(struct sip_msg)); if (msg==0) { LOG(L_ERR, "ERROR: receive_msg: no mem for sip_msg\n"); goto error00; } msg_no++; /* number of vias parsed -- good for diagnostic info in replies */ via_cnt=0; memset(msg,0, sizeof(struct sip_msg)); /* init everything to 0 */ /* fill in msg */ msg->buf=buf; msg->len=len; /* zero termination (termination of orig message bellow not that useful as most of the work is done with scratch-pad; -jiri */ /* buf[len]=0; */ /* WARNING: zero term removed! */ msg->rcv=*rcv_info; msg->id=msg_no; msg->pid=my_pid(); msg->set_global_address=default_global_address; msg->set_global_port=default_global_port; if(likely(sr_msg_time==1)) msg_set_time(msg); if (parse_msg(buf,len, msg)!=0){ LOG(cfg_get(core, core_cfg, corelog), "core parsing of SIP message failed (%s:%d/%d)\n", ip_addr2a(&msg->rcv.src_ip), (int)msg->rcv.src_port, (int)msg->rcv.proto); goto error02; } DBG("After parse_msg...\n"); /* ... clear branches from previous message */ clear_branches(); if (msg->first_line.type==SIP_REQUEST){ ruri_mark_new(); /* ruri is usable for forking (not consumed yet) */ if (!IS_SIP(msg)){ if ((ret=nonsip_msg_run_hooks(msg))!=NONSIP_MSG_ACCEPT){ if (unlikely(ret==NONSIP_MSG_ERROR)) goto error03; goto end; /* drop the message */ } } /* sanity checks */ if ((msg->via1==0) || (msg->via1->error!=PARSE_OK)){ /* no via, send back error ? */ LOG(L_ERR, "ERROR: receive_msg: no via found in request\n"); STATS_BAD_MSG(); goto error02; } /* check if necessary to add receive?->moved to forward_req */ /* check for the alias stuff */ #ifdef USE_TCP if (msg->via1->alias && cfg_get(tcp, tcp_cfg, accept_aliases) && (((rcv_info->proto==PROTO_TCP) && !tcp_disable) #ifdef USE_TLS || ((rcv_info->proto==PROTO_TLS) && !tls_disable) #endif ) ){ if (tcpconn_add_alias(rcv_info->proto_reserved1, msg->via1->port, rcv_info->proto)!=0){ LOG(L_ERR, " ERROR: receive_msg: tcp alias failed\n"); /* continue */ } } #endif /* skip: */ DBG("preparing to run routing scripts...\n"); #ifdef STATS gettimeofday( & tvb, &tz ); #endif /* execute pre-script callbacks, if any; -jiri */ /* if some of the callbacks said not to continue with script processing, don't do so if we are here basic sanity checks are already done (like presence of at least one via), so you can count on via1 being parsed in a pre-script callback --andrei */ if (exec_pre_script_cb(msg, REQUEST_CB_TYPE)==0 ) { STATS_REQ_FWD_DROP(); goto end; /* drop the request */ } set_route_type(REQUEST_ROUTE); /* exec the routing script */ if (run_top_route(main_rt.rlist[DEFAULT_RT], msg, 0)<0){ LOG(L_WARN, "WARNING: receive_msg: " "error while trying script\n"); goto error_req; } #ifdef STATS gettimeofday( & tve, &tz ); diff = (tve.tv_sec-tvb.tv_sec)*1000000+(tve.tv_usec-tvb.tv_usec); stats->processed_requests++; stats->acc_req_time += diff; DBG("successfully ran routing scripts...(%d usec)\n", diff); STATS_RX_REQUEST( msg->first_line.u.request.method_value ); #endif /* execute post request-script callbacks */ exec_post_script_cb(msg, REQUEST_CB_TYPE); }else if (msg->first_line.type==SIP_REPLY){ /* sanity checks */ if ((msg->via1==0) || (msg->via1->error!=PARSE_OK)){ /* no via, send back error ? */ LOG(L_ERR, "ERROR: receive_msg: no via found in reply\n"); STATS_BAD_RPL(); goto error02; } #ifdef STATS gettimeofday( & tvb, &tz ); STATS_RX_RESPONSE ( msg->first_line.u.reply.statuscode / 100 ); #endif /* execute pre-script callbacks, if any; -jiri */ /* if some of the callbacks said not to continue with script processing, don't do so if we are here basic sanity checks are already done (like presence of at least one via), so you can count on via1 being parsed in a pre-script callback --andrei */ if (exec_pre_script_cb(msg, ONREPLY_CB_TYPE)==0 ) { STATS_RPL_FWD_DROP(); goto end; /* drop the reply */ } /* exec the onreply routing script */ if (onreply_rt.rlist[DEFAULT_RT]){ set_route_type(CORE_ONREPLY_ROUTE); ret=run_top_route(onreply_rt.rlist[DEFAULT_RT], msg, &ctx); #ifndef NO_ONREPLY_ROUTE_ERROR if (unlikely(ret<0)){ LOG(L_WARN, "WARNING: receive_msg: " "error while trying onreply script\n"); goto error_rpl; }else #endif /* NO_ONREPLY_ROUTE_ERROR */ if (unlikely(ret==0 || (ctx.run_flags&DROP_R_F))){ STATS_RPL_FWD_DROP(); goto skip_send_reply; /* drop the message, no error */ } } /* send the msg */ forward_reply(msg); skip_send_reply: #ifdef STATS gettimeofday( & tve, &tz ); diff = (tve.tv_sec-tvb.tv_sec)*1000000+(tve.tv_usec-tvb.tv_usec); stats->processed_responses++; stats->acc_res_time+=diff; DBG("successfully ran reply processing...(%d usec)\n", diff); #endif /* execute post reply-script callbacks */ exec_post_script_cb(msg, ONREPLY_CB_TYPE); } end: #ifdef STATS skipped = 0; #endif /* free possible loaded avps -bogdan */ reset_avps(); #ifdef WITH_XAVP xavp_reset_list(); #endif DBG("receive_msg: cleaning up\n"); free_sip_msg(msg); pkg_free(msg); #ifdef STATS if (skipped) STATS_RX_DROPS; #endif return 0; #ifndef NO_ONREPLY_ROUTE_ERROR error_rpl: /* execute post reply-script callbacks */ exec_post_script_cb(msg, ONREPLY_CB_TYPE); reset_avps(); #ifdef WITH_XAVP xavp_reset_list(); #endif goto error02; #endif /* NO_ONREPLY_ROUTE_ERROR */ error_req: DBG("receive_msg: error:...\n"); /* execute post request-script callbacks */ exec_post_script_cb(msg, REQUEST_CB_TYPE); error03: /* free possible loaded avps -bogdan */ reset_avps(); #ifdef WITH_XAVP xavp_reset_list(); #endif error02: free_sip_msg(msg); pkg_free(msg); error00: STATS_RX_DROPS; return -1; } kamailio-4.0.4/etc/0000755000000000000000000000000012223032461012551 5ustar rootrootkamailio-4.0.4/etc/nathelper.cfg0000644000000000000000000001505512223032460015221 0ustar rootroot# # $Id$ # # simple quick-start config script including nathelper support # This default script includes nathelper support. To make it work # you will also have to install Maxim's RTP proxy. The proxy is enforced # if one of the parties is behind a NAT. # # If you have an endpoing in the public internet which is known to # support symmetric RTP (Cisco PSTN gateway or voicemail, for example), # then you don't have to force RTP proxy. If you don't want to enforce # RTP proxy for some destinations than simply use t_relay() instead of # route(1) # # Sections marked with !! Nathelper contain modifications for nathelper # # NOTE !! This config is EXPERIMENTAL ! # # ----------- global configuration parameters ------------------------ debug=3 # debug level (cmd line: -dddddddddd) fork=yes log_stderror=no # (cmd line: -E) /* Uncomment these lines to enter debugging mode fork=no log_stderror=yes */ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 fifo="/tmp/sip-router_fifo" # ------------------ module loading ---------------------------------- # Uncomment this if you want to use SQL database #loadmodule "/usr/local/lib/sip-router/modules/mysql.so" loadmodule "/usr/local/lib/sip-router/modules/sl.so" loadmodule "/usr/local/lib/sip-router/modules/tm.so" loadmodule "/usr/local/lib/sip-router/modules/rr.so" loadmodule "/usr/local/lib/sip-router/modules/maxfwd.so" loadmodule "/usr/local/lib/sip-router/modules/usrloc.so" loadmodule "/usr/local/lib/sip-router/modules/registrar.so" loadmodule "/usr/local/lib/sip-router/modules/textops.so" # Uncomment this if you want digest authentication # mysql.so must be loaded ! #loadmodule "/usr/local/lib/sip-router/modules/auth.so" #loadmodule "/usr/local/lib/sip-router/modules/auth_db.so" # !! Nathelper loadmodule "/usr/local/lib/sip-router/modules/nathelper.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- modparam("usrloc", "db_mode", 0) # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line #modparam("usrloc", "db_mode", 2) # -- auth params -- # Uncomment if you are using auth module # #modparam("auth_db", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) # #modparam("auth_db", "plain_password_column", "password") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # !! Nathelper modparam("registrar", "nat_flag", 6) modparam("nathelper", "natping_interval", 30) # Ping interval 30 s modparam("nathelper", "ping_nated_only", 1) # Ping only clients behind NAT # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); break; }; # !! Nathelper # Special handling for NATed clients; first, NAT test is # executed: it looks for via!=received and RFC1918 addresses # in Contact (may fail if line-folding is used); also, # the received test should, if completed, should check all # vias for rpesence of received if (nat_uac_test("3")) { # Allow RR-ed requests, as these may indicate that # a NAT-enabled proxy takes care of it; unless it is # a REGISTER if (method == "REGISTER" || ! search("^Record-Route:")) { log("LOG: Someone trying to register from private IP, rewriting\n"); # This will work only for user agents that support symmetric # communication. We tested quite many of them and majority is # smart enough to be symmetric. In some phones it takes a configuration # option. With Cisco 7960, it is called NAT_Enable=Yes, with kphone it is # called "symmetric media" and "symmetric signalling". fix_nated_contact(); # Rewrite contact with source IP of signalling if (method == "INVITE") { fix_nated_sdp("1"); # Add direction=active to SDP }; force_rport(); # Add rport parameter to topmost Via setflag(6); # Mark as NATed }; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!method=="REGISTER") record_route(); # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); break; }; if (!uri==myself) { # mark routing logic in request append_hf("P-hint: outbound\r\n"); route(1); break; }; # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (method=="REGISTER") { # Uncomment this if you want to use digest authentication # if (!www_authorize("iptel.org", "subscriber")) { # www_challenge("iptel.org", "0"); # break; # }; save("location"); break; }; lookup("aliases"); if (!uri==myself) { append_hf("P-hint: outbound alias\r\n"); route(1); break; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); break; }; }; append_hf("P-hint: usrloc applied\r\n"); route(1); } route[1] { # !! Nathelper if (uri=~"[@:](192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)" && !search("^Route:")){ sl_send_reply("479", "We don't forward to private IP addresses"); break; }; # if client or server know to be behind a NAT, enable relay if (isflagset(6)) { force_rtp_proxy(); }; # NAT processing of replies; apply to all transactions (for example, # re-INVITEs from public to private UA are hard to identify as # NATed at the moment of request processing); look at replies t_on_reply("1"); # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; } # !! Nathelper onreply_route[1] { # NATed transaction ? if (isflagset(6) && status =~ "(183)|2[0-9][0-9]") { fix_nated_contact(); force_rtp_proxy(); # otherwise, is it a transaction behind a NAT and we did not # know at time of request processing ? (RFC1918 contacts) } else if (nat_uac_test("1")) { fix_nated_contact(); }; } kamailio-4.0.4/etc/reasons-en_US.ascii.txt0000644000000000000000000000340312223032460017061 0ustar rootroot# # $Id$ # # English reason phrases # # Format: # ::: # 100::en_US.ascii:Trying 180::en_US.ascii:Ringing 181::en_US.ascii:Call Is Being Forwarded 182::en_US.ascii:Queued 183::en_US.ascii:Session Progress 200::en_US.ascii:OK 202::en_US.ascii:Pending 300::en_US.ascii:Multiple Choices 301::en_US.ascii:Moved Permanently 302::en_US.ascii:Moved Temporarily 305::en_US.ascii:Use Proxy 380::en_US.ascii:Alternative Service 400::en_US.ascii:Bad Request 401::en_US.ascii:Unauthorized 402::en_US.ascii:Payment Required 403::en_US.ascii:Forbidden 404::en_US.ascii:Not Found 405::en_US.ascii:Method Not Allowed 406::en_US.ascii:Not Acceptable 407::en_US.ascii:Proxy Authentication Required 408::en_US.ascii:Request Timeout 410::en_US.ascii:Gone 413::en_US.ascii:Request Entity Too Large 414::en_US.ascii:Request-URI Too Long 415::en_US.ascii:Unsupported Media Type 416::en_US.ascii:Unsupported URI Scheme 420::en_US.ascii:Bad Extension 421::en_US.ascii:Extension Required 423::en_US.ascii:Interval Too Brief 480::en_US.ascii:Temporarily Unavailable 481::en_US.ascii:Call/Transaction Does Not Exist 482::en_US.ascii:Loop Detected 483::en_US.ascii:Too Many Hops 484::en_US.ascii:Address Incomplete 485::en_US.ascii:Ambiguous 486::en_US.ascii:Busy Here 487::en_US.ascii:Request Terminated 488::en_US.ascii:Not Acceptable Here 491::en_US.ascii:Request Pending 493::en_US.ascii:Undecipherable 500::en_US.ascii:Server Internal Error 501::en_US.ascii:Not Implemented 502::en_US.ascii:Bad Gateway 503::en_US.ascii:Service Unavailable 504::en_US.ascii:Server Time-out 505::en_US.ascii:Version Not Supported 513::en_US.ascii:Message Too Large 600::en_US.ascii:Busy Everywhere 603::en_US.ascii:Decline 604::en_US.ascii:Does Not Exist Anywhere 606::en_US.ascii:Not Acceptable kamailio-4.0.4/etc/sip-router-oob.cfg0000644000000000000000000015125512223032460016130 0ustar rootroot# # $Id$ # # # Applicability of this Configuration File # ---------------------------------------- # # This is default SER script as used for example at the iptel.org # SIP service; it can deal with NATs, terminate calls to a PSTN # gateway, and it implements a couple of basic signaling features # (few types of call forwarding). In this scenario you may have # multiple SIP proxies sharing one database for accessing provisioned # data, which are maintained for example using serweb. The proxy # servers also share write-access to user location database (and # keeps a full cache of all usrloc entries synchronized using # multicast). # # If you look for a simpler version with a lot less dependencies # please refer to the sip-router-basic.cfg file in your SER distribution. # # If you look for documentation, try http://sip-router.org/wiki/. # The right mailing lists for questions about this file is # . # # Requirements: # --------------- # running DB, running RTP proxy, one public IP address # for SIP service, one private IP address for administrative purposes; # optional: IP address of a PSTN gateway # # HOWTOs: # --------- # To get this config running you need to execute the following commands # with the new serctl (the capital word are just place holders): # # $ ser_ctl domain add DOMAINNAME # $ ser_ctl user add USERNAME@DOMAINNAME -p PASSWORD # # (ser_ctl can be obtained from # http://ftp.iptel.org/pub/serctl/daily-snapshots/ ) # # If you want to have P-Asserted-ID header for your user # # $ ser_attr add uid=UID asserted_id="PID" # # If you want to have (PSTN) gateway support: # # $ ser_db add attr_types name=gw_ip rich_type=string raw_type=2 \ # description="The gateway IP for the default ser.cfg" default_flags=33 # $ ser_attr add global gw_ip=GATEWAY-IP # # Alternatively, you can simple uncomment the relevant line in this file # right at the beginning of the main route. # # You can also use serweb to set all the values above # (http://ftp.iptel.org/pub/serweb/daily-snapshots/ or # http://developer.berlios.de/projects/serweb). # # Users with permission to call PSTN using this script must have # the $gw_acl attribute set properly, and shall have $asserted_id # set to indicate their caller-id for calls to PSTN. For inbound # calls from PSTN, additional aliases may be also set. # # Warning: # ----------- # If this file is installed on Debian from package 'ser-oob' then some # options in this configuration file may be set by post-installation # script, according to values entered by user at installation time in # debconf configuration. These values are then applied automatically to # this file each time the 'ser-oob' package is upgraded or reconfigured by # calling 'dpkg-reconfigure sip-router-oob'. # # The parts of this configuration file that may be altered by debconf are # enclosed between '#DEBCONF-something-START' and '#DEBCONF-something-END' # comment marks. Please do not remove them. # # # TODO (Future possible improvements): # --------------------------------------- # * protocol tuning # - AVP-based diversion for call-forwarding (as opposed to specialized # module) # - add Date header in 200s to REGISTERs (to be packaged with NTP!) # * more security: # - pike/rate-limit # - identity # - TLS # - permissions # - Re-name all internal headers so that they start with a common prefix, # such as P-SER and then wipe all such headers from requests received # from untrusted sources, such as the user agents or foreign proxy # servers # * refined DB use (e.g., flatstore for acc) # * miscellanous: # - dialog module for monitoring purposes # - more extensive logging using xlog (controlled by gflags/gAVPs) # * leveraging 2.1 features: # - removal of private IP address (it takes a multicast-specific # command which will allow OS to determine source IP address) # - timer route: # * don't use exec (it takes domain.reload as script command) # * compare last-stored timestamp with current timestamp (it takes # assignment of gAVPs) # * check multicast REGISTERs for their TTL (this is a simple and # effective security check to prevent remote multicast messages # to damage our traffic) # - numerous fine-tuning parameters which are only available in 2.1 # (mlock_pages, dns_try_naptr, etc.) # - better support for preloaded routes with domain name # # Security considerations: # ------------------------ # The script has been tested against security leaks, but it comes # under terms of GPL "as is" without any warranties; better check # yourself that: # - IP based authentication of PSTN gateway and multicast REGISTERs # is compliant to your network setup and security policy. # - Multiple gateway IPs can't be provisioned as security checks # are applied only to one. # # Licensing # ---------- # Copyright (C) 2005-2008 iptelorg GmbH # This file is part of SER, a free SIP server. It is available under the # terms of the GNU General Public License. # Numerous folks have contributed to this file, including but not limited # to Andrei, Jan, Jiri, Michal, Miklos, Nils. # # # .... that's it, enough of yadiyada, here the real config begins! # ----------- Global Defines / Extra Features ------------------------------- # (can be enabled either by uncommenting the corresponding #!define # statement or by starting with -A WITH_, e.g. # ser -A WITH_TLS -f /etc/ser/ser-oob.cfg ) # enable TLS ##!define WITH_TLS #enable xmlrpc support ##!define WITH_XMLRPC # xmlrpc allowed only if it comes on TLS from a client with a valid cert ##!define XMLRPC_TLS_ONLY # xmlrpc allowed subnets (if defined XMLRPC requests with source ip matching # this network addresses will be allowed, if no XMLRPC_ALLOWED_SUBNETx is # defined only requests coming from localhost will be allowed). # E.g.: ser -A XMLRPC_ALLOW_NET1=192.168.1.0/24 -f ser-oob.cfg ##!define XMLRPC_ALLOW_NET1 192.168.0.0/16 ##!define XMLRPC_ALLOW_NET2 10.0.0.0/255.0.0.0 ##!define XMLRPC_ALLOW_NET3 172.16.0.0/12 # started from compile directory (not installed) ##!define LOCAL_TEST_RUN # ----------- Global Configuration Parameters ------------------------------- #debug=3 # debug level (cmd line: -ddd) #memdbg=10 # memory debug log level #memlog=10 # memory statistics log level #log_facility=LOG_LOCAL0 # the facility used for logging (see syslog(3)) #DEBCONF-SERVERID-START server_id=0 #DEBCONF-SERVERID-END # Uncomment these lines to enter debugging mode or start SER with # sip-router -ED # #fork=no #log_stderror=yes check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) #port=5060 #children=4 #user=sip-router #group=sip-router #disable_core=yes # disables core dumping #open_files_limit=20480 # sets the open file descriptors limit #mhomed=yes # usefull for multihomed hosts, small performance # penalty disable_tcp=no # be conservative about enabling TCP -- it can # degrade performance a lot #tcp_accept_aliases=yes # accepts the tcp alias via option phone2tel=no # ignore user=phone in request-URIs -- otherwise # these URIs would be interpreted as equivalent # to TEL URIs, and their lookup would fail in URI # database reply_to_via=no sip_warning=yes # public IP address #DEBCONF-LISTEN-START listen=127.0.0.1 #DEBCONF-LISTEN-END # sip.mcast.net for REGISTER replication #DEBCONF-LISTEN_REPL-START listen=udp:224.0.1.75 #DEBCONF-LISTEN_REPL-END # administrative interface -- needed for example for multicast source # or XML-RPC #DEBCONF-LISTEN_ADMIN-START listen=udp:127.0.0.1 #DEBCONF-LISTEN_ADMIN-END #listen=tls:127.0.0.1:5061 mlock_pages=yes shm_force_alloc=yes real_time=7 # ------------------- DNS Parameters ---------------------------------------- # (see doc/dns.txt for more details) # # minimum timeouts dns_retr_time=1 dns_retr_no=1 dns_servers_no=1 dns_use_search_list=no dns_try_ipv6=no # dns cache & failover use_dns_cache=on use_dns_failover=on # dns_cache_flags=0 dns_cache_negative_ttl=300 dns_cache_min_ttl=60 dns_cache_max_ttl=86400 # 1 day dns_cache_mem=2048 # 2 MB dns_cache_gc_interval=60 # garbage collection every minute # ser 2.1 specific options dns_try_naptr=yes dns_srv_lb=yes # srv based load balancing dns_udp_pref=3 # prefer udp (when resolving naptr record) dns_tcp_pref=2 # if no udp available accept tcp (for naptr) dns_sctp_pref=2 # same preference as tcp #!ifdef WITH_TLS dns_tls_pref=1 # low preference (heavy resource use) #!else dns_tls_pref=-1 # ignore / don't accept tls (for naptr) #!endif # dns_cache_delete_nonexpired=no # ------------------- Blacklist Parameters ---------------------------------- # (see doc/dst_blacklist.txt for more details) # use_dst_blacklist=on dst_blacklist_mem=1024 # 1 MB dst_blacklist_expire=300 # blacklist default time dst_blacklist_gc_interval=150 # 2.5 min # for sip-router 2.1 to the above add tm blst_503* parameters and/or use the # blst module (see NEWS) # ------------------- TCP Parameters ---------------------------------------- # (see NEWS for more details) tcp_connection_lifetime=3600 #tcp_max_connections=10240 # default is 2048 tcp_connect_timeout=1 tcp_async=yes # ------------------- TLS Parameters ---------------------------------------- #!ifdef WITH_TLS # Enable TLS hooks so that the TLS module can be used tls_enable=yes #!endif # -------------------- Custom Parameters ------------------------------------ # These parameters can be modified runtime via RPC interface, # read the documentation of cfg_rpc module. # Session Timer parameters, RFC 4028 # # Default session interval used by the proxy if the UAC does not support # session timer. Set it to "0" to disable session timer proxy support. # session_timer.default = "1800" desc "default session interval (in s)" # # Minimum session interval accepted by the proxy, it must not be less # than 90 seconds. # session_timer.min_se = "90" desc "minimum session interval (in s)" # RTP Proxy options # # Whether to enable or disable the rtp proxy. Possible values are: # "0" -- always disable # "1" -- always enable regardless of whether UAC or UAS is behind NAT # "detect" -- detect whether the UAC or the UAS is behind NAT, # and enable the rtp proxy when necessary # #DEBCONF-RTP_ENABLE-START rtp_proxy.enabled = "detect" desc "indicates whether the RTP Proxy is enabled or not (0/1/detect)" #DEBCONF-RTP_ENABLE-END # ------------------ Module Loading ----------------------------------------- #!ifdef LOCAL_TEST_RUN loadpath "modules:modules_s" #!else loadpath "/usr/lib/sip-router/modules:/usr/lib/sip-router/modules_s" #!endif # load a SQL database for authentication, domains, user AVPs etc. loadmodule "db_mysql" #loadmodule "postgres" loadmodule "tm" loadmodule "sl" loadmodule "rr" loadmodule "maxfwd" loadmodule "usrloc" loadmodule "registrar" loadmodule "xlog" loadmodule "textops" loadmodule "ctl" loadmodule "auth" loadmodule "auth_db" loadmodule "gflags" loadmodule "domain" loadmodule "uri_db" loadmodule "avp" loadmodule "avp_db" loadmodule "acc_db" #!ifdef WITH_XMLRPC loadmodule "xmlrpc" #!endif loadmodule "options" loadmodule "sanity" loadmodule "nathelper" loadmodule "uri" loadmodule "speeddial" loadmodule "timer" loadmodule "db_ops" loadmodule "exec" loadmodule "cfg_rpc" loadmodule "eval" loadmodule "enum" #!ifdef WITH_TLS loadmodule "tls" #!endif # ----------------- Declaration of Script Flags ----------------------------- flags FLAG_ACC : 1, # the request will be recorded by ACC FLAG_FAILUREROUTE : 2, # we are operating from the failure route FLAG_NAT : 3, # the UAC is behind a NAT FLAG_REPL_ENABLED : 4, # REGISTER replication is enabled if set FLAG_TOTAG : 5, # request has a To tag FLAG_PSTN_ALLOWED : 6, # the user is allowed to use the PSTN FLAG_DONT_RM_CRED : 7, # do not remove the credentials FLAG_AUTH_OK : 8, # authentication succeeded FLAG_SERWEB_RSVD1 : 9, # bit reserved for use with serweb FLAG_SERWEB_RSVD2 : 10, # bit reserved for use with serweb FLAG_SESSIONTIMER : 11, # indicates that the UAC supports Session Timer FLAG_RR_DONE : 12, # the request got already one RR header FLAG_RTP_PROXY : 13, # the RTP proxy is turned on FLAG_NAT_REG : 14, # the UAC behind NAT, stored in location record FLAG_INIT_DLG : 15, # init INVITE dialog FLAG_REVERSE_DIR : 16, # set if request goes callee -> caller direction, requires rr.append_fromtag=1 FLAG_ACC_MISSED : 17, # the missed call will be recorded by ACC FLAG_USRLOC_FWD : 18, # usrloc based forward FLAG_NEXT_ROUTE : 19; # there is a route remaining avpflags dialog_cookie; # attribute will be stored in Route headers # ----------------- Module-specific Parameters ------------------------------ # path to the database # #DEBCONF-DBURL-START modparam("speeddial|auth_db|usrloc|domain|uri_db|gflags|avp_db|db_ops", "db_url", "mysql://ser:heslo@localhost/ser") #DEBCONF-DBURL-END # specify the path to your database for accounting #DEBCONF-DBURLACC-START modparam("acc_db", "db_url", "mysql://ser:heslo@localhost/ser") #DEBCONF-DBURLACC-END # -- usrloc -- # Database access mode: 0 -- memory cached, 1 -- write through, # 2 -- delayed write. 1 is generally safer than 2. 2 can help # to survive peaks in load. However, it creates delayed peaks that can # impair request-processing latency later (usrloc would have to be # re-redesigned more lock-free to avoid it). #DEBCONF-DBMODE-START modparam("usrloc", "db_mode", 1) #DEBCONF-DBMODE-END # Don't delete expired records from database on a per-contact basis -- that # results in bulky DB operations and can lead to synchronization issues # in server farm when for a time a server doesn't obtain re-reregistrations modparam("usrloc","db_skip_delete",1) # -- registrar -- # Maximum expires time. Forces users to re-register every 10 min. modparam("registrar", "max_expires", 600) # Minimum expires time. Even if they try, clients cannot register # for a shorter time than this. modparam("registrar", "min_expires", 240) # Identify natted contacts using a flag. modparam("registrar", "load_nat_flag", "FLAG_NAT_REG") modparam("registrar", "save_nat_flag", "FLAG_NAT_REG") # Maximum number of contacts. modparam("registrar", "max_contacts", 10) # -- auth -- #modparam("auth_db", "calculate_ha1", yes) #modparam("auth_db", "password_column", "password") # Minimize replay-attack window. modparam("auth", "nonce_expire", 10) # Enable/disable extra authentication checks using the following modparams. # The values are: 1 -- Request-URI, 2 -- Call-ID, 4 -- From tag, # 8 -- source IP. The options are disabled by default. # For REGISTER requests we hash the Request-URI, Call-ID, and source IP of the # request into the nonce string. This ensures that the generated credentials # cannot be used with another registrar, user agent with another source IP # address or Call-ID. Note that user agents that change Call-ID with every # REGISTER message will not be able to register if you enable this. #modparam("auth", "auth_checks_register", 11) # For dialog-establishing requests (such as the original INVITE, OPTIONS, etc) # we hash the Request-URI and source IP. Hashing Call-ID and From tags takes # some extra precaution, because these checks could render some UA unusable. #modparam("auth", "auth_checks_no_dlg", 9) # For mid-dialog requests, such as re-INVITE, we can hash source IP and # Request-URI just like in the previous case. In addition to that we can hash # Call-ID and From tag because these are fixed within a dialog and are # guaranteed not to change. This settings effectively restrict the usage of # generated credentials to a single user agent within a single dialog. #modparam("auth", "auth_checks_in_dlg", 15) # Deal with clients who can't do qop properly modparam("auth", "qop", "") #DEBCONF-AUTHSECRET-START modparam("auth", "secret", "aqwedrftredswqwddcft") #DEBCONF-AUTHSECRET-END # -- rr -- # Add value to lr param to make some broken UAs happy. modparam("rr", "enable_full_lr", 1) # Limit the length of the AVP cookie to necessary attributes only modparam("rr", "cookie_filter", "(account|rproxy|stimer|dialog_id)") # You probably do not want that someone can simply read and change # the AVP cookie in your Routes, thus should really change this # secret value below modparam("rr", "cookie_secret", "sgsatewgdbsnmpoiewh") # The ftag Route parameter may be used to easily determine if a BYE # is coming from caller or callee, but we prefer shorter messages # Enable when FLAG_REVERSE_DIR is to be used modparam("rr", "append_fromtag", 0) # -- gflags -- # Load global attributes. modparam("gflags", "load_global_attrs", 1) # -- domain -- # Load domain attributes. modparam("domain", "load_domain_attrs", 1) # -- ctl -- # By default, ctl listens on unixs:/tmp/sip-router_ctl if no other address is # specified in modparams; this is also the default for sercmd. modparam("ctl", "binrpc", "unixs:/tmp/ser_ctl") # Listen on the "standard" fifo for backward compatibility. modparam("ctl", "fifo", "fifo:/tmp/ser_fifo") # Listen on tcp on localhost. modparam("ctl", "binrpc", "tcp:127.0.0.1:2046") # -- acc_db -- # Failed transactions (those with negative responses) should be logged, too. modparam("acc_db", "failed_transactions", 1) # If you don't want to have accounting entries written into the database, # comment the next line out. modparam("acc_db", "log_flag", "FLAG_ACC") # seems "log_flag" and "log_flag_missed" cannot share the same flag! modparam("acc_db", "log_missed_flag", "FLAG_ACC_MISSED") # if you would like to customize your CDRs, do it here.... #modparam("acc_db", "attrs", # "$f.sop_billing_category,$f.isPrepaidCustomer,$f.sop_cf_orig_uid") # -- tm -- # Do not restart the resend timer with each reply. (See INBOUND route # below.) modparam("tm", "restart_fr_on_each_reply", 0) # -- xmlrpc -- #!ifdef WITH_XMLRPC # Use a sub-route. This is a lot safer then relying on the request method # to distinguish HTTP from SIP modparam("xmlrpc", "route", "XMLRPC"); #!endif # -- nathelper -- # RTP Proxy address #DEBCONF-RTTPPROXY-START modparam("nathelper", "rtpproxy_sock", "udp:127.0.0.1:22222") #DEBCONF-RTTPPROXY-END # TCP keepalives as simple as CRLF modparam("nathelper", "natping_crlf", 0) # How often to send a NAT ping. Set this to 0 to turn NAT ping off. #DEBCONF-NATPING_INTERVAL-START modparam("nathelper", "natping_interval", 15) #DEBCONF-NATPING_INTERVAL-END # Only ping contacts that have the NAT flag set. modparam("nathelper", "ping_nated_only", 1) # Send an OPTIONS SIP request as NAT ping. If this is not set, a simple # 4-byte ping is used. modparam("nathelper", "natping_method", "OPTIONS") # Temporary statefull natping test (only in future versions) #modparam("nathelper", "natping_stateful", 1) # -- exec -- modparam("exec", "time_to_kill", 200); modparam("exec", "setvars", 0); # -- timer -- # Register route ON_1MIN_TIMER to be called every minute. modparam("timer", "declare_timer", "ON_1MIN_TIMER=ON_1MIN_TIMER,60000,slow,enable"); #!ifdef WITH_TLS # -- tls -- #!ifdef LOCAL_TEST_RUN modparam("tls", "config", "./modules/tls/tls.cfg"); #!else modparam("tls", "config", "tls.cfg"); #!endif #!endif # -- db_ops -- modparam("db_ops", "declare_handle", "reload") modparam("db_ops", "declare_handle", "gattr_reload") # ------------------------- Request Routing Logic -------------------------- # Main request route. # # Each request starts here. # route { # if you have a PSTN gateway just un-comment the follwoing line and # specify the IP address of it to route calls to it. #$gw_ip = "1.2.3.4" # Alternatively (even better), set it as global persistent parameter # using serweb or ser_attrs). If using a PSTN GW, per-subscriber # options must ($gw_acl) or may (asserted_id) be set to enable calls # to PSTN. If email-like URIs are used, having a URI alias for # processing incoming PSTN-to-ip requests may be useful, too. # Important: the script is assuming one global pstn-gw for all # domains! Failure to allow gw_ip to be a domain-specic attribute # would result in security gaps (onsend_route checks only for one # gateway). # First, do some initial sanity checks. route(INIT); # Bypass the rest of the script for CANCELs if possible. route(CATCH_CANCEL); # Check if the request is routed via Route header. route(PROCESS_ROUTES); # Look up domain IDs route(DOMAIN); # Answer OPTIONS requests to our system. route(OPTIONS_REPLY); # Enforce domain policy. route(DOMAIN_POLICY); # Handle REGISTER requests. route(REGISTRAR); # From here on we want to know who is calling. route(AUTHENTICATION); # We are finished with all the precaution work -- let's # try to locate the callee. The first route that matches # "wins" and relays the request. If none matches, SER will # send a 404. # Check if we should be outbound proxy for a local user. route(OUTBOUND); # Redirect in case user dialed a speed dial entry. route(SPEEDDIAL); # Place various site-specific routes here. route(SITE_SPECIFIC); # Check if the request is for a local user. route(INBOUND); # There is SIP user for the called address. Before trying PSTN, # you may have to convert the adress, for instance by using # ENUM. #route(ENUM); # Last resort: if none of the previous route has found # the recepient, try PSTN. route(PSTN); # nothing matched sl_reply("404", "No route matched"); } # Forward a request to the destination set. # route[FORWARD] { # If this is called from the failure route we need to add a new # branch. if (isflagset(FLAG_FAILUREROUTE)) { if (!append_branch()) { t_reply("500", "Too many branches"); drop; } } # If this is an initial INVITE (without a To-tag) we might try # another target (call forwarding or voicemail) after receiving # an error. if (isflagset(FLAG_INIT_DLG)) { t_on_failure("FAILURE_ROUTE"); } # Always use the reply route to check for NATed UAS. t_on_reply("REPLY_ROUTE"); # Remove credentials to keep requests shorter if (isflagset(FLAG_AUTH_OK) && !isflagset(FLAG_DONT_RM_CRED) ) { consume_credentials(); } # Activate the RTP proxy as the second last step because it modifies the # body but also sets an dialog AVP cookie. route(RTPPROXY); # Insert a Record-Route header into all requests. # This has to be done as one of the last steps to include all the # RR cookies which might have been created during the script run. route(RECORD_ROUTE); # Send it out now. if (!t_relay()) { if (isflagset(FLAG_FAILUREROUTE)) { # XXX This should be replaced with # t_reply_error() similar to sl_reply_error() # in order to return the proper failure code. # Only, there is no such function yet. t_reply("500", "Request cannot be forwarded"); } else { sl_reply_error(); } } drop; } # Perform initial checks on an incoming request. # # Rejects the request if it fails any of the checks. # route[INIT] { # Messages with a Max-Forwards header of zero. if (!mf_process_maxfwd_header("10")) { sl_reply("483","Too Many Hops"); drop; } # Set flag for use in the onsend route (because it does not # allow to use "select" statements) if (@to.tag != "") { setflag(FLAG_TOTAG); } # Check if the UAC is NATed and fix the message accordingly route(UAC_NAT_DETECTION); # Activate accounting for all initial INVITEs. In-dialog requests # are accounted by a RR cookie (see below). # It should work also when the call has been already forked at a previous router if (method == "INVITE" && !isflagset(FLAG_TOTAG)) { $dialog_id = @sys.unique; # make unique dialogid setflag(FLAG_ACC); setflag(FLAG_ACC_MISSED); setflag(FLAG_INIT_DLG); } else if (isflagset(FLAG_TOTAG) && @hf_value.route[0].params.ftag != @from.tag) { setflag(FLAG_REVERSE_DIR); # callee -> caller } # if needed then we MUST put after force_rport() which is located in NAT_DETECTION!!! # also must be called after FLAG_ACC is set !!! # Check t_reply() vs. sl_reply() usage in script #if (!t_newtran()) { # sl_reply("500", "Internal tm error"); # drop; #} # Set flag and use it instead of the attribute. if ($replicate==1) { setflag(FLAG_REPL_ENABLED); } } # Reply OPTIONS requests sent to the proxy itself. # route[OPTIONS_REPLY] { # OPTIONS requests without a username in the Request-URI but one # of our domains or IPs are addressed to the proxy itself and # can be answered statelessly. if (method == "OPTIONS" && strempty(@ruri.user) && (uri == myself || $t.did != "")) { options_reply(); drop; } } # Check if the sender of the request is behind a NAT device. If so, # fix the request so that other devices can talk to the sender nonetheless. # route[UAC_NAT_DETECTION] { # Lots of UAs do not include the rport parameter in there Via # header, so we put it there regardless. force_rport(); # If a reliable transport was used store the connection internally # so that SERs core can re-use the connection later. if (proto==TCP || proto == TLS) { force_tcp_alias(); } # Check if the request contains hints for a NATed UAC. Also, try to # rewrite contacts using maddr. Using maddr is a really dubious # technique and we better replace such with transport address. # Downside: it fails for clients fronted by another server, in # which case a valid contact we dislike because of maddr will be # substituted inapproprietely (e.g., WM from other domains will # fail). If you are worried about that, remove tests for maddr and # recompile SER using HONOR_MADDR. Also note that rewriting # contacts may possibly lead to client denying subseqent requests # to them because they don't recognized fixed contacts as their # own. Should you encounter such a case, a possible solution # would be to store the original information as a contact parameter # and restore it on its way back. # In case of UDP we test for # - private IPs in Contact # - mismatch of transport IP and IP in Via # - mismatch of transport port and port in Via # in all other cases we skip the port test, because lots of clients # do not correctly advertise their emphemeral port number in their Via # header in case of reliable transports (although they are not behind # a NAT). # Warning: if you are dealing with SIP implementations which are # running on public IP and do as-symmertic signaling for whatever # reason the following check will make their signaling symmetric. # If you need to support as-symmertic signaling reduce the following # nat_uac_test for UDP to "3" or even "1". if ((proto == UDP && nat_uac_test("19")) || (nat_uac_test("3")) || (@hf_value["contact"] != "" && @contact.uri.params.maddr != "")) { setflag(FLAG_NAT); if (method == "REGISTER") { # Prepare the Contact so that the registrar module # saves the source address and port as well. fix_nated_register(); } else { # Overwrite the Contact to allow proper in-dialog # routing. # but do not override if there is already a proxy in the path, we'll route by record-route, # RURI responsibility takes to previous proxy # TODO: shouldn't we rather limit to methods which are dialog aware (INVITE, UPDATE, SUBSCRIBE, ..) if (strempty(@hf_value.record_route) || (@hf_value["contact"]!="" && @contact.uri.params.maddr!="")) { fix_nated_contact(); } } } } # Check if the receiver of the request is behind a NAT device. If so, # fix the Contact header to allow proper routing of in-dialog requests. route[UAS_NAT_DETECTION] { # Fix the Contact in the reply if it contains a private IP to # allow proper routing of in-dialog messages. # Do the same if the contact is maddred. # But skip 3XX responses, because we do not know the right IP for that, # even if they contain private IPs. if (status=~"(3[0-9][0-9])") { break; } # prevent contact overwriting when a proxy between ser and UAS. # We get it from record-route but it's rather difficult or # do it only for UAS which is registered in usrloc database and has no # proxy on path. # Note: destination forced by $fwd_always_target is not NAT detected and contact left untouched! if (isflagset(FLAG_INIT_DLG) && !isflagset(FLAG_USRLOC_FWD)) { break; } # for in-dialog requests we get it easily because it provides loose_route() if (!isflagset(FLAG_INIT_DLG) && isflagset(FLAG_NEXT_ROUTE)) { break; } # Prevent that we over-write the Contact with the IP of our proxy when # the reply loops through ourself. if (src_ip == myself) { break; } # In this case we check only if the Contact URI contains a private # IP, because the Via header contains only informations from the UAC. # Additionally we check if the port in the Contact URI differs from # the port of the transport to catch UAS or ALG which put the public # IP address into the Contact header, but "forget" about the port. # Warning: if you are dealing with SIP implementations which are # running on public IP and do as-symmertic signaling for whatever # reason the following check will make their signaling symmetric. # If you need to support as-symmertic signaling reduce the following # nat_uac_test for UDP to just "1". if ( (proto == UDP && nat_uac_test("33")) || (nat_uac_test("1") || (@hf_value["contact"] != "" && @contact.uri.params.maddr != ""))) { # TODO: check if no proxy between UAS&myself fix_nated_contact(); } } # Activates RTP proxy if necessary. # route[RTPPROXY] { if (@cfg_get.rtp_proxy.enabled == "0") { # RTP Proxy is disabled break; } else if (@cfg_get.rtp_proxy.enabled == "detect") { if (!isflagset(FLAG_NAT)) { # If no NAT is involved we don't have to do here anything. break; } } else if (@cfg_get.rtp_proxy.enabled != "1") { # This is not a valid setting xlog("L_ERR", "Unknown option for rtp_proxy.enabled: %@cfg_get.rtp_proxy.enabled\n"); break; } # else rtp proxy is permanently enabled # If the message terminates a dialog for which the RTP proxy # was turned on, turn it off again. if ((method == "BYE" && isflagset(FLAG_RTP_PROXY)) || (method == "CANCEL")) { unforce_rtp_proxy(); append_hf("P-RTP-Proxy: Off\r\n"); break; } # Turn the RTP proxy on for INVITEs and UPDATEs, if they # have a body if (((method=="INVITE" || method == "UPDATE") && @msg.body != "") && !isflagset(FLAG_RTP_PROXY)) { force_rtp_proxy('r'); append_hf("P-RTP-Proxy: On\r\n"); setflag(FLAG_RTP_PROXY); $rproxy = 1; setavpflag($rproxy, "dialog_cookie"); } } # Handling of Route headers # route[PROCESS_ROUTES] { # subsequent messages withing a dialog should take the # path determined by the Route headers. if (loose_route()) { if (!defined $dialog_id) { $dialog_id = $t.dialog_id; # there is only 1 dialog_id } if (@rr.next_route != "") { setflag("FLAG_NEXT_ROUTE"); } xlog("L_DEBUG", "\n%mb\n\ndialogid -/from/to=%$dialog_id/%$f.dialog_id/%$t.dialog_id"); if (method == "INVITE" || method == "UPDATE" || method == "ACK" || method == "BYE") { if (!defined $dialog_id) { sl_reply("400", "Missing cookie"); drop; } } # Mark routing logic in request. append_hf("P-hint: rr-enforced\r\n"); # If the Route contained the accounting AVP cookie we # set the accounting flag for the acc_db module. # This is more for demonstration purpose as this could # also be solved without RR cookies. # Note: this means all in-dialog request will show up in # the accounting tables, so prepare your accounting software # for this. if ($account == "yes") { setflag(FLAG_ACC); setflag(FLAG_ACC_MISSED); } # Restore the RTP proxy flag if present if ($rproxy == "1") { setflag(FLAG_RTP_PROXY); } # Restore Session Timer flag and headers. if ( defined $stimer && ($stimer != "0")) { route(SESSION_TIMER); } # Some broken devices overide the dialog route set with the # Record-Route headers from each in-dialog request. So, we # better add Record-Route headers again. If we call # record_route() after loose_route(), the AVP cookies are # restored automatically. Additionally, there is a scenario # where Record-Route headers are necessary if an initial # SUBSCRIBE is forked. # # Note that here we forward before authentication checks # are executed. Generally, we only authenticate # out-of-dialog requests. Some in-dialog requests can't be # authenticated at all, see the call-forwarding example in # route[DOMAIN]. route(RECORD_ROUTE); route(FORWARD); } } # Add a Record-Route header # route[RECORD_ROUTE] { if (!isflagset(FLAG_RR_DONE) && method != "REGISTER") { # We record-route all messages to make sure that # subsequent messages will go through our proxy. This is # particularly good if upstream and downstream entities # use different transport protocols. # If the ACC flag is set, store this in a Record-Route # AVP cookie. This is more for demonstration purposes. if (isflagset(FLAG_ACC)) { $account = "yes"; setavpflag($account, "dialog_cookie"); } setavpflag("$f.dialog_id", "dialog_cookie"); # Insert the RR header. record_route(); # This flag allows to call this route several times # without inserting several RR headers. setflag(FLAG_RR_DONE); } } # Look up the domains of the caller and the callee. # route[DOMAIN] { # Check whether the caller is from a local domain. lookup_domain("$fd", "@from.uri.host"); # Check whether the callee is at a local domain lookup_domain("$td", "@ruri.host"); } # Check domain usage policies and reject illegal requests. # route[DOMAIN_POLICY] { # If we don't know the domain of the caller nor the domain of the # callee, somone tries to use our proxy as a relay. However, we # can only apply this check out-of-dialog requests without a To # tag. In some cases such as call-forwarding, subsequent requests # may not include served domain neither as origination nor # destination (a@A calls b@B who forwards to c@C. A BYE by c@C is # then From b@B and To a@A. There is no mentioning of c@C despite # legitimate behaviour of c@C). if (!isflagset(FLAG_TOTAG) && strempty($t.did) && strempty($f.did)) { sl_reply("403", "Relaying Forbidden"); drop; } } # The Registrar # route[REGISTRAR] { # Process only REGISTERs here. if (method != "REGISTER") { break; } # If this is a replica (sent to the multicast address), trust it to # be secure and store it in usrloc if (dst_ip==224.0.1.75) { if (!isflagset(FLAG_REPL_ENABLED)) { # Multicast replication administratively disabled. # Ignore. drop; } # Read marker from master if (search("^Repl-Marker: nated")) { setflag(FLAG_NAT); } # If the replicating server added its own server id to the # request, obtain the value and store it in an attribute. # This is used by registrar. $server_id = @msg.header["SER-Server-ID"]; # Assume URI in form of UID@mydomain and store contacts # under this UID. Note that this only works if local policy # causes UIDs to have form compliant to RFC3261 URI # usernames. if (@ruri.user!="") $tu.uid = @ruri.user; if (isflagset(FLAG_NAT)) { setflag(FLAG_NAT_REG); } if (!save_mem_nr("location")) { log(1, "Error while saving replicated REGISTER.\n"); } drop; } else { # This is a REGISTER request received from the UA. Remove # our internal header fields if they are present. The may # have been added maliciously. remove_hf("SER-Server-ID"); remove_hf("Repl-Marker"); } # Check if the REGISTER if for one of our local domains. if (strempty($t.did)) { sl_reply("403", "Register Forwarding Forbidden"); drop; } # The REGISTER target is in the To header, so reload the domain. if (!lookup_domain("$td", "@to.uri.host")) { sl_reply("404", "Unknown Domain"); drop; } # Useful for clients that ignore expires in 200 (OK). This is an # attempt to keep them sticking to our value of 600. append_to_reply("Expires: 600\r\n"); append_to_reply("Min-Expires: 240\r\n"); # We want only authenticated users to be registered. if (!www_authenticate("$fd.digest_realm", "credentials")) { if ($? == -2) { sl_reply("500", "Internal Server Error"); } else if ($? == -3) { sl_reply("400", "Bad Request"); } else { if ($digest_challenge != "") { append_to_reply("%$digest_challenge"); } sl_reply("401", "Unauthorized"); } drop; } # Check if the authenticated user is the same as the target user. if (!lookup_user("$tu.uid", "@to.uri")) { sl_reply("404", "Unknown user in To"); drop; } # the authentication ID does not match the ID in the To header if ($f.uid != $t.uid) { sl_reply("403", "Authentication and To-Header mismatch"); drop; } # Check if the authenticated user is the same as the request # originator. You may uncomment it if you care, which URI is in # the From header. #if (!lookup_user("$fr.uid", "@from.uri")) { # sl_reply("404", "Unknown user in From"); # drop; #} #if ($fu.uid != $fr.uid) { # sl_reply("403", "Authentication and From-Header mismatch"); # drop; #} if (isflagset(FLAG_NAT)) { setflag(FLAG_NAT_REG); } # Everything is fine. Store the binding. if (!save_contacts("location")) { sl_reply("400", "Invalid REGISTER Request"); drop; } # do not delete the following 3 lines, they are used by debconf #DEBCONF-REPLICATION1-START # #DEBCONF-REPLICATION1-END if (isflagset(FLAG_REPL_ENABLED)) { if (isflagset(FLAG_NAT)) { append_hf("Repl-Marker: nated\r\n"); } # Append this server's unique ID to the request append_hf_value("SER-Server-ID", "%@sys.server_id"); # We are multicasting a successful REGISTER to all proxies # on the multicast network to replicate the contact # addresses to all of them. In case they share the same IP # address (VIP) it is important to set the sending IP # address to an unshared one (in the future a special mcast # module may use unbound sockets for sending and leave # the source IP address decision up to kernel routing # tables). #DEBCONF-REPL_SEND_ADDR-START force_send_socket(udp:127.0.0.1); #DEBCONF-REPL_SEND_ADDR-END # Put the UID in the Request-URI so that it doesn't have to # be looked up in the database by all multicast receivers. attr2uri("$tu.uid","user"); forward_udp(224.0.1.75,5060); } #DEBCONF-REPLICATION2-START # #DEBCONF-REPLICATION2-END drop; } # Authentication of request originators claiming to belong to one of our # domains. # route[AUTHENTICATION] { # CANCELs and ACKs cannot be challenged. if (method=="CANCEL" || method=="ACK") { break; } # Requests from non-local to local domains should be permitted. # Remove this if you want a walled garden. if (strempty($f.did)) { break; } # Gateways are usually not able to authenticate for their requests. # You have to trust them base on some other information such as the # source IP address. # WARNING: If at all this is only safe in a local network! if (src_ip == $gw_ip) { break; } if (!proxy_authenticate("$fd.digest_realm", "credentials")) { if ($? == -2) { sl_reply("500", "Internal Server Error"); } else if ($? == -3) { sl_reply("400", "Bad Request"); } else { if (defined $digest_challenge && $digest_challenge != "") { append_to_reply("%$digest_challenge"); } sl_reply("407", "Proxy Authentication Required"); } drop; } # Check if the UID derived from authentication matches that from # the From header. if (!lookup_user("$fr.uid", "@from.uri")) { sl_reply("403", "Fake Identity"); drop; } if ($fu.uid != $fr.uid) { sl_reply("403", "Fake Identity"); drop; } setflag(FLAG_AUTH_OK); # Load the user attributes of the caller. load_attrs("$fu", "$f.uid"); } # Process request targeted to non-local domains. # route[OUTBOUND] { # If a local user calls to a foreign domain we play outbound # proxy for them. # Comment this out if you want a walled garden. if ($f.did != "" && strempty($t.did)) { append_hf("P-hint: outbound\r\n"); route(FORWARD); } } # Process speeddial addresses. # route[SPEEDDIAL] { # If the caller is local and uses two digits only, we redirect the # UA to the real target. if ($fd.did != "" && uri =~ "sip:[0-9][0-9]@") { if (sd_lookup("speed_dial")) { sl_reply("302", "Speed Dial Redirect"); } else { sl_reply("404", "Speed Dial Not Found"); } drop; } } # Process requests targeted to a local user. # route[INBOUND] { # lets see if know the callee if (!lookup_user("$tu.uid", "@ruri")) { break; } # Load the attributes of the callee. load_attrs("$tu", "$t.uid"); # You can check if the called URI is in fact an alias like this. #if (! $tu.uri_canonical) { # # If the alias URI has different attributes, you can load # # them into the URI track like this. # load_attrs("$tr", "@ruri"); #} # Check for call forwarding of the callee. # Note: The forwarding target has to be full routable URI # in this example. if (defined $tu.fwd_always_target && $tu.fwd_always_target != "") { attr2uri("$tu.fwd_always_target"); # If we are forwarding to ourselves, don't remove # credentials. Otherwise the request would be challenged # again. # Note: This doesn't apply to failure_route which may # still be problematic -- credentials are already # removed when we forward. Consider using a 3xx. lookup_domain("$td", "@ruri.host"); if (defined $t.did && $t.did != "") { setflag(FLAG_DONT_RM_CRED); } route(FORWARD); } # Native SIP destinations are handled using the usrloc database. if (lookup_contacts("location")) { append_hf("P-hint: usrloc applied\r\n"); # destination is behind NAT if (isflagset(FLAG_NAT_REG)) { setflag(FLAG_NAT); /* client was behind NAT when made registration */ } # We set the tm module timers according to the prefences # of the callee (avoid too long ringing of his phones). # Note1: Timer values have to be in ms now! # Note2: This makes even more sense if you switch to a # voicemail from the FAILURE_ROUTE below. if ($t.fr_inv_timer) { if ($t.fr_timer) { t_set_fr("$t.fr_inv_timer", "$t.fr_timer"); } else { t_set_fr("$t.fr_inv_timer"); } } # This enables session timer support as long as one side # supports it. If you want to have session timmer support # only for calls from your PSTN gateway but not between pure # VoIP calls you can remove the comment marks from the if # clause in the next line and closing bracket below. # WARNING: If at all you should trust IP addresses only in # your local network! #if (src_ip == $gw_ip) { route(SESSION_TIMER); #} route(FORWARD); } else { sl_reply("480", "Temporarily unavailable"); drop; } } # Process calls for PSTN. # route[PSTN] { # Check some conditions first: # PSTN is available for our own users only. if (strempty($f.did)) { break; } # If the attribute $gw_ip isn't set, there is no PSTN service # active. if (!defined $gw_ip) { break; } # And finally, the username of the Request-URI must look like # a phone number. if (!uri =~ "^sips?:\+?[0-9]{3,18}@") { break; } # You may have to convert the number in the Request-URI into a # format that is accepted by your gateway here. # Check permissions of the caller for initial INVITEs. if (isflagset(FLAG_INIT_DLG)) { if ($f.gw_acl != "1") { sl_reply("403", "PSTN Not Permitted"); drop; } } # If the attribute $asserted_id is set, we add its contents as a # Remote-Party-ID header. # Depending on your gateway, you may have to add a # P-Asserted-Identity header here instead. if (defined $asserted_id) { xlset_attr("$rpidheader", ";screen=yes"); replace_attr_hf("Remote-Party-ID", "$rpidheader"); } # Enable Session Timer support with the gateway. route(SESSION_TIMER); # Replace the domain part of the Request-URI with the value from # the attribute and send it out. attr2uri("$gw_ip", "domain"); # Set the PSTN_ALLOWED flag. This will be checked on the # onsend_route. setflag(FLAG_PSTN_ALLOWED); route(FORWARD); } # Try to process CANCEL requests quickly. # route[CATCH_CANCEL] { if (method == CANCEL) { # t_relay_cancel() will stop processing if a matching # INVITE was found. xlog("L_DEBUG", "catching cancel dialogid=%$dialog_id\n"); if (!t_relay_cancel()) { # An INVITE was found but some error occurred. sl_reply("500", "Internal Server Error"); drop; } # Bad luck, no corresponding INVITE was found, we have to # continue with the script. } } # Site specific policy. # route[SITE_SPECIFIC] { # This is only relevant for requests for one of our domains. if (strempty($t.did)) { break; } # Do site specific routing such as peering. # For example: if (uri=~"^sip:000777") { rewritehostport("sems01.iptel.org:5074"); route(FORWARD); } } # Process Session-Timer. # route[SESSION_TIMER] { # We are only interested in session establishment or session # refreshing. # if (method != "INVITE" && method != "UPDATE") { break; } # Let's check if the Session-Expires header is already present. if (@hf_value.session_expires != "") { # Compare the Session-Expires header value with the # configured Min-SE. eval_push("x:%@hf_value.session_expires.uri"); eval_oper("(int)", -1); eval_push("x:%@cfg_get.session_timer.min_se"); eval_oper("(int)", -1); eval_oper(">=", -2); # Let's check for the Suported header. if (hf_value_exists("Supported", "timer")) { # The UAC supports Session-Timer, so we # only need to take a look at the values if (@eval.pop[-1] == "0") { # Session interval is lower than the # configured Min-SE append_to_reply("Min-SE: %@cfg_get.session_timer.min_se\r\n"); sl_reply("422", "Session Interval Too Small"); drop; } # We store the session expires value for the reply # route and mark the attribute for inserting as # Record-Route cookie. $stimer = @hf_value.session_expires.uri; setavpflag($stimer, "dialog_cookie"); # Set the session timer flag that indicates the # UAC supports the extension. setflag(FLAG_SESSIONTIMER); } else { # Session epxires was already inserted by some other # proxy if (@eval.pop[-1] == "0") { # Session interval is lower than the # configured Min-SE. There is no point in # sending 422 response, because the UAC # does not support the extension, the values # can be corrected instead. assign_hf_value("Session-Expires", "%@cfg_get.session_timer.min_se"); remove_hf_value("Min-SE"); append_hf_value("Min-SE", "%@cfg_get.session_timer.min_se"); } } } else { # No Session Timer is requested yet, neither by UAC nor by # proxy if (@cfg_get.session_timer.default != "0") { # Add a Session Expires header to see if the UAS # supports Session Timer. We do not insert a # Required header because then the call might fail. append_hf_value("Session-Expires", "%@cfg_get.session_timer.default"); if (@cfg_get.session_timer.min_se != "90") { append_hf_value("Min-SE", "%@cfg_get.session_timer.min_se"); } # Mark the attribute to be inserted as a # Record-Route cookie $stimer = @cfg_get.session_timer.default; setavpflag($stimer, "dialog_cookie"); } } } # Route which checks and performs ENUM queries # # route[ENUM] { # perform ENUM query only if the RURI contains an E.164 # number as uer part if (uri =~ "sip:\+[0-9]?@") { # if the ENUM query was successful send it right # away of to the new target, otherwise do nothing if (enum_query()) { route(FORWARD); } } } # Failure route for initial INVITEs. # failure_route[FAILURE_ROUTE] { if (isflagset(FLAG_INIT_DLG)) { # Mark that we are operating from a failure route. setflag(FLAG_FAILUREROUTE); if (t_check_status("486|600")) { # If we received a busy and a busy target is set, forward # it there. # Note: Again, the forwarding target has to be a routeable # URI. We redirect using 3xx to avoid possible issues with # credentials (if we consumed them, they may be missing in # a loop, if we don't consume them, messages are bigger and # more vulnerable) if ($tu.fwd_busy_target != "") { attr2uri("$tu.fwd_busy_target"); #attr_destination("$tu.fwd_busy_target"); #route(FORWARD); t_reply("302", "Redirect On Busy"); } # Alternatively, you could forward the request to # SEMS/voicemail here } else if (t_check_status("408|480")) { # If we received no answer and the noanswer target is set, # forward it there. # Note: See above. if ($tu.fwd_noanswer_target != "") { attr2uri("$tu.fwd_noanswer_target"); #attr_destination("$tu.fwd_noanswer_target"); #route(FORWARD); t_reply("302", "Redirect On No Answer"); } } } # if (isflagset... } # Onreply route that fixes NAT in responses. # onreply_route[REPLY_ROUTE] { # Check and fix the Contact in the reply to # allow proper routing of in-dialog messages. route(UAS_NAT_DETECTION); # If RTP proxy was activated and this is a 18x or 2xx reply with a # body, inform RTP proxy. if (isflagset(FLAG_RTP_PROXY) && status=~"(18[03])|(2[0-9][0-9])" && @msg.body != "") { force_rtp_proxy('r'); } # Let's check for session timer support. if (isflagset(FLAG_SESSIONTIMER) && status =~ "2[0-9][0-9]") { # The UAC wanted to have a session timer. if (strempty(@hf_value.session_expires)) { # But the UAS does not support it, so we will try # to convince the UAC to do it. append_hf_value("Session-Expires", "%$stimer;refresher=uac"); if (!hf_value_exists("Require", "timer")) { include_hf_value("Require", "timer"); } } } } # Do some final checks before a request is sent out. onsend_route { # Bypass check: Eliminate requests to the PSTN gateway if they have # not passed ACL checks and are not marked with FLAG_PSTN_ALLOWED # but are dialog-initiating requests (no to-tag, no CANCEL, no ACK). # This helps to stop policy bypasses (gateway IP uploaded as a # forked contact, or a call-forwarding destination, or a DNS name, # or a preloaded route, or something else possibly) if (defined $g.gw_ip && to_ip==$g.gw_ip && !isflagset(FLAG_PSTN_ALLOWED) && !isflagset(FLAG_TOTAG) && method != "ACK" && method != "CANCEL") { log(1, "ALERT: non authorized packet for PSTN, dropping...\n%mb\n"); # You can't use advanced features from onsend_route. # xlog("L_ALERT", "non authorized packet for PSTN, dropping...\n%mb\n"); drop; } # RFC 1918 relay protection: Useful if SER is attached to an # administrative network using private IP address space and you # wish to prevent UACs from relaying their packets there. # # You will have to comment this out, if you are regularly serving # an RFC 1918 address space. if (to_ip==10.0.0.0/8 || to_ip==172.16.0.0/12 || to_ip==192.168.0.0/16) { log(1, "ALERT: Packet targeted to an RFC1918 address dropped\n"); drop; } } # Run every minute by the timer module. # route[ON_1MIN_TIMER] { # Cleanup expired location records # MySQL version: db_query("delete from location where expires= 8192) { xmlrpc_reply("513", "request too big"); return; } #!ifdef XMLRPC_TLS_ONLY # allow xmlrpc only on TLS and only if the client certificate is valid if (proto!=TLS){ xmlrpc_reply("400", "xmlrpc allowed only over TLS"); return; } if (@tls.peer.verified!=""){ xmlrpc_reply("400", "Unauthorized"); return; } #!endif # close connection only for xmlrpclib user agents (there is a bug in # xmlrpclib: it waits for EOF before interpreting the response). if (search("^User-Agent:.*xmlrpclib")) set_reply_close(); set_reply_no_connect(); # optional dispatch_rpc(); } kamailio-4.0.4/etc/kamailio-oob.cfg0000644000000000000000000005330212223032460015577 0ustar rootroot#!KAMAILIO # # Kamailio (OpenSER) SIP Server v3.4 - default configuration script # - web: http://www.kamailio.org # - git: http://sip-router.org # # Direct your questions about this file to: # # Refer to the Core CookBook at http://www.kamailio.org/wiki/ # for an explanation of possible statements, functions and parameters. # # Several features can be enabled using '#!define WITH_FEATURE' directives: # # *** To run in debug mode: # - define WITH_DEBUG # # *** To enable mysql: # - define WITH_MYSQL # # *** To enable authentication execute: # - enable mysql # - define WITH_AUTH # - add users using 'kamctl' # # *** To enable IP authentication execute: # - enable mysql # - enable authentication # - define WITH_IPAUTH # - add IP addresses with group id '1' to 'address' table # # *** To enable persistent user location execute: # - enable mysql # - define WITH_USRLOCDB # # *** To enable presence server execute: # - enable mysql # - define WITH_PRESENCE # # *** To enable nat traversal execute: # - define WITH_NAT # - install RTPProxy: http://www.rtpproxy.org # - start RTPProxy: # rtpproxy -l _your_public_ip_ -s udp:localhost:7722 # # *** To enable PSTN gateway routing execute: # - define WITH_PSTN # - set the value of pstn.gw_ip # - check route[PSTN] for regexp routing condition # # *** To enable database aliases lookup execute: # - enable mysql # - define WITH_ALIASDB # # *** To enable speed dial lookup execute: # - enable mysql # - define WITH_SPEEDDIAL # # *** To enable multi-domain support execute: # - enable mysql # - define WITH_MULTIDOMAIN # # *** To enable TLS support execute: # - adjust CFGDIR/tls.cfg as needed # - define WITH_TLS # # *** To enable XMLRPC support execute: # - define WITH_XMLRPC # - adjust route[XMLRPC] for access policy # # *** To enable the embedded http server: # - define WITH_XHTTP # # *** To enable the RPC web interface execute: # - enable xhttp # - define WITH_XHTTP_RPC # # *** To enable the provisioning web interface execute: # - enable mysql # - enable xhttp # - define WITH_XHTTP_PI # # *** To enable anti-flood detection execute: # - adjust pike and htable=>ipban settings as needed (default is # block if more than 16 requests in 2 seconds and ban for 300 seconds) # - define WITH_ANTIFLOOD # # *** To block 3XX redirect replies execute: # - define WITH_BLOCK3XX # # *** To enable VoiceMail routing execute: # - define WITH_VOICEMAIL # - set the value of voicemail.srv_ip # - adjust the value of voicemail.srv_port # # *** To enhance accounting execute: # - enable mysql # - define WITH_ACCDB # - add following columns to database #!ifdef ACCDB_COMMENT ALTER TABLE acc ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE acc ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT ''; ALTER TABLE acc ADD COLUMN src_ip varchar(64) NOT NULL default ''; ALTER TABLE acc ADD COLUMN dst_ouser VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE acc ADD COLUMN dst_user VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE acc ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT ''; ALTER TABLE missed_calls ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE missed_calls ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT ''; ALTER TABLE missed_calls ADD COLUMN src_ip varchar(64) NOT NULL default ''; ALTER TABLE missed_calls ADD COLUMN dst_ouser VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE missed_calls ADD COLUMN dst_user VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE missed_calls ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT ''; #!endif ####### Include Local Config If Exists ######### import_file "kamailio-local.cfg" ####### Defined Values ######### # *** Value defines - IDs used later in config #!ifdef WITH_MYSQL # - database URL - used to connect to database server by modules such # as: auth_db, acc, usrloc, a.s.o. #!ifndef DBURL #!define DBURL "mysql://kamailio:kamailiorw@localhost/kamailio" #!endif #!endif #!ifdef WITH_MULTIDOMAIN # - the value for 'use_domain' parameters #!define MULTIDOMAIN 1 #!else #!define MULTIDOMAIN 0 #!endif # - flags # FLT_ - per transaction (message) flags # FLB_ - per branch flags #!define FLT_ACC 1 #!define FLT_ACCMISSED 2 #!define FLT_ACCFAILED 3 #!define FLT_NATS 5 #!define FLB_NATB 6 #!define FLB_NATSIPPING 7 ####### Global Parameters ######### #!ifdef WITH_DEBUG debug=4 log_stderror=yes #!else debug=2 log_stderror=no #!endif memdbg=5 memlog=5 log_facility=LOG_LOCAL0 fork=yes children=4 /* uncomment the next line to disable TCP (default on) */ #disable_tcp=yes /* uncomment the next line to disable the auto discovery of local aliases based on reverse DNS on IPs (default on) */ #auto_aliases=no /* add local domain aliases */ #alias="sip.mydomain.com" /* uncomment and configure the following line if you want Kamailio to bind on a specific interface/port/proto (default bind on all available) */ #listen=udp:10.0.0.10:5060 /* port to listen to * - can be specified more than once if needed to listen on many ports */ port=5060 #!ifdef WITH_TLS enable_tls=yes #!endif # life time of TCP connection when there is no traffic # - a bit higher than registration expires to cope with UA behind NAT tcp_connection_lifetime=3605 #!ifdef WITH_XHTTP tcp_accept_no_cl=yes #!endif ####### Custom Parameters ######### # These parameters can be modified runtime via RPC interface # - see the documentation of 'cfg_rpc' module. # # Format: group.id = value 'desc' description # Access: $sel(cfg_get.group.id) or @cfg_get.group.id # #!ifdef WITH_PSTN # PSTN GW Routing # # - pstn.gw_ip: valid IP or hostname as string value, example: # pstn.gw_ip = "10.0.0.101" desc "My PSTN GW Address" # # - by default is empty to avoid misrouting pstn.gw_ip = "" desc "PSTN GW Address" pstn.gw_port = "" desc "PSTN GW Port" #!endif #!ifdef WITH_VOICEMAIL # VoiceMail Routing on offline, busy or no answer # # - by default Voicemail server IP is empty to avoid misrouting voicemail.srv_ip = "" desc "VoiceMail IP Address" voicemail.srv_port = "5060" desc "VoiceMail Port" #!endif ####### Modules Section ######## # set paths to location of modules (to sources or installation folders) #!ifdef WITH_SRCPATH mpath="modules_k:modules" #!else mpath="/usr/local/lib/kamailio/modules_k/:/usr/local/lib/kamailio/modules/" #!endif #!ifdef WITH_MYSQL loadmodule "db_mysql.so" #!endif loadmodule "mi_fifo.so" loadmodule "kex.so" loadmodule "corex.so" loadmodule "tm.so" loadmodule "tmx.so" loadmodule "sl.so" loadmodule "rr.so" loadmodule "pv.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "siputils.so" loadmodule "xlog.so" loadmodule "sanity.so" loadmodule "ctl.so" loadmodule "cfg_rpc.so" loadmodule "mi_rpc.so" loadmodule "acc.so" #!ifdef WITH_AUTH loadmodule "auth.so" loadmodule "auth_db.so" #!ifdef WITH_IPAUTH loadmodule "permissions.so" #!endif #!endif #!ifdef WITH_ALIASDB loadmodule "alias_db.so" #!endif #!ifdef WITH_SPEEDDIAL loadmodule "speeddial.so" #!endif #!ifdef WITH_MULTIDOMAIN loadmodule "domain.so" #!endif #!ifdef WITH_PRESENCE loadmodule "presence.so" loadmodule "presence_xml.so" #!endif #!ifdef WITH_NAT loadmodule "nathelper.so" loadmodule "rtpproxy.so" #!endif #!ifdef WITH_TLS loadmodule "tls.so" #!endif #!ifdef WITH_ANTIFLOOD loadmodule "htable.so" loadmodule "pike.so" #!endif #!ifdef WITH_XMLRPC loadmodule "xmlrpc.so" #!endif #!ifdef WITH_DEBUG loadmodule "debugger.so" #!endif #!ifdef WITH_XHTTP loadmodule "xhttp.so" #!endif #!ifdef WITH_XHTTP_RPC loadmodule "xhttp_rpc.so" #!endif #!ifdef WITH_XHTTP_PI loadmodule "xhttp_pi.so" #!endif # ----------------- setting module-specific parameters --------------- # ----- mi_fifo params ----- modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") # ----- tm params ----- # auto-discard branches from previous serial forking leg modparam("tm", "failure_reply_mode", 3) # default retransmission timeout: 30sec modparam("tm", "fr_timer", 30000) # default invite retransmission timeout after 1xx: 120sec modparam("tm", "fr_inv_timer", 120000) # ----- rr params ----- # add value to ;lr param to cope with most of the UAs modparam("rr", "enable_full_lr", 1) # do not append from tag to the RR (no need for this script) modparam("rr", "append_fromtag", 0) # ----- registrar params ----- modparam("registrar", "method_filtering", 1) /* uncomment the next line to disable parallel forking via location */ # modparam("registrar", "append_branches", 0) /* uncomment the next line not to allow more than 10 contacts per AOR */ #modparam("registrar", "max_contacts", 10) # max value for expires of registrations modparam("registrar", "max_expires", 3600) # set it to 1 to enable GRUU modparam("registrar", "gruu_enabled", 0) # ----- acc params ----- /* what special events should be accounted ? */ modparam("acc", "early_media", 0) modparam("acc", "report_ack", 0) modparam("acc", "report_cancels", 0) /* by default ww do not adjust the direct of the sequential requests. if you enable this parameter, be sure the enable "append_fromtag" in "rr" module */ modparam("acc", "detect_direction", 0) /* account triggers (flags) */ modparam("acc", "log_flag", FLT_ACC) modparam("acc", "log_missed_flag", FLT_ACCMISSED) modparam("acc", "log_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_ouser=$tU;dst_user=$rU;dst_domain=$rd") modparam("acc", "failed_transaction_flag", FLT_ACCFAILED) /* enhanced DB accounting */ #!ifdef WITH_ACCDB modparam("acc", "db_flag", FLT_ACC) modparam("acc", "db_missed_flag", FLT_ACCMISSED) modparam("acc", "db_url", DBURL) modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_ouser=$tU;dst_user=$rU;dst_domain=$rd") #!endif # ----- usrloc params ----- /* enable DB persistency for location entries */ #!ifdef WITH_USRLOCDB modparam("usrloc", "db_url", DBURL) modparam("usrloc", "db_mode", 2) modparam("usrloc", "use_domain", MULTIDOMAIN) #!endif # ----- auth_db params ----- #!ifdef WITH_AUTH modparam("auth_db", "db_url", DBURL) modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") modparam("auth_db", "load_credentials", "") modparam("auth_db", "use_domain", MULTIDOMAIN) # ----- permissions params ----- #!ifdef WITH_IPAUTH modparam("permissions", "db_url", DBURL) modparam("permissions", "db_mode", 1) #!endif #!endif # ----- alias_db params ----- #!ifdef WITH_ALIASDB modparam("alias_db", "db_url", DBURL) modparam("alias_db", "use_domain", MULTIDOMAIN) #!endif # ----- speeddial params ----- #!ifdef WITH_SPEEDDIAL modparam("speeddial", "db_url", DBURL) modparam("speeddial", "use_domain", MULTIDOMAIN) #!endif # ----- domain params ----- #!ifdef WITH_MULTIDOMAIN modparam("domain", "db_url", DBURL) # register callback to match myself condition with domains list modparam("domain", "register_myself", 1) #!endif #!ifdef WITH_PRESENCE # ----- presence params ----- modparam("presence", "db_url", DBURL) # ----- presence_xml params ----- modparam("presence_xml", "db_url", DBURL) modparam("presence_xml", "force_active", 1) #!endif #!ifdef WITH_NAT # ----- rtpproxy params ----- modparam("rtpproxy", "rtpproxy_sock", "udp:127.0.0.1:7722") # ----- nathelper params ----- modparam("nathelper", "natping_interval", 30) modparam("nathelper", "ping_nated_only", 1) modparam("nathelper", "sipping_bflag", FLB_NATSIPPING) modparam("nathelper", "sipping_from", "sip:pinger@kamailio.org") # params needed for NAT traversal in other modules modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)") modparam("usrloc", "nat_bflag", FLB_NATB) #!endif #!ifdef WITH_TLS # ----- tls params ----- modparam("tls", "config", "/usr/local/etc/kamailio/tls.cfg") #!endif #!ifdef WITH_ANTIFLOOD # ----- pike params ----- modparam("pike", "sampling_time_unit", 2) modparam("pike", "reqs_density_per_unit", 16) modparam("pike", "remove_latency", 4) # ----- htable params ----- # ip ban htable with autoexpire after 5 minutes modparam("htable", "htable", "ipban=>size=8;autoexpire=300;") #!endif #!ifdef WITH_XMLRPC # ----- xmlrpc params ----- modparam("xmlrpc", "route", "XMLRPC"); modparam("xmlrpc", "url_match", "^/RPC") #!endif #!ifdef WITH_DEBUG # ----- debugger params ----- modparam("debugger", "cfgtrace", 1) #!endif #!ifdef WITH_XHTTP_RPC modparam("xhttp_rpc", "xhttp_rpc_root", "http_rpc") #!endif #!ifdef WITH_XHTTP_PI modparam("xhttp_pi", "xhttp_pi_root", "http_pi") modparam("xhttp_pi", "framework", "/usr/local/etc/kamailio/pi_framework.xml") #!endif ####### Routing Logic ######## # Main SIP request routing logic # - processing of any incoming SIP request starts with this route # - note: this is the same as route { ... } request_route { # per request initial checks route(REQINIT); # NAT detection route(NATDETECT); # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } # handle requests within SIP dialogs route(WITHINDLG); ### only initial requests (no To tag) t_check_trans(); # authentication route(AUTH); # record routing for dialog forming requests (in case they are routed) # - remove preloaded route headers remove_hf("Route"); if (is_method("INVITE|SUBSCRIBE")) record_route(); # account only INVITEs if (is_method("INVITE")) { setflag(FLT_ACC); # do accounting } # dispatch requests to foreign domains route(SIPOUT); ### requests for my local domains # handle presence related requests route(PRESENCE); # handle registrations route(REGISTRAR); if ($rU==$null) { # request with no Username in RURI sl_send_reply("484","Address Incomplete"); exit; } # dispatch destinations to PSTN route(PSTN); # user location service route(LOCATION); route(RELAY); } route[RELAY] { # enable additional event routes for forwarded requests # - serial forking, RTP relaying handling, a.s.o. if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) { if(!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH"); } if (is_method("INVITE|SUBSCRIBE|UPDATE")) { if(!t_is_set("onreply_route")) t_on_reply("MANAGE_REPLY"); } if (is_method("INVITE")) { if(!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE"); } if (!t_relay()) { sl_reply_error(); } exit; } # Per SIP request initial checks route[REQINIT] { #!ifdef WITH_ANTIFLOOD # flood dection from same IP and traffic ban for a while # be sure you exclude checking trusted peers, such as pstn gateways # - local host excluded (e.g., loop to self) if(src_ip!=myself) { if($sht(ipban=>$si)!=$null) { # ip is already blocked xdbg("request from blocked IP - $rm from $fu (IP:$si:$sp)\n"); exit; } if (!pike_check_req()) { xlog("L_ALERT","ALERT: pike blocking $rm from $fu (IP:$si:$sp)\n"); $sht(ipban=>$si) = 1; exit; } } #!endif if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if(!sanity_check("1511", "7")) { xlog("Malformed SIP message from $si:$sp\n"); exit; } } # Handle requests within SIP dialogs route[WITHINDLG] { if (has_totag()) { # sequential request withing a dialog should # take the path determined by record-routing if (loose_route()) { route(DLGURI); if (is_method("BYE")) { setflag(FLT_ACC); # do accounting ... setflag(FLT_ACCFAILED); # ... even if the transaction fails } else if ( is_method("ACK") ) { # ACK is forwarded statelessy route(NATMANAGE); } else if ( is_method("NOTIFY") ) { # Add Record-Route for in-dialog NOTIFY as per RFC 6665. record_route(); } route(RELAY); } else { if (is_method("SUBSCRIBE") && uri == myself) { # in-dialog subscribe requests route(PRESENCE); exit; } if ( is_method("ACK") ) { if ( t_check_trans() ) { # no loose-route, but stateful ACK; # must be an ACK after a 487 # or e.g. 404 from upstream server t_relay(); exit; } else { # ACK without matching transaction ... ignore and discard exit; } } sl_send_reply("404","Not here"); } exit; } } # Handle SIP registrations route[REGISTRAR] { if (is_method("REGISTER")) { if(isflagset(FLT_NATS)) { setbflag(FLB_NATB); # uncomment next line to do SIP NAT pinging ## setbflag(FLB_NATSIPPING); } if (!save("location")) sl_reply_error(); exit; } } # USER location service route[LOCATION] { #!ifdef WITH_SPEEDDIAL # search for short dialing - 2-digit extension if($rU=~"^[0-9][0-9]$") if(sd_lookup("speed_dial")) route(SIPOUT); #!endif #!ifdef WITH_ALIASDB # search in DB-based aliases if(alias_db_lookup("dbaliases")) route(SIPOUT); #!endif $avp(oexten) = $rU; if (!lookup("location")) { $var(rc) = $rc; route(TOVOICEMAIL); t_newtran(); switch ($var(rc)) { case -1: case -3: send_reply("404", "Not Found"); exit; case -2: send_reply("405", "Method Not Allowed"); exit; } } # when routing via usrloc, log the missed calls also if (is_method("INVITE")) { setflag(FLT_ACCMISSED); } } # Presence server route route[PRESENCE] { if(!is_method("PUBLISH|SUBSCRIBE")) return; #!ifdef WITH_PRESENCE if (!t_newtran()) { sl_reply_error(); exit; }; if(is_method("PUBLISH")) { handle_publish(); t_release(); } else if( is_method("SUBSCRIBE")) { handle_subscribe(); t_release(); } exit; #!endif # if presence enabled, this part will not be executed if (is_method("PUBLISH") || $rU==$null) { sl_send_reply("404", "Not here"); exit; } return; } # Authentication route route[AUTH] { #!ifdef WITH_AUTH #!ifdef WITH_IPAUTH if((!is_method("REGISTER")) && allow_source_address()) { # source IP allowed return; } #!endif if (is_method("REGISTER") || from_uri==myself) { # authenticate requests if (!auth_check("$fd", "subscriber", "1")) { auth_challenge("$fd", "0"); exit; } # user authenticated - remove auth header if(!is_method("REGISTER|PUBLISH")) consume_credentials(); } # if caller is not local subscriber, then check if it calls # a local destination, otherwise deny, not an open relay here if (from_uri!=myself && uri!=myself) { sl_send_reply("403","Not relaying"); exit; } #!endif return; } # Caller NAT detection route route[NATDETECT] { #!ifdef WITH_NAT force_rport(); if (nat_uac_test("19")) { if (is_method("REGISTER")) { fix_nated_register(); } else { add_contact_alias(); } setflag(FLT_NATS); } #!endif return; } # RTPProxy control route[NATMANAGE] { #!ifdef WITH_NAT if (is_request()) { if(has_totag()) { if(check_route_param("nat=yes")) { setbflag(FLB_NATB); } } } if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB))) return; rtpproxy_manage(); if (is_request()) { if (!has_totag()) { add_rr_param(";nat=yes"); } } if (is_reply()) { if(isbflagset(FLB_NATB)) { add_contact_alias(); } } #!endif return; } # URI update for dialog requests route[DLGURI] { #!ifdef WITH_NAT if(!isdsturiset()) { handle_ruri_alias(); } #!endif return; } # Routing to foreign domains route[SIPOUT] { if (!uri==myself) { append_hf("P-hint: outbound\r\n"); route(RELAY); } } # PSTN GW routing route[PSTN] { #!ifdef WITH_PSTN # check if PSTN GW IP is defined if (strempty($sel(cfg_get.pstn.gw_ip))) { xlog("SCRIPT: PSTN rotuing enabled but pstn.gw_ip not defined\n"); return; } # route to PSTN dialed numbers starting with '+' or '00' # (international format) # - update the condition to match your dialing rules for PSTN routing if(!($rU=~"^(\+|00)[1-9][0-9]{3,20}$")) return; # only local users allowed to call if(from_uri!=myself) { sl_send_reply("403", "Not Allowed"); exit; } if (strempty($sel(cfg_get.pstn.gw_port))) { $ru = "sip:" + $rU + "@" + $sel(cfg_get.pstn.gw_ip); } else { $ru = "sip:" + $rU + "@" + $sel(cfg_get.pstn.gw_ip) + ":" + $sel(cfg_get.pstn.gw_port); } route(RELAY); exit; #!endif return; } # XMLRPC routing #!ifdef WITH_XMLRPC route[XMLRPC] { # allow XMLRPC from localhost if ((method=="POST" || method=="GET") && (src_ip==127.0.0.1)) { # close connection only for xmlrpclib user agents (there is a bug in # xmlrpclib: it waits for EOF before interpreting the response). if ($hdr(User-Agent) =~ "xmlrpclib") set_reply_close(); set_reply_no_connect(); dispatch_rpc(); exit; } send_reply("403", "Forbidden"); exit; } #!endif # route to voicemail server route[TOVOICEMAIL] { #!ifdef WITH_VOICEMAIL if(!is_method("INVITE")) return; # check if VoiceMail server IP is defined if (strempty($sel(cfg_get.voicemail.srv_ip))) { xlog("SCRIPT: VoiceMail rotuing enabled but IP not defined\n"); return; } if($avp(oexten)==$null) return; $ru = "sip:" + $avp(oexten) + "@" + $sel(cfg_get.voicemail.srv_ip) + ":" + $sel(cfg_get.voicemail.srv_port); route(RELAY); exit; #!endif return; } # manage outgoing branches branch_route[MANAGE_BRANCH] { xdbg("new branch [$T_branch_idx] to $ru\n"); route(NATMANAGE); } # manage incoming replies onreply_route[MANAGE_REPLY] { xdbg("incoming reply\n"); if(status=~"[12][0-9][0-9]") route(NATMANAGE); } # manage failure routing cases failure_route[MANAGE_FAILURE] { route(NATMANAGE); if (t_is_canceled()) { exit; } #!ifdef WITH_BLOCK3XX # block call redirect based on 3xx replies. if (t_check_status("3[0-9][0-9]")) { t_reply("404","Not found"); exit; } #!endif #!ifdef WITH_VOICEMAIL # serial forking # - route to voicemail on busy or no answer (timeout) if (t_check_status("486|408")) { route(TOVOICEMAIL); exit; } #!endif } #!ifdef WITH_XHTTP event_route[xhttp:request] { #!ifdef WITH_XHTTP_RPC $var(xhttp_rpc_root) = $(hu{s.substr,0,9}); if ($var(xhttp_rpc_root) == "/http_rpc") { dispatch_xhttp_rpc(); } #!endif #!ifdef WITH_XHTTP_PI $var(xhttp_rpc_root) = $(hu{s.substr,0,8}); if ($var(xhttp_rpc_root) == "/http_pi") { dispatch_xhttp_pi(); } #!endif xhttp_reply("200", "OK", "text/html", "Wrong URL $hu"); } #!endif kamailio-4.0.4/etc/dbtext.cfg0000644000000000000000000000506212223032460014526 0ustar rootroot# # $Id$ # # simple quick-start config script # # ----------- global configuration parameters ------------------------ debug=3 # debug level (cmd line: -dddddddddd) fork=yes log_stderror=no # (cmd line: -E) /* debug=7 fork=no log_stderror=yes */ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 fifo="/tmp/sip-router_fifo" # ------------------ module loading ---------------------------------- # Uncomment this if you want to use SQL database loadmodule "./modules/dbtext/dbtext.so" loadmodule "./modules/sl/sl.so" loadmodule "./modules/tm/tm.so" loadmodule "./modules/rr/rr.so" loadmodule "./modules/maxfwd/maxfwd.so" loadmodule "./modules/usrloc/usrloc.so" loadmodule "./modules/registrar/registrar.so" # Uncomment this if you want digest authentication # mysql.so must be loaded ! loadmodule "./modules/auth/auth.so" loadmodule "./modules/auth_db/auth_db.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- #modparam("usrloc", "db_mode", 0) # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line modparam("usrloc", "db_mode", 1) modparam("usrloc", "db_url", "/home/janakj/sip-router") modparam("auth_db", "db_url", "/home/janakj/sip-router") # -- auth params -- # Uncomment if you are using auth module # modparam("auth_db", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) # modparam("auth_db", "plain_password_column", "password") # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (len_gt( max_len )) { sl_send_reply("513", "Message too big"); break; }; # Do strict routing if pre-loaded route headers present loose_route(); # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (method=="REGISTER") { if (!www_authorize("", "subscriber")) { www_challenge("", "0"); break; }; save("location"); break; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); break; }; }; # forward to current uri now if (!t_relay()) { sl_reply_error(); }; } kamailio-4.0.4/etc/sip-router.cfg0000644000000000000000000004223612223032460015351 0ustar rootroot# # $Id$ # # Example configuration file (simpler then ser-oob.cfg, but more # complex then ser-basic.cfg). # # First start SER sample config script with: # database, accounting, authentication, multi-domain support # PSTN GW section, named flags, named routes, global-, # domain- and user-preferences with AVPs # Several of these features are only here for demonstration purpose # what can be achieved with the SER config script language. # # If you look for a simpler version with a lot less dependencies # please refer to the ser-basic.cfg file in your SER distribution. # # If you look for documentation, try http://sip-router.org/wiki/. # The right mailing lists for questions about this file is # . # To get this config running you need to execute the following commands # with the new serctl (the capital word are just place holders) # - ser_ctl domain add DOMAINNAME # - ser_ctl user add USERNAME@DOMAINNAME -p PASSWORD # ser_ctl can be obtained from # http://ftp.iptel.org/pub/serctl/daily-snapshots/. # # If you want to have PID header for your user # - ser_attr add uid=UID asserted_id="PID" # If you want to have gateway support # - ser_db add attr_types name=gw_ip rich_type=string raw_type=2 description="The gateway IP for the default ser.cfg" default_flags=33 # - ser_attr add global gw_ip=GATEWAY-IP # ----------- Global Defines / Extra Features ------------------------------- # (can be enabled either by uncommenting the corresponding #!define # statement or by starting with -A WITH_, e.g. # ser -A WITH_TLS -f /etc/ser/ser-oob.cfg ) # enable TLS ##!define WITH_TLS # started from compile directory (not installed) ##!define LOCAL_TEST_RUN # xmlrpc allowed subnets (if defined XMLRPC requests with source ip matching # this network addresses will be allowed, if no XMLRPC_ALLOWED_SUBNETx is # defined only requests coming from localhost will be allowed). # E.g.: ser -A XMLRPC_ALLOW_NET1=192.168.1.0/24 -f ser-oob.cfg ##!define XMLRPC_ALLOW_NET1 192.168.0.0/16 ##!define XMLRPC_ALLOW_NET2 10.0.0.0/255.0.0.0 ##!define XMLRPC_ALLOW_NET3 172.16.0.0/12 # ----------- global configuration parameters ------------------------ debug=2 # debug level (cmd line: -dddddddddd) #memdbg=10 # memory debug log level #memlog=10 # memory statistics log level #log_facility=LOG_LOCAL0 # sets the facility used for logging (see syslog(3)) /* Uncomment these lines to enter debugging mode fork=no log_stderror=yes */ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) #port=5060 #children=4 #user=ser #group=ser #disable_core=yes #disables core dumping #open_fd_limit=1024 # sets the open file descriptors limit #mhomed=yes # usefull for multihomed hosts, small performance penalty #disable_tcp=yes #tcp_accept_aliases=yes # accepts the tcp alias via option (see NEWS) sip_warning=yes #!ifdef WITH_TLS enable_tls=yes #!endif # # ------------------ module loading ---------------------------------- #!ifdef LOCAL_TEST_RUN loadpath "modules:modules_s" #!else loadpath "/usr/lib/ser/modules:/usr/lib/ser/modules_s" #!endif # load a SQL database for authentication, domains, user AVPs etc. loadmodule "db_mysql" loadmodule "tm" loadmodule "sl" loadmodule "rr" loadmodule "maxfwd" loadmodule "usrloc" loadmodule "registrar" loadmodule "xlog" loadmodule "textops" loadmodule "ctl" loadmodule "cfg_rpc" loadmodule "auth" loadmodule "auth_db" loadmodule "gflags" loadmodule "domain" loadmodule "uri_db" loadmodule "avp" loadmodule "avp_db" loadmodule "acc_db" loadmodule "xmlrpc" #!ifdef WITH_TLS loadmodule "tls" #!endif # ----------------- setting script FLAGS ----------------------------- flags FLAG_ACC : 1, # include message in accounting FLAG_FAILUREROUTE : 2; # we are operating from a failure route avpflags dialog_cookie; # handled by rr module # ----------------- setting module-specific parameters --------------- # specify the path to you database here modparam("acc_db|auth_db|avp_db|domain|gflags|usrloc|uri_db", "db_url", "mysql://ser:heslo@127.0.0.1/ser") # -- usrloc params -- # as we use the database anyway we will use it for usrloc as well modparam("usrloc", "db_mode", 1) # -- auth params -- modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "plain_password_column", "password") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # # limit the length of the AVP cookie to only necessary ones modparam("rr", "cookie_filter", "(account)") # # you probably do not want that someone can simply read and change # the AVP cookie in your Routes, thus should really change this # secret value below modparam("rr", "cookie_secret", "MyRRAVPcookiesecret") # -- gflags params -- # load the global AVPs modparam("gflags", "load_global_attrs", 1) # -- domain params -- # load the domain AVPs modparam("domain", "load_domain_attrs", 1) # -- ctl params -- # by default ctl listens on unixs:/tmp/ser_ctl if no other address is # specified in modparams; this is also the default for sercmd modparam("ctl", "binrpc", "unixs:/tmp/ser_ctl") # listen on the "standard" fifo for backward compatibility modparam("ctl", "fifo", "fifo:/tmp/ser_fifo") # listen on tcp, localhost modparam("ctl", "binrpc", "tcp:127.0.0.1:2046") # -- acc_db params -- # failed transactions (=negative responses) should be logged to modparam("acc_db", "failed_transactions", 1) # comment the next line if you dont want to have accounting to DB modparam("acc_db", "log_flag", "FLAG_ACC") # -- tm params -- # uncomment the following line if you want to avoid that each new reply # restarts the resend timer (see INBOUND route below) #modparam("tm", "restart_fr_on_each_reply", "0") #!ifdef WITH_TLS # -- tls params -- modparam("tls", "verify_certificate", 0) #!ifdef LOCAL_TEST_RUN modparam("tls", "certificate", "./modules/tls/sip-router-selfsigned.pem") modparam("tls", "private_key", "./modules/tls/sip-router-selfsigned.key") #separate TLS config file #modparam("tls", "config", "./modules/tls/tls.cfg") #!else modparam("tls", "certificate", "ser-selfsigned.pem") modparam("tls", "private_key", "ser-selfsigned.key") #separate TLS config file #modparam("tls", "config", "tls.cfg") #!endif # -- xmlrpc params -- # using a sub-route from the module is a lot safer then relying on the # request method to distinguish HTTP from SIP modparam("xmlrpc", "route", "RPC"); # ------------------------- request routing logic ------------------- # main routing logic route{ # if you have a PSTN gateway just un-comment the follwoing line and # specify the IP address of it to route calls to it #$gw_ip = "1.2.3.4" # first do some initial sanity checks route(INIT); # bypass the rest of the script for CANCELs if possible route(CATCH_CANCEL); # check if the request is routed via Route header or # needs a Record-Route header route(RR); # check if the request belongs to our proxy route(DOMAIN); # handle REGISTER requests route(REGISTRAR); # from here on we want to know you is calling route(AUTHENTICATION); # check if we should be outbound proxy for a local user route(OUTBOUND); # check if the request is for a local user route(INBOUND); # here you could for example try to do an ENUM lookup before # the call gets routed to the PSTN #route(ENUM); # lets see if someone wants to call a PSTN number route(PSTN); # nothing matched, reject it finally sl_reply("404", "No route matched"); } route[FORWARD] { # here you could decide wether this call needs a RTP relay or not # if this is called from the failure route we need to open a new branch if (isflagset(FLAG_FAILUREROUTE)) { append_branch(); } # if this is an initial INVITE (without a To-tag) we might try another # (forwarding or voicemail) target after receiving an error if (method=="INVITE" && strempty(@to.tag)) { t_on_failure("FAILURE_ROUTE"); } # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); } drop; } route[INIT] { # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_reply("483","Too Many Hops"); drop; } if (msg:len >= 4096 ) { sl_reply("513", "Message too big"); drop; } # you could add some NAT detection here for example # or you cuold call here some of the check from the sanity module # lets account all initial INVITEs # further in-dialog requests are accounted by a RR cookie (see below) if (method=="INVITE" && strempty(@to.tag)) { setflag(FLAG_ACC); } } route[RPC] { # allow XMLRPC from localhost if ((method=="POST" || method=="GET") && (src_ip==127.0.0.1 #!ifdef XMLRPC_ALLOW_NET1 || src_ip == XMLRPC_ALLOW_NET1 #!endif #!ifdef XMLRPC_ALLOW_NET2 || src_ip == XMLRPC_ALLOW_NET2 #!endif #!ifdef XMLRPC_ALLOW_NET3 || src_ip == XMLRPC_ALLOW_NET3 #!endif )) { if (msg:len >= 8192) { sl_reply("513", "Request to big"); drop; } # close connection only for xmlrpclib user agents (there is a bug in # xmlrpclib: it waits for EOF before interpreting the response). if (search("^User-Agent:.*xmlrpclib")) set_reply_close(); set_reply_no_connect(); # optional # lets see if a module wants to answer this dispatch_rpc(); drop; } } route[RR] { # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); # if the Route contained the accounting AVP cookie we # set the accounting flag for the acc_db module. # this is more for demonstration purpose as this could # also be solved without RR cookies. # Note: this means all in-dialog request will show up in the # accounting tables, so prepare your accounting software for this ;-) if ($account == "yes") { setflag(FLAG_ACC); } # for broken devices which overwrite their Route's with each # (not present) RR from within dialog requests it is better # to repeat the RRing # and if we call rr after loose_route the AVP cookies are restored # automatically :) record_route(); route(FORWARD); } else if (!method=="REGISTER") { # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol # if the inital INVITE got the ACC flag store this in # an RR AVP cookie. this is more for demonstration purpose if (isflagset(FLAG_ACC)) { $account = "yes"; setavpflag($account, "dialog_cookie"); } record_route(); } } route[DOMAIN] { # check if the caller is from a local domain lookup_domain("$fd", "@from.uri.host"); # check if the callee is at a local domain lookup_domain("$td", "@ruri.host"); # we dont know the domain of the caller and also not # the domain of the callee -> somone uses our proxy as # a relay if (strempty($t.did) && strempty($f.did)) { sl_reply("403", "Relaying Forbidden"); drop; } } route[REGISTRAR] { # if the request is a REGISTER lets take care of it if (method=="REGISTER") { # check if the REGISTER if for one of our local domains if (strempty($t.did)) { sl_reply("403", "Register forwarding forbidden"); drop; } # we want only authenticated users to be registered if (!www_authenticate("$fd.digest_realm", "credentials")) { if ($? == -2) { sl_reply("500", "Internal Server Error"); } else if ($? == -3) { sl_reply("400", "Bad Request"); } else { if ($digest_challenge != "") { append_to_reply("%$digest_challenge"); } sl_reply("401", "Unauthorized"); } drop; } # check if the authenticated user is the same as the target user if (!lookup_user("$tu.uid", "@to.uri")) { sl_reply("404", "Unknown user in To"); drop; } if ($f.uid != $t.uid) { sl_reply("403", "Authentication and To-Header mismatch"); drop; } # check if the authenticated user is the same as the request originator # you may uncomment it if you care, what uri is in From header #if (!lookup_user("$fu.uid", "@from.uri")) { # sl_reply("404", "Unknown user in From"); # drop; #} #if ($fu.uid != $tu.uid) { # sl_reply("403", "Authentication and From-Header mismatch"); # drop; #} # everything is fine so lets store the binding if (!save_contacts("location")) { sl_reply("400", "Invalid REGISTER Request"); drop; } drop; } } route[AUTHENTICATION] { if (method=="CANCEL" || method=="ACK") { # you are not allowed to challenge these methods break; } # requests from non-local to local domains should be permitted # remove this if you want a walled garden if (strempty($f.did)) { break; } # as gateways are usually not able to authenticate for their # requests you will have trust them base on some other information # like the source IP address. WARNING: if at all this is only safe # in a local network!!! #if (src_ip==a.b.c.d) { # break; #} if (!proxy_authenticate("$fd.digest_realm", "credentials")) { if ($? == -2) { sl_reply("500", "Internal Server Error"); } else if ($? == -3) { sl_reply("400", "Bad Request"); } else { if ($digest_challenge != "") { append_to_reply("%$digest_challenge"); } sl_reply("407", "Proxy Authentication Required"); } drop; } # check if the UID from the authentication meets the From header $authuid = $uid; if (!lookup_user("$fu.uid", "@from.uri")) { del_attr("$uid"); } if ($fu.uid != $fr.authuid) { sl_reply("403", "Fake Identity"); drop; } # load the user AVPs (preferences) of the caller, e.g. for RPID header load_attrs("$fu", "$f.uid"); } route[OUTBOUND] { # if a local user calls to a foreign domain we play outbound proxy for him # comment this out if you want a walled garden if ($f.did != "" && $t.did == "") { append_hf("P-hint: outbound\r\n"); route(FORWARD); } } route[INBOUND] { # lets see if know the callee if (lookup_user("$tu.uid", "@ruri")) { # load the preferences of the callee to have his timeout values loaded load_attrs("$tu", "$t.uid"); # if you want to know if the callee username was an alias # check it like this #if (strempty($tu.uri_canonical)) { # if the alias URI has different AVPs/preferences # you can load them into the URI track like this #load_attrs("$tr", "@ruri"); #} # check for call forwarding of the callee # Note: the forwarding target has to be full routable URI # in this example if ($tu.fwd_always_target != "") { attr2uri("$tu.fwd_always_target"); route(FORWARD); } # native SIP destinations are handled using our USRLOC DB if (lookup_contacts("location")) { append_hf("P-hint: usrloc applied\r\n"); # we set the TM module timers according to the prefences # of the callee (avoid too long ringing of his phones) # Note1: timer values have to be in ms now! # Note2: this makes even more sense if you switch to a voicemail # from the FAILURE_ROUTE below if ($t.fr_inv_timer != 0) { if ($t.fr_timer != 0) { t_set_fr("$t.fr_inv_timer", "$t.fr_timer"); } else { t_set_fr("$t.fr_inv_timer"); } } route(FORWARD); } else { sl_reply("480", "User temporarily not available"); drop; } } } route[PSTN] { # Only if the AVP 'gw_ip' is set and the request URI contains # only a number we consider sending this to the PSTN GW. # Only users from a local domain are permitted to make calls. # Additionally you might want to check the acl AVP to verify # that the user is allowed to make such expensives calls. if ($f.did != "" && $gw_ip != "" && uri=~"sips?:\+?[0-9]{3,18}@.*") { # probably you need to convert the number in the request # URI according to the requirements of your gateway here # if an AVP 'asserted_id' is set we insert an RPID header if ($asserted_id != "") { xlset_attr("$rpidheader", ";screen=yes"); replace_attr_hf("Remote-Party-ID", "$rpidheader"); } # just replace the domain part of the RURI with the # value from the AVP and send it out attr2uri("$gw_ip", "domain"); route(FORWARD); } } route[CATCH_CANCEL] { # check whether there is a corresponding INVITE to the CANCEL, # and bypass the rest of the script if possible if (method == CANCEL) { if (!t_relay_cancel()) { # implicit drop if the INVITE was found # INVITE was found but some error occurred sl_reply("500", "Internal Server Error"); drop; } # bad luck, no corresponding INVITE was found, # we have to continue with the script } } failure_route[FAILURE_ROUTE] { # mark for the other routes that we are operating from here on from a # failure route setflag(FLAG_FAILUREROUTE); if (t_check_status("486|600")) { # if we received a busy and a busy target is set, forward it there # Note: again the forwarding target has to be a routeable URI if ($tu.fwd_busy_target != "") { attr2uri("$tu.fwd_busy_target"); route(FORWARD); } # alternatively you could forward the request to SEMS/voicemail here } else if (t_check_status("408|480")) { # if we received no answer and the noanswer target is set, # forward it there # Note: again the target has to be a routeable URI if ($tu.fwd_noanswer_target != "") { attr2uri("$tu.fwd_noanswer_target"); route(FORWARD); } # alternatively you could forward the request to SEMS/voicemail here } } kamailio-4.0.4/etc/pa.cfg0000644000000000000000000000166012223032460013634 0ustar rootroot# # $Id$ # # simple quick-start config script # # ----------- global configuration parameters ------------------------ debug=4 # debug level (cmd line: -dddddddddd) fork=no log_stderror=yes # (cmd line: -E) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=4 fifo="/tmp/ser_fifo" listen=147.32.119.187 loadmodule "./modules/sl/sl.so" loadmodule "./modules/tm/tm.so" loadmodule "./modules/usrloc/usrloc.so" loadmodule "./modules/registrar/registrar.so" loadmodule "./modules/pa/pa.so" modparam("usrloc", "db_mode", 0) modparam("usrloc", "use_domain", 1) modparam("registrar", "use_domain", 1) route { if (uri==myself) { if (method=="SUBSCRIBE") { if (t_newtran()) { subscribe("registrar"); break; }; }; if (method=="REGISTER") { save("location"); break; }; lookup("location"); }; if (!t_relay()) { sl_reply_error(); }; } kamailio-4.0.4/etc/serresponse0000755000000000000000000001071112223032460015046 0ustar rootroot#!/bin/sh # # $Id$ # # PLEASE configure before use !!! # # This script uses sipsak (http://sipsak.berlios.de) to test if a # SIP server is still responding to requests and will send messages # to the developers. It is configured for the iptel.org enviroment. # PLEASE adapt it to your local enviroment. # NOTIFY=sr@iptel.org SIPSAK=/home/srouter/sipsak/sipsak SIPURI=sip:sipsak@iptel.org LOCKDIR=/var/lock LOCKFILE=serresponse LOCK_TIMEOUT=240 TMP=/tmp/serresponse.$$ MAILCOMMAND=/bin/mail HOSTN=`hostname` RUNHOST=iptel.org SMSSEND=/home/sms/smstools/bin/putsms SMSDEVICE=/dev/ttyS0 SMSNUMBERS="" ############################ LOCKF=$LOCKDIR/$LOCKFILE TMP2=$TMP.dns TMP3=$TMP.ips SIPSAKCMD="$SIPSAK -v -s $SIPURI" SIPSAKNCMD="$SIPSAK -v -n -s $SIPURI" SMSCMD="$SMSSEND -d$SMSDEVICE -b9600" if [ -e $LOCKF ] ; then find $LOCKDIR -name $LOCKFILE -amin +$LOCK_TIMEOUT -exec rm {} ';' if [ ! -e $LOCKF ] ; then echo "This is a reminder !!!" > $TMP echo "The lockfile $LOCKF" >> $TMP echo "was just removed because ist was older then $LOCK_TIMEOUT minutes." >> $TMP echo "But if you receive this mail the cause of this error still exists or respawned." >> $TMP SERR_SUBJECT="serresponse reminder" fi fi if [ ! -e $LOCKF ] ; then if [ ! -x $SIPSAK ] ; then echo "serresponse did not find the required sipsak executable $SIPSAK" >> $TMP SERR_SUBJECT="serresponse config failure" elif [ ! -x $SMSSEND ]; then echo "serresponse did not find the required SMS send executable $SMSSEND" >> $TMP SERR_SUBJECT="serresponse config failure" else date >> $TMP2 echo " $SIPSAKCMD" >> $TMP2 echo "produced this output:" >> $TMP2 $SIPSAKCMD >> $TMP2 2>&1 if [ $? -eq 3 ] ; then grep -i "Connection refused" $TMP2 if [ $? -eq 0 ] ; then sleep 30 fi date >> $TMP3 echo " $SIPSAKNCMD" >> $TMP3 echo "produced this output:" >> $TMP3 $SIPSAKNCMD >> $TMP3 2>&1 if [ $? -le 1 ] ; then echo "ser did not responsed (fast enough) on the sipsak requests with fqdn in Via" >> $TMP echo "but the test with IPs in Via succeeded." >> $TMP echo "" >> $TMP echo "Sending this alert is stopped for $LOCK_TIMEOUT minutes." >>$TMP echo "If you want to re-enable alerts sooner, please remove the lock file" >> $TMP echo "$LOCKF @ $HOSTN" >> $TMP echo "(you presumably need to be root to do this)" >> $TMP echo "" >> $TMP echo "Command output of sipsak with fqdn in Via follows:" >> $TMP cat $TMP2 >> $TMP SERR_SUBJECT="serresponse delayed" else echo "ser did not responsed (fast enough) on requests with fqdn in Via" >> $TMP echo "but also requests with IPs in Via failed." >> $TMP echo "" >> $TMP echo "Sending this alert is stopped for $LOCK_TIMEOUT minutes." >>$TMP echo "If you want to re-enable alerts sooner, please remove the lock file" >> $TMP echo "$LOCKF @ $HOSTN" >> $TMP echo "(you presumably need to be root to do this)" >> $TMP echo "" >> $TMP echo "First command output with fqdn in Via:" >> $TMP cat $TMP2 >> $TMP echo "" >> $TMP echo "Second command output with IPs in Via:" >> $TMP cat $TMP3 >> $TMP SERR_SUBJECT="serresponse failed" fi rm -f $TMP3 rm -f $TMP2 elif [ $? -eq 2 ] ; then echo "The ser response test failed due to a local error on" >> $TMP echo "host $HOSTN ." >> $TMP echo "" >> $TMP echo "Sending this alert is stopped for $LOCK_TIMEOUT minutes." >>$TMP echo "If you want to re-enable alerts sooner, please remove the lock file" >> $TMP echo "$LOCKF @ $HOSTN" >> $TMP echo "(you presumably need to be root to do this)" >> $TMP echo "" >> $TMP echo "Command output of sipsak with fqdn follows:" >> $TMP cat $TMP2 >> $TMP rm -f $TMP2 SERR_SUBJECT="serresponse local failure" else rm -f $TMP2 rm -f $TMP fi fi if [ -e $TMP ] ; then if [ $HOSTN = $RUNHOST ] ; then $MAILCOMMAND -s "$SERR_SUBJECT" $NOTIFY < $TMP rm -f $TMP touch $LOCKF for i in $SMSNUMBERS; do $SMSCMD $i "serresponse failed. please check your emails for details" done else echo "unconfigured serresponse executed on ${HOSTN}." > $TMP echo "Warning: This script if configured for the iptel.org enviroment." echo " Please configure it to your local settings first." echo echo "If you do not press CTRL-C within 2 seconds an informational message" echo "with your hostname will be send to the ser developers." sleep 2 $MAILCOMMAND -s "serresponse executed on ${HOSTN}" $NOTIFY < $TMP rm -f $TMP fi fi fi kamailio-4.0.4/etc/reasons-cs_CZ.iso-8859-2.txt0000644000000000000000000000426212223032460017331 0ustar rootroot# # $Id$ # # Czech translation of reason phrases # # Format: # ::: # 100::cs_CZ.iso-8859-2:Navazuji spojeni 180::cs_CZ.iso-8859-2:Vyzvani 181::cs_CZ.iso-8859-2:Hovor byl presmerovan 182::cs_CZ.iso-8859-2:Jste v poradi 183::cs_CZ.iso-8859-2:Probiha navazovani spojeni 200::cs_CZ.iso-8859-2:Uspesne zpracovano 202::cs_CZ.iso-8859-2:Bude vyrizeno pozdeji 300::cs_CZ.iso-8859-2:Vice moznosti 301::cs_CZ.iso-8859-2:Trvale presmerovano 302::cs_CZ.iso-8859-2:Docasne presmerovano 305::cs_CZ.iso-8859-2:Pouzijte jiny server 380::cs_CZ.iso-8859-2:Alternativni sluzba 400::cs_CZ.iso-8859-2:Chyba protokolu 402::cs_CZ.iso-8859-2:Placena sluzba 403::cs_CZ.iso-8859-2:Zakazano 404::cs_CZ.iso-8859-2:Nenalezeno 405::cs_CZ.iso-8859-2:Nepovoleny prikaz 406::cs_CZ.iso-8859-2:Neni povoleno 407::cs_CS.iso-8859-2:Server vyzaduje autentizaci 408::cs_CZ.iso-8859-2:Vyprsel casovy limit 410::cs_CZ.iso-8859-2:Nenalezeno 413::cs_CZ.iso-8859-2:Dotaz je prilis dlouhy 414::cs_CZ.iso-8859-2:Request-URI je prilis dlouhe 415::cs_CZ.iso-8859-2:Nepodporovany typ medii 416::cs_CZ.iso-8859-2:Nepodporovany typ identifikatoru 420::cs_CZ.iso-8859-2:Spatne cislo linky 421::cs_CZ.iso-8859-2:Zadejte cislo linky 423::cs_CZ.iso-8859-2:Prilis kratky interval 480::cs_CZ.iso-8859-2:Docasne nedostupne 481::cs_CZ.iso-8859-2:Spojeni nenalezeno 482::cs_CZ.iso-8859-2:Zprava se zacyklila (chyba serveru) 483::cs_CZ.iso-8859-2:Prilis mnoho mezikroku 484::cs_CZ.iso-8859-2:Neuplna adresa 485::cs_CZ.iso-8859-2:Neni jednoznacne 486::cs_CZ.iso-8859-2:Volany je zaneprazdnen 487::cs_CZ.iso-8859-2:Prikaz predcasne ukoncen 488::cs_CZ.iso-8859-2:Nebylo akceptovano 491::cs_CZ.iso-8859-2:Cekam na odpoved 493::cs_CZ.iso-8859-2:Nelze dekodovat 500::cs_CZ.iso-8859-2:Interni chyba serveru 501::cs_CZ.iso-8859-2:Neni implementovano 502::cs_CZ.iso-8859-2:Spatna brana 503::cs_CZ.iso-8859-2:Sluzba neni dostupna 504::cs_CZ.iso-8859-2:Casovy limit serveru vyprsel 505::cs_CZ.iso-8859-2:Nepodporovana verze protokolu 513::cs_CZ.iso-8859-2:Zprava je prilis dlouha 600::cs_CZ.iso-8859-2:Uzivatel je zaneprazdnen 603::cs_CZ.iso-8859-2:Odmitnuto 604::cs_CZ.iso-8859-2:Neexistujici uzivatel nebo sluzba 606::cs_CZ.iso-8859-2:Nelze akceptovat kamailio-4.0.4/etc/imgwd0000755000000000000000000001131212223032460013603 0ustar rootroot#!/bin/sh # # 3w-xxxx: Starts the sip2jab process # # Version: @(#) /etc/rc.d/init.d/3w-xxxx # # chkconfig: 2345 85 15 # description: controls execution of SIP2Jabber gateway # JABBER JDIR="/home/sms/jabber-1.4.2" JABBERD="${JDIR}/jabberd/jabberd" JICQEXT="${JDIR}/icqv7ext.sh" CONF="${JDIR}/conf/single.xml" JUSR="sms" JPID="${JDIR}/spool/jabbers.pid" JOUT="${JDIR}/spool/jabberd.log" # SER BIN=imgw HM=/home/sms/srouter BINDIR=$HM/bin ETC=$HM/sip_router/etc/imgw.cfg USR="sms" GRP="sms" MYDIR=$HM/core CORE=$MYDIR/core RETVAL=0 ############ ==== ############# # Test if Jabber exists test -f ${JABBERD} -a -f ${CONF} || exit 0 # Source function library. . /etc/rc.d/init.d/functions # Get config. . /etc/sysconfig/network # Check that networking is up. [ "${NETWORKING}" = "no" ] && exit 0 # Current user CRT_USER=`whoami` ### Stop Jabber server stop_jab() { echo "Stopping ICQ transport - external component ..." killproc ${JICQEXT} # echo echo "Stopping Jabber server ..." killproc ${JABBERD} RETVAL=$? # echo # kill `cat ${JPID}` # echo " OK." } ### Start Jabber server start_jab() { if [ -f ${JPID} ] then echo "Cleaning Jabber from a previous dirty crash ..." stop_jab sleep 1 rm -f ${JPID} fi if [ `id -u` = 0 ] then #### Run by root echo "Starting Jabber server ..." su - ${JUSR} -c "cd ${JDIR}; ${JABBERD} -B -c ${CONF} > /dev/null 2>&1" > /dev/null 2>&1 # echo sleep 2 echo "Starting ICQ transport - external component ..." su - ${JUSR} -c "cd ${JDIR}; ${JICQEXT} > /dev/null 2>&1 &" > /dev/null 2>&1 RETVAL=$? # echo else ### Run by other users echo "Starting Jabber server ..." cd ${JDIR} ${JABBERD} -B -c ${CONF} > /dev/null 2>&1 # echo sleep 2 echo "Starting ICQ transport - external component ..." ${JICQEXT} > /dev/null 2>&1 & RETVAL=$? # echo fi sleep 1 ### Checking if processes are started if [ `ps auxw | grep ${JABBERD} | head --lines=1 | awk '{print $11}'` = ${JABBERD} ] then echo "Jabber server: [[ STARTED ]]" else echo "Jabber server: [[ NOT STARTED ]]" fi if [ `ps auxw | grep ${JICQEXT} | head --lines=1 | awk '{print $11}'` != "grep" ] then echo "ICQ transport: [[ STARTED ]]" else echo "ICQ transport: [[ NOT STARTED ]]" fi } ### Stop SER stop_ser() { echo -n "Stopping SIP router ..." killproc ${BIN} RETVAL=$? echo # killall ser # echo " [ OK ]" } ### Start SER start_ser() { echo "Starting SIP router ..." if [ `id -u` = 0 ] then #### Run by root echo "- ${USR} -c cd ${MYDIR}; ${BINDIR}/${BIN} -f ${ETC} -w ${MYDIR} > /dev/null 2>&1" su - ${USR} -c "cd ${MYDIR}; ${BINDIR}/${BIN} -f ${ETC} -w ${MYDIR}" > /dev/null 2>&1 RETVAL=$? else #### Run by other users #cd $MYDIR # core timestamping moved to sercheck; -jiri # if [ -f $CORE ] ; then # chmod a+r $CORE # DATE=`date "+%Y-%m-%d--%H-%M"` # mv $CORE $CORE.$DATE # ( cd ../sip_router; tar cf - . ) | gzip > ser.$DATE.tgz # fi cd ${MYDIR} ${BINDIR}/${BIN} -f ${ETC} -w ${MYDIR} RETVAL=$? # man setuid: If uid is different from the old effective uid, # the process will be forbidden from eaving core dumps. # -> don't set uid, we want to collect core dumps # -u $USR -g $GRP fi sleep 1 ### Checking if processes are started if [ `ps auxw | grep ${BINDIR}/${BIN} | head --lines=1 | awk '{print $11}'` = ${BINDIR}/${BIN} ] then echo "SIP router: [[ STARTED ]]" else echo "SIP router: [[ NOT STARTED ]]" fi } ### Check check_run() { ### Checking if Jabber server is running if [ `ps auxw | grep ${JABBERD} | head --lines=1 | awk '{print $11}'` = ${JABBERD} ] then echo "Jabber server: [[ RUNNING ]]" else echo "Jabber server: [[ NOT RUNNING ]]" fi ### Checking if ICQ transport is running if [ `ps auxw | grep ${JICQEXT} | head --lines=1 | awk '{print $11}'` != "grep" ] then echo "ICQ transport: [[ RUNNING ]]" else echo "ICQ transport: [[ NOT RUNNING ]]" fi ### Checking if SIP router is running if [ `ps auxw | grep ${BINDIR}/${BIN} | head --lines=1 | awk '{print $11}'` = ${BINDIR}/${BIN} ] then echo "SIP router: [[ RUNNING ]]" else echo "SIP router: [[ NOT RUNNING ]]" fi } ### Cleaning make_clean() { sleep 2 echo "Cleaning Jabber core if generated on killing ..." rm -f ${JDIR}/core.* } case "$1" in start) start_jab start_ser ;; stop) stop_ser stop_jab sleep 2 make_clean ;; restart) echo "Restarting SIP2Jabber ..." stop_ser stop_jab sleep 2 echo start_jab start_ser make_clean ;; check) check_run ;; *) N=$0 echo "Usage: $N {start|stop|restart|check}" >&2 exit 1 ;; esac echo exit $RETVAL kamailio-4.0.4/etc/sip-router-basic.cfg0000644000000000000000000001126112223032460016422 0ustar rootroot# # $Id$ # # This a very basic config file w/ aliases and a named route but # w/o authentication, accounting, database, multi-domain support etc. # Please refer to sip-router.cfg for a more complete example # # Direct your questions about this file to: . # # For more information about the various parameters, functions and statements # try http://sip-router.org/wiki/ . # #------------ Features ----------------------------------------------- # Several extra features can be enabled by adding #!define WITH_ # statements to the config file, or by starting sr/ser with -A WITH_. # E.g.: ser -f /etc/ser/sip-router-basic.cfg -A WITH_TLS # run in debug mode: ##!define WITH_DEBUG # enable tls support: ##!define WITH_TLS # started from compile directory (not installed) ##!define LOCAL_TEST_RUN # ----------- global configuration parameters ------------------------ #!ifdef WITH_DEBUG debug=5 log_stderror=yes fork=no #!else debug=2 # debug level (cmd line: -dddddddddd) #!endif #memdbg=10 # memory debug message level #memlog=10 # memory statistics log level #log_facility=LOG_LOCAL0 # sets the facility used for logging (see syslog(3)) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) #port=5060 #children=4 #user=sip-router #group=sip-router #disable_core=yes #disables core dumping #open_fd_limit=1024 # sets the open file descriptors limit #mhomed=yes # usefull for multihomed hosts, small performance penalty #disable_tcp=yes #tcp_accept_aliases=yes # accepts the tcp alias via option (see NEWS) sip_warning=yes #!ifdef WITH_TLS enable_tls=yes #!endif # # ------------------ module loading ---------------------------------- #!ifdef LOCAL_TEST_RUN loadpath "modules:modules_s" #!else loadpath "/usr/lib/sip-router/modules:/usr/lib/sip-router/modules_s" #!endif loadmodule "tm" loadmodule "sl" loadmodule "rr" loadmodule "textops" loadmodule "maxfwd" loadmodule "usrloc" loadmodule "registrar" loadmodule "ctl" loadmodule "cfg_rpc" #!ifdef WITH_TLS loadmodule "tls" #!endif # ----------------- setting module-specific parameters --------------- # -- usrloc params -- modparam("usrloc", "db_mode", 0) # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # ctl params # by default ctl listens on unixs:/tmp/sip-router_ctl if no other address is # specified in modparams; this is also the default for sercmd modparam("ctl", "binrpc", "unixs:/tmp/ser_ctl") # listen on the "standard" fifo for backward compatibility modparam("ctl", "fifo", "fifo:/tmp/ser_fifo") # listen on tcp, localhost modparam("ctl", "binrpc", "tcp:127.0.0.1:2046") #!ifdef WITH_TLS modparam("tls", "verify_certificate", 0) #!ifdef LOCAL_TEST_RUN modparam("tls", "certificate", "./modules/tls/sip-router-selfsigned.pem") modparam("tls", "private_key", "./modules/tls/sip-router-selfsigned.key") #separate TLS config file #modparam("tls", "config", "./modules/tls/tls.cfg") #!else modparam("tls", "certificate", "ser-selfsigned.pem") modparam("tls", "private_key", "ser-selfsigned.key") #separate TLS config file #modparam("tls", "config", "tls.cfg") #!endif #!endif #!endif # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_reply("483","Too Many Hops"); break; } if (msg:len >= 4096 ) { sl_reply("513", "Message too big"); break; } # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!method=="REGISTER") record_route(); # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(FORWARD); break; } if (!uri==myself) { # mark routing logic in request append_hf("P-hint: outbound\r\n"); route(FORWARD); break; } # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (method=="REGISTER") { save_contacts("location"); break; } # native SIP destinations are handled using our USRLOC DB if (!lookup_contacts("location")) { sl_reply("404", "Not Found"); break; } append_hf("P-hint: usrloc applied\r\n"); } route(FORWARD); } route[FORWARD] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); } } kamailio-4.0.4/etc/rules.m40000644000000000000000000000174712223032460014155 0ustar rootroot# # $Id$ # # (c) 2003 iptel.org # # Rules to process sip-router.cfg templates # #id generator, usage: gen_id(`name')) define(`gen_id', `ifdef(`$1',,`define(`$1', 0)')define(`$1',eval($1 + 1))') #declare enum constants, usage: declare(route, R_MAIN, R_NAT, ...) declare(route, R_REGISTER) define(`declare', `ifelse($#, 1, , `gen_id(`$1'_cnt) define(`$2', indir(`$1'_cnt)) ifelse($#, 2, ,`declare(`$1', shift(shift($@)))')')') define(`FROM_GW', `(_FROM_GW(1))') define(`_FROM_GW', `ifdef(`GW_IP_$1', `_FROM_GW(incr($1))(src_ip == GW_IP_$1)ifelse($1, 1, , ` || ')')') define(`TO_GW', `(@(_TO_GW(1))([;:].*)*)') define(`_TO_GW', `ifdef(`GW_IP_$1', `_TO_GW(incr($1))(patsubst(GW_IP_$1, `\.', `\\.'))ifelse($1, 1, , `|')')') define(`DIGEST_REALM', `SER_HOSTNAME') define(`SER_IP_REGEX', `patsubst(SER_IP, `\.', `\\.')') define(`SER_HOSTNAME_REGEX', `patsubst(SER_HOSTNAME, `\.', `\\.')') define(`SER_HOST_REGEX', `((SER_IP_REGEX)|(SER_HOSTNAME_REGEX))') define(`FROM_MYSELF', `(src_ip == SER_IP)') kamailio-4.0.4/etc/dictionary.sip-router0000644000000000000000000001316012223032460016751 0ustar rootroot# # $Id$ # # RADIUS Attributes used by SER # # Schulzrinne indicates attributes according to # draft-schulzrinne-sipping-radius-accounting-00 # # Sterman indicates attributes according to # draft-sterman-aaa-sip-00 # # SER indicates implementation specific attributes of SER # # Check out http://www.iana.org/assignments/radius-types # for up-to-date list of standard RADIUS attributes # and values # # # NOTE: All standard (IANA registered) attributes are # commented out except those that are missing in # the default dictionary of the radiusclient-ng # library. # #### Attributes ### #ATTRIBUTE User-Name 1 string # RFC2865, acc_radius, auth_radius, avp_radius, group_radius, uri_radius #ATTRIBUTE NAS-IP-Address 4 ipaddr # RFC2865, acc_radius, auth_radius #ATTRIBUTE NAS-Port 5 integer # RFC2865, acc_radius, auth_radius #ATTRIBUTE Service-Type 6 integer # RFC2865, acc_radius, auth_radius, avp_radius, group_radius, uri_radius #ATTRIBUTE Called-Station-Id 30 string # RFC2865, acc_radius #ATTRIBUTE Calling-Station-Id 31 string # RFC2865, acc_radius #ATTRIBUTE Acct-Status-Type 40 integer # RFC2866, acc_radius #ATTRIBUTE Acct-Session-Id 44 string # RFC2866, acc_radius #ATTRIBUTE Acct-Session-Time 46 integer # RFC2866, acc_radius ATTRIBUTE Sip-Method 101 string # Schulzrinne, acc_radius ATTRIBUTE Sip-Response-Code 102 integer # Schulzrinne, acc_radius ATTRIBUTE Sip-Cseq 103 integer # Schulzrinne, acc_radius ATTRIBUTE Sip-To-Tag 104 string # Schulzrinne, acc_radius ATTRIBUTE Sip-From-Tag 105 string # Schulzrinne, acc_radius ATTRIBUTE Sip-Branch-Id 106 string # Schulzrinne, not used ATTRIBUTE Sip-Translated-Request-ID 107 string # Schulzrinne, acc_radius ATTRIBUTE Sip-Source-IP-Address 108 ipaddr # Schulzrinne, acc_radius ATTRIBUTE Sip-Source-Port 109 integer # Schulzrinne, acc_radius ATTRIBUTE Digest-Response 206 string # Sterman, auth_radius # Digest-Response attribute sub-types ATTRIBUTE Digest-Realm 1063 string # Sterman, auth_radius ATTRIBUTE Digest-Nonce 1064 string # Sterman, auth_radius ATTRIBUTE Digest-Method 1065 string # Sterman, auth_radius ATTRIBUTE Digest-URI 1066 string # Sterman, auth_radius ATTRIBUTE Digest-QOP 1067 string # Sterman, auth_radius ATTRIBUTE Digest-Algorithm 1068 string # Sterman, auth_radius ATTRIBUTE Digest-Body-Digest 1069 string # Sterman, auth_radius ATTRIBUTE Digest-CNonce 1070 string # Sterman, auth_radius ATTRIBUTE Digest-Nonce-Count 1071 string # Sterman, auth_radius ATTRIBUTE Digest-User-Name 1072 string # Sterman, auth_radius ### CISCO Vendor Specific Attributes ### #VENDOR Cisco 9 #ATTRIBUTE Cisco-AVPair 1 string Cisco # VSA, auth_radius ### iptel.org Vendor Specific Attributes ### VENDOR iptelorg 24960 ATTRIBUTE SER-Attr 1 string iptelorg # SER, acc_radius, avp_radius, auth_radius ATTRIBUTE SER-From 2 string iptelorg # SER, acc_radius ATTRIBUTE SER-Flags 3 integer iptelorg # SER, acc_radius ATTRIBUTE SER-Original-Request-ID 4 string iptelorg # SER, acc_radius ATTRIBUTE SER-To 5 string iptelorg # SER, acc_radius ATTRIBUTE SER-Digest-Username 6 string iptelorg # SER, acc_radius ATTRIBUTE SER-Digest-Realm 7 string iptelorg # SER, acc_radius ATTRIBUTE SER-Request-Timestamp 8 integer iptelorg # SER, acc_radius ATTRIBUTE SER-To-DID 9 string iptelorg # SER, acc_radius ATTRIBUTE SER-From-UID 10 string iptelorg # SER, acc_radius ATTRIBUTE SER-From-DID 11 string iptelorg # SER, acc_radius ATTRIBUTE SER-To-UID 12 string iptelorg # SER, acc_radius ATTRIBUTE SER-Response-Timestamp 13 integer iptelorg # SER, acc_radius ATTRIBUTE SER-Service-Type 14 integer iptelorg # SER, avp_radius ATTRIBUTE SER-DID 16 string iptelorg # SER, ? ATTRIBUTE SER-UID 17 string iptelorg # SER, ? ATTRIBUTE SER-Domain 18 string iptelorg # SER, ? ATTRIBUTE SER-Uri-User 19 string iptelorg # SER, auth_radius ATTRIBUTE SER-Uri-Scheme 20 string iptelorg # SER, avp_radius ### Acct-Status-Type Values ### #VALUE Acct-Status-Type Start 1 # RFC2866, acc_radius #VALUE Acct-Status-Type Stop 2 # RFC2866, acc_radius VALUE Acct-Status-Type Interim-Update 3 # RFC2866, acc_radius VALUE Acct-Status-Type Failed 15 # RFC2866, acc_radius ### Service-Type Values ### VALUE Service-Type Sip-Session 15 # Schulzrinne, acc_radius, auth_radius ### SER-Service-Type Values ### VALUE SER-Service-Type Get-URI-Attrs 1 # SER, avp_radius VALUE SER-Service-Type Get-User-Attrs 2 # SER, avp_radius VALUE SER-Service-Type Digest-Authentication 3 # SER, auth_radius VALUE SER-Service-Type Get-Domain-Attrs 4 # SER, ? VALUE SER-Service-Type Get-Global-Attrs 5 # SER, ? VALUE SER-Service-Type Lookup-Domain 6 # SER, ? kamailio-4.0.4/etc/presence/0000755000000000000000000000000012223032460014354 5ustar rootrootkamailio-4.0.4/etc/presence/full-no-failover.cfg0000644000000000000000000003015612223032460020223 0ustar rootrootdebug=3 # debug level (cmd line: -dddddddddd) #fork=yes #log_stderror=no # (cmd line: -E) #memlog=5 # memory debug log level #log_facility=LOG_LOCAL0 # sets the facility used for logging (see syslog(3)) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=2 alias="test-domain.com" #user=ser #group=ser #open_fd_limit=1024 # sets the open file descriptors limit mhomed=yes # usefull for multihomed hosts, small performance penalty #disable_tcp=yes #tcp_accept_aliases=yes # accepts the tcp alias via option (see NEWS) #tcp_poll_method="sigio_rt" # ------------------ module loading ---------------------------------- # Uncomment this if you want to use SQL database loadmodule "/usr/lib/ser/modules/sl.so" loadmodule "/usr/lib/ser/modules/avp.so" loadmodule "/usr/lib/ser/modules/avpops.so" loadmodule "/usr/lib/ser/modules/tm.so" loadmodule "/usr/lib/ser/modules/rr.so" loadmodule "/usr/lib/ser/modules/maxfwd.so" loadmodule "/usr/lib/ser/modules/usrloc.so" loadmodule "/usr/lib/ser/modules/registrar.so" loadmodule "/usr/lib/ser/modules/textops.so" loadmodule "/usr/lib/ser/modules/mysql.so" loadmodule "/usr/lib/ser/modules/dialog.so" loadmodule "/usr/lib/ser/modules/rls.so" loadmodule "/usr/lib/ser/modules/pa.so" loadmodule "/usr/lib/ser/modules/presence_b2b.so" loadmodule "/usr/lib/ser/modules/uri.so" loadmodule "/usr/lib/ser/modules/uri_db.so" loadmodule "/usr/lib/ser/modules/domain.so" loadmodule "/usr/lib/ser/modules/fifo.so" loadmodule "/usr/lib/ser/modules/xmlrpc.so" loadmodule "/usr/lib/ser/modules/xlog.so" #loadmodule "/usr/lib/ser/modules/unixsock.so" # Uncomment this if you want digest authentication # mysql.so must be loaded ! loadmodule "/usr/lib/ser/modules/auth.so" loadmodule "/usr/lib/ser/modules/auth_db.so" loadmodule "/usr/lib/ser/modules/msilo.so" # ----------------- setting module-specific parameters --------------- # modparam("msilo","registrar","sip:registrar@test-domain.com") modparam("msilo","use_contact",0) modparam("msilo","expire_time",7200) # -- usrloc params -- # -- auth params -- # Uncomment if you are using auth module # modparam("auth_db", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) # modparam("auth_db", "plain_password_column", "password") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) modparam("rls", "min_expiration", 200) modparam("rls", "max_expiration", 300) modparam("rls", "default_expiration", 300) modparam("rls", "auth", "none") modparam("rls", "xcap_root", "http://localhost/xcap") modparam("rls", "reduce_xcap_needs", 1) modparam("rls", "db_mode", 0) modparam("rls", "db_url", "mysql://ser:heslo@localhost:3306/ser") modparam("pa", "use_db", 0) # allow storing authorization requests for offline users into database modparam("pa", "use_offline_winfo", 1) # how often try to remove old stored authorization requests modparam("pa", "offline_winfo_timer", 600) # how long stored authorization requests live modparam("pa", "offline_winfo_expiration", 600) modparam("pa", "db_url", "mysql://ser:heslo@localhost:3306/ser") # mode of PA authorization: none, implicit or xcap modparam("pa", "auth", "xcap") modparam("pa", "auth_xcap_root", "http://localhost/xcap") # do not authorize watcherinfo subscriptions modparam("pa", "winfo_auth", "none") # use only published information if set to 0 modparam("pa", "use_callbacks", 1) # don't accept internal subscriptions from RLS, ... modparam("pa", "accept_internal_subscriptions", 0) # maximum value of Expires for subscriptions modparam("pa", "max_subscription_expiration", 600) # maximum value of Expires for publications modparam("pa", "max_publish_expiration", 120) # how often test if something changes and send NOTIFY modparam("pa", "timer_interval", 10) # route for generated SUBSCRIBE requests for presence modparam("presence_b2b", "presence_route", "") # waiting time from error to new attepmt about SUBSCRIBE modparam("presence_b2b", "on_error_retry_time", 60) # how long wait for NOTIFY with Subscription-Status=terminated after unsubscribe modparam("presence_b2b", "wait_for_term_notify", 33) # how long before expiration send renewal SUBSCRIBE request modparam("presence_b2b", "resubscribe_delta", 30) # minimal time to send renewal SUBSCRIBE request from receiving previous response modparam("presence_b2b", "min_resubscribe_time", 60) # default expiration timeout modparam("presence_b2b", "default_expiration", 3600) # process internal subscriptions to presence events modparam("presence_b2b", "handle_presence_subscriptions", 1) modparam("usrloc", "db_mode", 0) modparam("domain", "db_mode", 1) modparam("domain|uri_db|acc|auth_db|usrloc|msilo", "db_url", "mysql://ser:heslo@localhost:3306/ser") modparam("fifo", "fifo_file", "/tmp/ser_fifo") # ------------------------- request routing logic ------------------- # main routing logic route{ # XML RPC if (method == "POST" || method == "GET") { create_via(); dispatch_rpc(); break; } # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); break; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!method=="REGISTER") record_route(); # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); break; }; # if the request is for other domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (!lookup_domain("To")) { xlog("L_ERR", "Unknown domain to: %tu from: %fu\n"); route(1); break; } if (method=="SUBSCRIBE") { if (!t_newtran()) { sl_reply_error(); break; }; if (@to.tag=="") { # only for new subscriptions (with empty to tag) if (lookup_user("To")) { # existing user -> it is subscription to PA if (handle_subscription("registrar")) { if ((@msg.event=~"presence\.winfo")) { # new watcher info subscription # sends one watcher info NOTIFY message with all saved authorization requests xlog("L_ERR", "dumping stored winfo to %fu\n"); dump_stored_winfo("registrar", "presence"); } else { # new presence subscription if ((@msg.event=~"presence") && (%subscription_status=="pending")) { # if offline user and new pending subscription if (!target_online("registrar")) { #%subscription_status="waiting"; # store it as waiting subscription xlog("L_ERR", "storing 'pending' winfo to: %tu, from: %fu\n"); store_winfo("registrar"); } } } } break; } if ((@msg.supported=~"eventlist")) { # such user doesn't exist and Supported header field # -> probably RLS subscription if (lookup_domain("From")) { if (lookup_user("From")) { if (is_simple_rls_target("$uid-list")) { # log(1, "it is simple subscription!\n"); # takes From UID and makes XCAP query for user's # list named "default" if (!query_resource_list("default")) { t_reply("404", "No such user list"); break; } } } } if (!have_flat_list()) { # query_resource_list failed or was not called # do standard RLS query acording to To/AOR if (!query_rls_services()) { log(1, "XCAP query failed\n"); t_reply("404", "No such list URI"); break; } } # uncomment this if you want to authenticate first SUBSCRIBE request to resource list # if (!proxy_authenticate("test-domain.com", "credentials")) { # proxy_challenge( "test-domain.com", "0"); # break; # }; handle_rls_subscription("1"); } else { # not resource list subscription -> invalid user xlog("L_ERR", "subscription to invalid user %tu\n"); t_reply("404", "User not found"); } break; } else { # renewal subscriptions - try to handle it as RLS and if failed, handle it as PA subscription # FIXME: better will be test like existing_rls_subscription() # and existing_subscription("registrar") if (!handle_rls_subscription("0")) { handle_subscription("registrar"); } break; } }; # get user (common for all other messages than SUBSCRIBE) if (!lookup_user("To")) { # log(1, "Unknown user - message should be forwarded?"); # # break; append_hf("P-hint: unknown user\r\n"); route(1); break; } if (method=="PUBLISH") { if (!t_newtran()) { # log(1, "newtran error\n"); sl_reply_error(); break; }; handle_publish("registrar"); # deliver messages to online user # TODO: only if user goes from offline to online? if (target_online("registrar")) { # log(1, "Dumping stored messages\n"); # dump stored messages - route it through myself (otherwise routed via DNS!) if (m_dump("sip:127.0.0.1")) { xlog("L_ERR", "MSILO: offline messages for %fu dumped\n"); } } break; }; if (method=="NOTIFY") { if (!t_newtran()) { log(1, "newtran error\n"); sl_reply_error(); break; }; # handle notification sent in internal subscriptions (presence_b2b) if (!handle_notify()) { t_reply("481", "Unable to handle notification"); } break; }; if (method=="MESSAGE") { if (authorize_message("http://localhost/xcap")) { # use usrloc for delivery if (lookup("location")) { log(1, "Delivering MESSAGE using usrloc\n"); t_on_failure("1"); if (!t_relay()) { sl_reply_error(); } break; } else { # store messages for offline user xlog("L_ERR", "MSILO: storing MESSAGE for %tu\n"); if (!t_newtran()) { log(1, "newtran error\n"); sl_reply_error(); break; }; # store only text messages NOT isComposing... ! if (search("^(Content-Type|c):.*application/im-iscomposing\+xml.*")) { log(1, "it is only isComposing message - ignored\n"); t_reply("202", "Ignored"); break; } if (m_store("0", "sip:127.0.0.1")) { # log(1, "MSILO: offline message stored\n"); if (!t_reply("202", "Accepted")) { sl_reply_error(); }; } else { log(1, "MSILO: error storing offline message\n"); if (!t_reply("503", "Service Unavailable")) { sl_reply_error(); }; }; break; } break; } else { # log(1, "unauthorized message\n"); sl_reply("403", "Forbidden"); } break; } if (method=="REGISTER") { # uncomment this if you want to authenticate REGISTER request # if (!www_authenticate("test-domain.com", "credentials")) { # www_challenge( "test-domain.com", "0"); # break; # }; save("location"); # dump stored messages - route it through myself (otherwise routed via DNS!) if (m_dump("sip:127.0.0.1")) { xlog("L_ERR", "MSILO: offline messages for %fu dumped\n"); } break; }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); break; }; }; # append_hf("P-hint: usrloc applied\r\n"); route(1); } route[1] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; } failure_route[1] { # forwarding failed -- check if the request was a MESSAGE if (!method=="MESSAGE") { break; }; log(1, "MSILO: MESSAGE forward failed - storing it\n"); # we have changed the R-URI with the contact address, ignore it now if (m_store("0", "")) { t_reply("202", "Accepted"); } else { log(1, "MSILO: offline message NOT stored\n"); t_reply("503", "Service Unavailable"); }; } kamailio-4.0.4/etc/presence/no-db.cfg0000644000000000000000000001377112223032460016045 0ustar rootrootdebug=3 # debug level (cmd line: -dddddddddd) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=2 alias="test-domain.com" mhomed=yes # usefull for multihomed hosts, small performance penalty #tcp_accept_aliases=yes # accepts the tcp alias via option (see NEWS) #tcp_poll_method="sigio_rt" # ------------------ module loading ---------------------------------- # Uncomment this if you want to use SQL database loadmodule "/usr/lib/ser/modules/sl.so" loadmodule "/usr/lib/ser/modules/avp.so" loadmodule "/usr/lib/ser/modules/avpops.so" loadmodule "/usr/lib/ser/modules/tm.so" loadmodule "/usr/lib/ser/modules/rr.so" loadmodule "/usr/lib/ser/modules/maxfwd.so" loadmodule "/usr/lib/ser/modules/usrloc.so" loadmodule "/usr/lib/ser/modules/registrar.so" loadmodule "/usr/lib/ser/modules/textops.so" loadmodule "/usr/lib/ser/modules/dialog.so" loadmodule "/usr/lib/ser/modules/rls.so" loadmodule "/usr/lib/ser/modules/pa.so" loadmodule "/usr/lib/ser/modules/presence_b2b.so" loadmodule "/usr/lib/ser/modules/uri.so" loadmodule "/usr/lib/ser/modules/fifo.so" loadmodule "/usr/lib/ser/modules/xmlrpc.so" loadmodule "/usr/lib/ser/modules/xlog.so" # ----------------- setting module-specific parameters --------------- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) modparam("rls", "min_expiration", 120) modparam("rls", "max_expiration", 120) modparam("rls", "default_expiration", 120) modparam("rls", "auth", "none") modparam("rls", "xcap_root", "http://localhost/xcap") modparam("rls", "reduce_xcap_needs", 1) modparam("rls", "db_mode", 0) # modparam("rls", "db_url", "mysql://ser:heslo@127.0.0.1:3306/ser") modparam("pa", "use_db", 0) modparam("pa", "offline_winfo_timer", 600) modparam("pa", "offline_winfo_expiration", 600) # modparam("pa", "db_url", "mysql://ser:heslo@127.0.0.1:3306/ser") # mode of PA authorization: none, implicit or xcap modparam("pa", "auth", "xcap") modparam("pa", "auth_xcap_root", "http://localhost/xcap") modparam("pa", "winfo_auth", "none") modparam("pa", "use_callbacks", 1) modparam("pa", "accept_internal_subscriptions", 0) modparam("pa", "max_subscription_expiration", 120) modparam("pa", "timer_interval", 1) modparam("presence_b2b", "presence_route", "") # modparam("presence_b2b", "presence_route", "") modparam("presence_b2b", "on_error_retry_time", 60) modparam("presence_b2b", "wait_for_term_notify", 33) modparam("presence_b2b", "resubscribe_delta", 30) modparam("presence_b2b", "min_resubscribe_time", 60) modparam("presence_b2b", "default_expiration", 3600) modparam("presence_b2b", "handle_presence_subscriptions", 1) modparam("usrloc", "db_mode", 0) # modparam("domain|uri_db|acc|auth_db|usrloc|msilo", "db_url", "mysql://ser:heslo@127.0.0.1:3306/ser") modparam("fifo", "fifo_file", "/tmp/ser_fifo") # ------------------------- request routing logic ------------------- # main routing logic route{ # XML RPC if (method == "POST" || method == "GET") { create_via(); dispatch_rpc(); break; } # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); break; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!method=="REGISTER") record_route(); # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); break; }; if (uri==myself) { if (method=="SUBSCRIBE") { if (!t_newtran()) { sl_reply_error(); break; }; # new subscription if (@to.tag=="") { if ((@msg.supported=~"eventlist")) { # Supported header field # -> may be RLS subscription if (is_simple_rls_target("$uid-list")) { # log(1, "it is simple subscription!\n"); # handle_rls_subscription("1"); # takes From UID and makes XCAP query # for user's list named "default" if (@to.tag=="") { # only for new subscriptions (with empty to tag if (!query_resource_list("default")) { t_reply("500", "XCAP query error"); break; } } } if (!have_flat_list()) { # query_resource_list failed or was not called # do standard RLS query acording to To/AOR query_rls_services(); } if (have_flat_list()) { handle_rls_subscription("1"); break; } } # SUBSCRIBE to existing user # xlog("L_ERR", "PA: handling subscription: %tu from: %fu\n"); handle_subscription("registrar"); break; } else { # renewal subscription if (!handle_rls_subscription("0")) { handle_subscription("registrar"); } break; } } if (method=="REGISTER") { save("location"); break; }; if (method=="PUBLISH") { if (!t_newtran()) { sl_reply_error(); break; }; handle_publish("registrar"); break; }; if (method=="NOTIFY") { if (!t_newtran()) { log(1, "newtran error\n"); sl_reply_error(); break; }; if (!handle_notify()) { t_reply("481", "Unable to handle notification"); } break; }; # message authorization if (method=="MESSAGE") { log(1, "MESSAGE authorization\n"); if (!authorize_message("http://localhost/xcap")) { sl_reply("403", "Forbidden"); break; } } # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found"); break; }; }; # append_hf("P-hint: usrloc applied\r\n"); route(1); } route[1] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; } kamailio-4.0.4/etc/kamailio.cfg0000644000000000000000000005123512223032461015026 0ustar rootroot#!KAMAILIO # # Kamailio (OpenSER) SIP Server v4.0 - default configuration script # - web: http://www.kamailio.org # - git: http://sip-router.org # # Direct your questions about this file to: # # Refer to the Core CookBook at http://www.kamailio.org/wiki/ # for an explanation of possible statements, functions and parameters. # # Several features can be enabled using '#!define WITH_FEATURE' directives: # # *** To run in debug mode: # - define WITH_DEBUG # # *** To enable mysql: # - define WITH_MYSQL # # *** To enable authentication execute: # - enable mysql # - define WITH_AUTH # - add users using 'kamctl' # # *** To enable IP authentication execute: # - enable mysql # - enable authentication # - define WITH_IPAUTH # - add IP addresses with group id '1' to 'address' table # # *** To enable persistent user location execute: # - enable mysql # - define WITH_USRLOCDB # # *** To enable presence server execute: # - enable mysql # - define WITH_PRESENCE # # *** To enable nat traversal execute: # - define WITH_NAT # - install RTPProxy: http://www.rtpproxy.org # - start RTPProxy: # rtpproxy -l _your_public_ip_ -s udp:localhost:7722 # # *** To enable PSTN gateway routing execute: # - define WITH_PSTN # - set the value of pstn.gw_ip # - check route[PSTN] for regexp routing condition # # *** To enable database aliases lookup execute: # - enable mysql # - define WITH_ALIASDB # # *** To enable speed dial lookup execute: # - enable mysql # - define WITH_SPEEDDIAL # # *** To enable multi-domain support execute: # - enable mysql # - define WITH_MULTIDOMAIN # # *** To enable TLS support execute: # - adjust CFGDIR/tls.cfg as needed # - define WITH_TLS # # *** To enable XMLRPC support execute: # - define WITH_XMLRPC # - adjust route[XMLRPC] for access policy # # *** To enable anti-flood detection execute: # - adjust pike and htable=>ipban settings as needed (default is # block if more than 16 requests in 2 seconds and ban for 300 seconds) # - define WITH_ANTIFLOOD # # *** To block 3XX redirect replies execute: # - define WITH_BLOCK3XX # # *** To enable VoiceMail routing execute: # - define WITH_VOICEMAIL # - set the value of voicemail.srv_ip # - adjust the value of voicemail.srv_port # # *** To enhance accounting execute: # - enable mysql # - define WITH_ACCDB # - add following columns to database #!ifdef ACCDB_COMMENT ALTER TABLE acc ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE acc ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT ''; ALTER TABLE acc ADD COLUMN src_ip varchar(64) NOT NULL default ''; ALTER TABLE acc ADD COLUMN dst_ouser VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE acc ADD COLUMN dst_user VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE acc ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT ''; ALTER TABLE missed_calls ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE missed_calls ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT ''; ALTER TABLE missed_calls ADD COLUMN src_ip varchar(64) NOT NULL default ''; ALTER TABLE missed_calls ADD COLUMN dst_ouser VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE missed_calls ADD COLUMN dst_user VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE missed_calls ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT ''; #!endif ####### Include Local Config If Exists ######### import_file "kamailio-local.cfg" ####### Defined Values ######### # *** Value defines - IDs used later in config #!ifdef WITH_MYSQL # - database URL - used to connect to database server by modules such # as: auth_db, acc, usrloc, a.s.o. #!ifndef DBURL #!define DBURL "mysql://kamailio:kamailiorw@localhost/kamailio" #!endif #!endif #!ifdef WITH_MULTIDOMAIN # - the value for 'use_domain' parameters #!define MULTIDOMAIN 1 #!else #!define MULTIDOMAIN 0 #!endif # - flags # FLT_ - per transaction (message) flags # FLB_ - per branch flags #!define FLT_ACC 1 #!define FLT_ACCMISSED 2 #!define FLT_ACCFAILED 3 #!define FLT_NATS 5 #!define FLB_NATB 6 #!define FLB_NATSIPPING 7 ####### Global Parameters ######### ### LOG Levels: 3=DBG, 2=INFO, 1=NOTICE, 0=WARN, -1=ERR #!ifdef WITH_DEBUG debug=4 log_stderror=yes #!else debug=2 log_stderror=no #!endif memdbg=5 memlog=5 log_facility=LOG_LOCAL0 fork=yes children=4 /* uncomment the next line to disable TCP (default on) */ #disable_tcp=yes /* uncomment the next line to disable the auto discovery of local aliases based on reverse DNS on IPs (default on) */ #auto_aliases=no /* add local domain aliases */ #alias="sip.mydomain.com" /* uncomment and configure the following line if you want Kamailio to bind on a specific interface/port/proto (default bind on all available) */ #listen=udp:10.0.0.10:5060 /* port to listen to * - can be specified more than once if needed to listen on many ports */ port=5060 #!ifdef WITH_TLS enable_tls=yes #!endif # life time of TCP connection when there is no traffic # - a bit higher than registration expires to cope with UA behind NAT tcp_connection_lifetime=3605 ####### Custom Parameters ######### # These parameters can be modified runtime via RPC interface # - see the documentation of 'cfg_rpc' module. # # Format: group.id = value 'desc' description # Access: $sel(cfg_get.group.id) or @cfg_get.group.id # #!ifdef WITH_PSTN # PSTN GW Routing # # - pstn.gw_ip: valid IP or hostname as string value, example: # pstn.gw_ip = "10.0.0.101" desc "My PSTN GW Address" # # - by default is empty to avoid misrouting pstn.gw_ip = "" desc "PSTN GW Address" pstn.gw_port = "" desc "PSTN GW Port" #!endif #!ifdef WITH_VOICEMAIL # VoiceMail Routing on offline, busy or no answer # # - by default Voicemail server IP is empty to avoid misrouting voicemail.srv_ip = "" desc "VoiceMail IP Address" voicemail.srv_port = "5060" desc "VoiceMail Port" #!endif ####### Modules Section ######## # set paths to location of modules (to sources or installation folders) #!ifdef WITH_SRCPATH mpath="modules_k:modules" #!else mpath="/usr/local/lib/kamailio/modules_k/:/usr/local/lib/kamailio/modules/" #!endif #!ifdef WITH_MYSQL loadmodule "db_mysql.so" #!endif loadmodule "mi_fifo.so" loadmodule "kex.so" loadmodule "corex.so" loadmodule "tm.so" loadmodule "tmx.so" loadmodule "sl.so" loadmodule "rr.so" loadmodule "pv.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "siputils.so" loadmodule "xlog.so" loadmodule "sanity.so" loadmodule "ctl.so" loadmodule "cfg_rpc.so" loadmodule "mi_rpc.so" loadmodule "acc.so" #!ifdef WITH_AUTH loadmodule "auth.so" loadmodule "auth_db.so" #!ifdef WITH_IPAUTH loadmodule "permissions.so" #!endif #!endif #!ifdef WITH_ALIASDB loadmodule "alias_db.so" #!endif #!ifdef WITH_SPEEDDIAL loadmodule "speeddial.so" #!endif #!ifdef WITH_MULTIDOMAIN loadmodule "domain.so" #!endif #!ifdef WITH_PRESENCE loadmodule "presence.so" loadmodule "presence_xml.so" #!endif #!ifdef WITH_NAT loadmodule "nathelper.so" loadmodule "rtpproxy.so" #!endif #!ifdef WITH_TLS loadmodule "tls.so" #!endif #!ifdef WITH_ANTIFLOOD loadmodule "htable.so" loadmodule "pike.so" #!endif #!ifdef WITH_XMLRPC loadmodule "xmlrpc.so" #!endif #!ifdef WITH_DEBUG loadmodule "debugger.so" #!endif # ----------------- setting module-specific parameters --------------- # ----- mi_fifo params ----- modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") # ----- tm params ----- # auto-discard branches from previous serial forking leg modparam("tm", "failure_reply_mode", 3) # default retransmission timeout: 30sec modparam("tm", "fr_timer", 30000) # default invite retransmission timeout after 1xx: 120sec modparam("tm", "fr_inv_timer", 120000) # ----- rr params ----- # add value to ;lr param to cope with most of the UAs modparam("rr", "enable_full_lr", 1) # do not append from tag to the RR (no need for this script) modparam("rr", "append_fromtag", 0) # ----- registrar params ----- modparam("registrar", "method_filtering", 1) /* uncomment the next line to disable parallel forking via location */ # modparam("registrar", "append_branches", 0) /* uncomment the next line not to allow more than 10 contacts per AOR */ #modparam("registrar", "max_contacts", 10) # max value for expires of registrations modparam("registrar", "max_expires", 3600) # set it to 1 to enable GRUU modparam("registrar", "gruu_enabled", 0) # ----- acc params ----- /* what special events should be accounted ? */ modparam("acc", "early_media", 0) modparam("acc", "report_ack", 0) modparam("acc", "report_cancels", 0) /* by default ww do not adjust the direct of the sequential requests. if you enable this parameter, be sure the enable "append_fromtag" in "rr" module */ modparam("acc", "detect_direction", 0) /* account triggers (flags) */ modparam("acc", "log_flag", FLT_ACC) modparam("acc", "log_missed_flag", FLT_ACCMISSED) modparam("acc", "log_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_ouser=$tU;dst_user=$rU;dst_domain=$rd") modparam("acc", "failed_transaction_flag", FLT_ACCFAILED) /* enhanced DB accounting */ #!ifdef WITH_ACCDB modparam("acc", "db_flag", FLT_ACC) modparam("acc", "db_missed_flag", FLT_ACCMISSED) modparam("acc", "db_url", DBURL) modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_ouser=$tU;dst_user=$rU;dst_domain=$rd") #!endif # ----- usrloc params ----- /* enable DB persistency for location entries */ #!ifdef WITH_USRLOCDB modparam("usrloc", "db_url", DBURL) modparam("usrloc", "db_mode", 2) modparam("usrloc", "use_domain", MULTIDOMAIN) #!endif # ----- auth_db params ----- #!ifdef WITH_AUTH modparam("auth_db", "db_url", DBURL) modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") modparam("auth_db", "load_credentials", "") modparam("auth_db", "use_domain", MULTIDOMAIN) # ----- permissions params ----- #!ifdef WITH_IPAUTH modparam("permissions", "db_url", DBURL) modparam("permissions", "db_mode", 1) #!endif #!endif # ----- alias_db params ----- #!ifdef WITH_ALIASDB modparam("alias_db", "db_url", DBURL) modparam("alias_db", "use_domain", MULTIDOMAIN) #!endif # ----- speeddial params ----- #!ifdef WITH_SPEEDDIAL modparam("speeddial", "db_url", DBURL) modparam("speeddial", "use_domain", MULTIDOMAIN) #!endif # ----- domain params ----- #!ifdef WITH_MULTIDOMAIN modparam("domain", "db_url", DBURL) # register callback to match myself condition with domains list modparam("domain", "register_myself", 1) #!endif #!ifdef WITH_PRESENCE # ----- presence params ----- modparam("presence", "db_url", DBURL) # ----- presence_xml params ----- modparam("presence_xml", "db_url", DBURL) modparam("presence_xml", "force_active", 1) #!endif #!ifdef WITH_NAT # ----- rtpproxy params ----- modparam("rtpproxy", "rtpproxy_sock", "udp:127.0.0.1:7722") # ----- nathelper params ----- modparam("nathelper", "natping_interval", 30) modparam("nathelper", "ping_nated_only", 1) modparam("nathelper", "sipping_bflag", FLB_NATSIPPING) modparam("nathelper", "sipping_from", "sip:pinger@kamailio.org") # params needed for NAT traversal in other modules modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)") modparam("usrloc", "nat_bflag", FLB_NATB) #!endif #!ifdef WITH_TLS # ----- tls params ----- modparam("tls", "config", "/usr/local/etc/kamailio/tls.cfg") #!endif #!ifdef WITH_ANTIFLOOD # ----- pike params ----- modparam("pike", "sampling_time_unit", 2) modparam("pike", "reqs_density_per_unit", 16) modparam("pike", "remove_latency", 4) # ----- htable params ----- # ip ban htable with autoexpire after 5 minutes modparam("htable", "htable", "ipban=>size=8;autoexpire=300;") #!endif #!ifdef WITH_XMLRPC # ----- xmlrpc params ----- modparam("xmlrpc", "route", "XMLRPC"); modparam("xmlrpc", "url_match", "^/RPC") #!endif #!ifdef WITH_DEBUG # ----- debugger params ----- modparam("debugger", "cfgtrace", 1) #!endif ####### Routing Logic ######## # Main SIP request routing logic # - processing of any incoming SIP request starts with this route # - note: this is the same as route { ... } request_route { # per request initial checks route(REQINIT); # NAT detection route(NATDETECT); # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) { route(RELAY); } exit; } # handle requests within SIP dialogs route(WITHINDLG); ### only initial requests (no To tag) t_check_trans(); # authentication route(AUTH); # record routing for dialog forming requests (in case they are routed) # - remove preloaded route headers remove_hf("Route"); if (is_method("INVITE|SUBSCRIBE")) record_route(); # account only INVITEs if (is_method("INVITE")) { setflag(FLT_ACC); # do accounting } # dispatch requests to foreign domains route(SIPOUT); ### requests for my local domains # handle presence related requests route(PRESENCE); # handle registrations route(REGISTRAR); if ($rU==$null) { # request with no Username in RURI sl_send_reply("484","Address Incomplete"); exit; } # dispatch destinations to PSTN route(PSTN); # user location service route(LOCATION); } route[RELAY] { # enable additional event routes for forwarded requests # - serial forking, RTP relaying handling, a.s.o. if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) { if(!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH"); } if (is_method("INVITE|SUBSCRIBE|UPDATE")) { if(!t_is_set("onreply_route")) t_on_reply("MANAGE_REPLY"); } if (is_method("INVITE")) { if(!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE"); } if (!t_relay()) { sl_reply_error(); } exit; } # Per SIP request initial checks route[REQINIT] { #!ifdef WITH_ANTIFLOOD # flood dection from same IP and traffic ban for a while # be sure you exclude checking trusted peers, such as pstn gateways # - local host excluded (e.g., loop to self) if(src_ip!=myself) { if($sht(ipban=>$si)!=$null) { # ip is already blocked xdbg("request from blocked IP - $rm from $fu (IP:$si:$sp)\n"); exit; } if (!pike_check_req()) { xlog("L_ALERT","ALERT: pike blocking $rm from $fu (IP:$si:$sp)\n"); $sht(ipban=>$si) = 1; exit; } } #!endif if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if(!sanity_check("1511", "7")) { xlog("Malformed SIP message from $si:$sp\n"); exit; } } # Handle requests within SIP dialogs route[WITHINDLG] { if (has_totag()) { # sequential request withing a dialog should # take the path determined by record-routing if (loose_route()) { route(DLGURI); if (is_method("BYE")) { setflag(FLT_ACC); # do accounting ... setflag(FLT_ACCFAILED); # ... even if the transaction fails } else if ( is_method("ACK") ) { # ACK is forwarded statelessy route(NATMANAGE); } else if ( is_method("NOTIFY") ) { # Add Record-Route for in-dialog NOTIFY as per RFC 6665. record_route(); } route(RELAY); } else { if (is_method("SUBSCRIBE") && uri == myself) { # in-dialog subscribe requests route(PRESENCE); exit; } if ( is_method("ACK") ) { if ( t_check_trans() ) { # no loose-route, but stateful ACK; # must be an ACK after a 487 # or e.g. 404 from upstream server route(RELAY); exit; } else { # ACK without matching transaction ... ignore and discard exit; } } sl_send_reply("404","Not here"); } exit; } } # Handle SIP registrations route[REGISTRAR] { if (is_method("REGISTER")) { if(isflagset(FLT_NATS)) { setbflag(FLB_NATB); # uncomment next line to do SIP NAT pinging ## setbflag(FLB_NATSIPPING); } if (!save("location")) sl_reply_error(); exit; } } # USER location service route[LOCATION] { #!ifdef WITH_SPEEDDIAL # search for short dialing - 2-digit extension if($rU=~"^[0-9][0-9]$") if(sd_lookup("speed_dial")) route(SIPOUT); #!endif #!ifdef WITH_ALIASDB # search in DB-based aliases if(alias_db_lookup("dbaliases")) route(SIPOUT); #!endif $avp(oexten) = $rU; if (!lookup("location")) { $var(rc) = $rc; route(TOVOICEMAIL); t_newtran(); switch ($var(rc)) { case -1: case -3: send_reply("404", "Not Found"); exit; case -2: send_reply("405", "Method Not Allowed"); exit; } } # when routing via usrloc, log the missed calls also if (is_method("INVITE")) { setflag(FLT_ACCMISSED); } route(RELAY); exit; } # Presence server route route[PRESENCE] { if(!is_method("PUBLISH|SUBSCRIBE")) return; #!ifdef WITH_PRESENCE if (!t_newtran()) { sl_reply_error(); exit; }; if(is_method("PUBLISH")) { handle_publish(); t_release(); } else if( is_method("SUBSCRIBE")) { handle_subscribe(); t_release(); } exit; #!endif # if presence enabled, this part will not be executed if (is_method("PUBLISH") || $rU==$null) { sl_send_reply("404", "Not here"); exit; } return; } # Authentication route route[AUTH] { #!ifdef WITH_AUTH #!ifdef WITH_IPAUTH if((!is_method("REGISTER")) && allow_source_address()) { # source IP allowed return; } #!endif if (is_method("REGISTER") || from_uri==myself) { # authenticate requests if (!auth_check("$fd", "subscriber", "1")) { auth_challenge("$fd", "0"); exit; } # user authenticated - remove auth header if(!is_method("REGISTER|PUBLISH")) consume_credentials(); } # if caller is not local subscriber, then check if it calls # a local destination, otherwise deny, not an open relay here if (from_uri!=myself && uri!=myself) { sl_send_reply("403","Not relaying"); exit; } #!endif return; } # Caller NAT detection route route[NATDETECT] { #!ifdef WITH_NAT force_rport(); if (nat_uac_test("19")) { if (is_method("REGISTER")) { fix_nated_register(); } else { add_contact_alias(); } setflag(FLT_NATS); } #!endif return; } # RTPProxy control route[NATMANAGE] { #!ifdef WITH_NAT if (is_request()) { if(has_totag()) { if(check_route_param("nat=yes")) { setbflag(FLB_NATB); } } } if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB))) return; rtpproxy_manage("co"); if (is_request()) { if (!has_totag()) { if(t_is_branch_route()) { add_rr_param(";nat=yes"); } } } if (is_reply()) { if(isbflagset(FLB_NATB)) { add_contact_alias(); } } #!endif return; } # URI update for dialog requests route[DLGURI] { #!ifdef WITH_NAT if(!isdsturiset()) { handle_ruri_alias(); } #!endif return; } # Routing to foreign domains route[SIPOUT] { if (!uri==myself) { append_hf("P-hint: outbound\r\n"); route(RELAY); } } # PSTN GW routing route[PSTN] { #!ifdef WITH_PSTN # check if PSTN GW IP is defined if (strempty($sel(cfg_get.pstn.gw_ip))) { xlog("SCRIPT: PSTN rotuing enabled but pstn.gw_ip not defined\n"); return; } # route to PSTN dialed numbers starting with '+' or '00' # (international format) # - update the condition to match your dialing rules for PSTN routing if(!($rU=~"^(\+|00)[1-9][0-9]{3,20}$")) return; # only local users allowed to call if(from_uri!=myself) { sl_send_reply("403", "Not Allowed"); exit; } if (strempty($sel(cfg_get.pstn.gw_port))) { $ru = "sip:" + $rU + "@" + $sel(cfg_get.pstn.gw_ip); } else { $ru = "sip:" + $rU + "@" + $sel(cfg_get.pstn.gw_ip) + ":" + $sel(cfg_get.pstn.gw_port); } route(RELAY); exit; #!endif return; } # XMLRPC routing #!ifdef WITH_XMLRPC route[XMLRPC] { # allow XMLRPC from localhost if ((method=="POST" || method=="GET") && (src_ip==127.0.0.1)) { # close connection only for xmlrpclib user agents (there is a bug in # xmlrpclib: it waits for EOF before interpreting the response). if ($hdr(User-Agent) =~ "xmlrpclib") set_reply_close(); set_reply_no_connect(); dispatch_rpc(); exit; } send_reply("403", "Forbidden"); exit; } #!endif # route to voicemail server route[TOVOICEMAIL] { #!ifdef WITH_VOICEMAIL if(!is_method("INVITE")) return; # check if VoiceMail server IP is defined if (strempty($sel(cfg_get.voicemail.srv_ip))) { xlog("SCRIPT: VoiceMail rotuing enabled but IP not defined\n"); return; } if($avp(oexten)==$null) return; $ru = "sip:" + $avp(oexten) + "@" + $sel(cfg_get.voicemail.srv_ip) + ":" + $sel(cfg_get.voicemail.srv_port); route(RELAY); exit; #!endif return; } # manage outgoing branches branch_route[MANAGE_BRANCH] { xdbg("new branch [$T_branch_idx] to $ru\n"); route(NATMANAGE); } # manage incoming replies onreply_route[MANAGE_REPLY] { xdbg("incoming reply\n"); if(status=~"[12][0-9][0-9]") route(NATMANAGE); } # manage failure routing cases failure_route[MANAGE_FAILURE] { route(NATMANAGE); if (t_is_canceled()) { exit; } #!ifdef WITH_BLOCK3XX # block call redirect based on 3xx replies. if (t_check_status("3[0-9][0-9]")) { t_reply("404","Not found"); exit; } #!endif #!ifdef WITH_VOICEMAIL # serial forking # - route to voicemail on busy or no answer (timeout) if (t_check_status("486|408")) { $du = $null; route(TOVOICEMAIL); exit; } #!endif } kamailio-4.0.4/etc/tls/0000755000000000000000000000000012223032460013352 5ustar rootrootkamailio-4.0.4/etc/tls/user/0000755000000000000000000000000012223032460014330 5ustar rootrootkamailio-4.0.4/etc/tls/user/user-calist.pem0000644000000000000000000000242612223032460017272 0ustar rootroot-----BEGIN CERTIFICATE----- MIIDlTCCAn2gAwIBAgIJAIajPSGooWsRMA0GCSqGSIb3DQEBBQUAMGQxEDAOBgNV BAMTB09wZW5TRVIxDDAKBgNVBAgTA1NJUDELMAkGA1UEBhMCSVAxHzAdBgkqhkiG 9w0BCQEWEHRlYW1Ab3BlbnNlci5vcmcxFDASBgNVBAoTC29wZW5zZXIub3JnMB4X DTA1MTAyODE5MDk0NFoXDTA2MTAyODE5MDk0NFowZDEQMA4GA1UEAxMHT3BlblNF UjEMMAoGA1UECBMDU0lQMQswCQYDVQQGEwJJUDEfMB0GCSqGSIb3DQEJARYQdGVh bUBvcGVuc2VyLm9yZzEUMBIGA1UEChMLb3BlbnNlci5vcmcwggEiMA0GCSqGSIb3 DQEBAQUAA4IBDwAwggEKAoIBAQCtOHaQSReSuBpet7hGSJtAtqoKzPYpYdoIijiw 9tlB/ODBFWjz583MKJuUxIWt7cqnUheBNgBGvrtZYVB6bUxjyTS4m3g8NrrIcWuw ASTYqYWRmdWbiyLraC6MeoNd8fUG50kbfZa6fh/pkBzZDQb/4Z1cfqIEJ4VpVTLO CRDreGNfTmG1BREtX+DnzGiKKpGCJVUs/ffX+pTViVC9Y9TDwinJyV9fdtwtI42U pAxJA+iiG1oOTuk5yjxKOkxKlHju4DIKND3XnPriU3cjdh/93XhMQDxb/jNZhL6R Y/XhcEsS567ZL3VMUw2VxaDiA+exjAaS/wA8b/rHR8af8+svAgMBAAGjSjBIMAwG A1UdEwQFMAMBAf8wGwYDVR0RBBQwEoEQdGVhbUBvcGVuc2VyLm9yZzAbBgNVHRIE FDASgRB0ZWFtQG9wZW5zZXIub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAabH/C3EiJ 1Dc3uq9KlF7hp2OODAI4928Yy6+pzcbppGoOhAzmDaEoXmlhIMomgeoD031rsESg +WNavXs2IFCD0U4ocpCa7L0AKDYr8U+5lUtEi7DzXYkqNW17TB+HxkaQVdzvhGrh +R3WasX0AIc+inxvgkXgzjm5M6IyRUE0FK3wDcACNfnjrDzzXVvdccrM0ZpCFr5T NWJjQYpGcqDB1HDXVBMPFxQ7XyqKOTOgDCMh4J9g9Ye8eL8uukKJx1eEjHoG4RbM uQmCYQcCWO0Z/oqiI0FObzsZigf8Xoi9GjK2RjdPEukG3QOflnAm7IjPOLMGzFBn peqgBkp+jZv5 -----END CERTIFICATE----- kamailio-4.0.4/etc/tls/user/user-cert.pem0000644000000000000000000000562512223032460016754 0ustar rootrootCertificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha1WithRSAEncryption Issuer: CN=OpenSER, ST=SIP, C=IP/emailAddress=team@openser.org, O=openser.org Validity Not Before: Oct 28 19:16:29 2005 GMT Not After : Oct 28 19:16:29 2006 GMT Subject: C=IP, ST=SIP, O=OpenSER project, OU=OpenSER TLS tester, CN=OpenSER/emailAddress=team@openser.org Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (512 bit) Modulus (512 bit): 00:b6:13:f8:54:99:2a:c3:39:2d:fa:b0:5a:cc:4d: ca:8b:d0:53:9d:c9:59:ce:17:1e:ba:0a:8e:82:eb: 9b:c2:69:33:93:3a:b1:68:aa:da:40:bd:de:b5:6f: c2:5e:99:72:59:f4:68:75:4c:01:05:94:1b:ba:1d: f2:bb:10:67:d7 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Signature Algorithm: sha1WithRSAEncryption 3d:41:b5:28:a4:10:c7:c8:de:29:6f:2e:ed:a8:30:28:2f:9e: 3c:a9:95:c4:df:73:7b:2e:1c:51:84:a2:bd:ff:56:94:6f:5b: ac:e2:8f:77:31:74:82:29:8d:e7:a8:c7:da:14:7d:6c:62:dc: 2f:2e:70:0c:eb:53:67:fa:1b:0a:e5:e8:58:41:5e:dd:84:3d: 3d:22:c2:c3:b5:69:e5:11:86:2a:a6:4c:f3:07:98:00:f5:cf: c8:f1:ea:a3:62:f6:40:ef:08:74:93:de:5b:f2:dc:01:dc:0f: 2a:81:e3:03:56:d1:ef:ca:22:fc:18:29:4f:b0:45:b1:d0:30: 6b:63:1b:72:ef:9d:ae:bf:ef:b3:0d:fa:39:49:25:48:46:6d: 68:a1:12:7a:23:1e:ba:53:8e:a5:a2:38:8e:3b:0f:df:b1:b6: 1e:61:69:80:57:c1:f1:8d:62:69:e0:85:e9:6b:e0:10:4d:37: b0:3e:98:cc:b5:b5:ea:db:2f:a2:02:51:85:27:1d:65:74:2e: e3:f4:1f:0c:52:3e:f8:86:6b:50:f1:38:1d:23:97:53:3c:84: 03:4e:25:a0:66:3a:16:aa:94:77:f2:c8:65:db:ce:c7:0d:c2: 44:7a:8e:af:ee:c5:bc:4e:aa:2f:29:c5:02:33:ea:c7:78:76: 02:d4:b4:ca -----BEGIN CERTIFICATE----- MIICqjCCAZKgAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMRAwDgYDVQQDEwdPcGVu U0VSMQwwCgYDVQQIEwNTSVAxCzAJBgNVBAYTAklQMR8wHQYJKoZIhvcNAQkBFhB0 ZWFtQG9wZW5zZXIub3JnMRQwEgYDVQQKEwtvcGVuc2VyLm9yZzAeFw0wNTEwMjgx OTE2MjlaFw0wNjEwMjgxOTE2MjlaMIGFMQswCQYDVQQGEwJJUDEMMAoGA1UECBMD U0lQMRgwFgYDVQQKEw9PcGVuU0VSIHByb2plY3QxGzAZBgNVBAsTEk9wZW5TRVIg VExTIHRlc3RlcjEQMA4GA1UEAxMHT3BlblNFUjEfMB0GCSqGSIb3DQEJARYQdGVh bUBvcGVuc2VyLm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC2E/hUmSrDOS36 sFrMTcqL0FOdyVnOFx66Co6C65vCaTOTOrFoqtpAvd61b8JemXJZ9Gh1TAEFlBu6 HfK7EGfXAgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADggEBAD1B tSikEMfI3ilvLu2oMCgvnjyplcTfc3suHFGEor3/VpRvW6zij3cxdIIpjeeox9oU fWxi3C8ucAzrU2f6Gwrl6FhBXt2EPT0iwsO1aeURhiqmTPMHmAD1z8jx6qNi9kDv CHST3lvy3AHcDyqB4wNW0e/KIvwYKU+wRbHQMGtjG3Lvna6/77MN+jlJJUhGbWih EnojHrpTjqWiOI47D9+xth5haYBXwfGNYmnghelr4BBNN7A+mMy1terbL6ICUYUn HWV0LuP0HwxSPviGa1DxOB0jl1M8hANOJaBmOhaqlHfyyGXbzscNwkR6jq/uxbxO qi8pxQIz6sd4dgLUtMo= -----END CERTIFICATE----- kamailio-4.0.4/etc/tls/user/user-cert_req.pem0000644000000000000000000000077512223032460017624 0ustar rootroot-----BEGIN CERTIFICATE REQUEST----- MIIBQDCB6wIBADCBhTEQMA4GA1UEAxMHT3BlblNFUjEMMAoGA1UECBMDU0lQMQsw CQYDVQQGEwJJUDEfMB0GCSqGSIb3DQEJARYQdGVhbUBvcGVuc2VyLm9yZzEYMBYG A1UEChMPT3BlblNFUiBwcm9qZWN0MRswGQYDVQQLExJPcGVuU0VSIFRMUyB0ZXN0 ZXIwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAthP4VJkqwzkt+rBazE3Ki9BTnclZ zhceugqOguubwmkzkzqxaKraQL3etW/CXplyWfRodUwBBZQbuh3yuxBn1wIDAQAB oAAwDQYJKoZIhvcNAQEFBQADQQA0mFBhg/bbxznLbLcc2nQo0022x0HeT3Qxl0lm SlIvfG2YphvBYuc54HFjqHfRNrmckAVoSrVpEpcVXSO/g+L6 -----END CERTIFICATE REQUEST----- kamailio-4.0.4/etc/tls/user/user-privkey.pem0000644000000000000000000000076112223032460017504 0ustar rootroot-----BEGIN RSA PRIVATE KEY----- MIIBOwIBAAJBALYT+FSZKsM5LfqwWsxNyovQU53JWc4XHroKjoLrm8JpM5M6sWiq 2kC93rVvwl6Zcln0aHVMAQWUG7od8rsQZ9cCAwEAAQJBALCEy8u4cmyxkpHnRx+q iyLg5S+jdR0H7RIQCfmC0Y63LFIAsXasHQorV83r2br4eRRaeU87CsVLXdBUjvbe ywECIQDaq7ojtDBTGhNKILZ9CBOTk18jDdHgTJAC7ZpGvdGt+QIhANUpE9j5JWqA kIcR55eSJXmjoKB1IGnTz0kaMB/8B3hPAiEAw0351IXNW4vAisao9wdNpNNNd5uS RklbnqHk1yYWrtECIEXqi1AHqHYeZUloXgYhMZmMSgtXX6JWjw7zQAW9rNWRAiBe Xmwo0k9fY/KawVSsnY4rgYqk6PDWK98jl5/x/veDZA== -----END RSA PRIVATE KEY----- kamailio-4.0.4/etc/tls/README0000644000000000000000000000175212223032460014237 0ustar rootroot This directory contains an already generated TLS certificate that can be used in your OpenSER configuration. It's a generic certificate with the main purpose of serving as example and for testings. IMPORTANT: it's not a trustable certificate - the CA is also an example. All TLS configuration file may be found in "user" directory. If you want to generate your own certificate, you may find in the "rootCA" directory the root CA to sign your request with. Use "kamctl tls userCERT" command to create a new certificate; the rootCA password is "openser". What is the purpose of these default CA and certificate? First to make an out-of-the box TLS configuration for users not so familiar with SSL/TLS. Second, to give access to the same CA root to a large community in order to encourage testings and interconnections via TLS with minimum of troubles. For any questions, please address to : team@openser.org (if you want to keep your question private) users@openser.org (public mailing list) kamailio-4.0.4/etc/tls/user.conf0000644000000000000000000000111712223032460015177 0ustar rootroot# # LocalServer.conf # [ req ] prompt = no distinguished_name = server_distinguished_name [ server_distinguished_name ] commonName = somename.somewhere.com # please update stateOrProvinceName = Some State # please update countryName = XY # please update emailAddress = root@somename.somewhere.com # please update organizationName = My Large Organization Name # please update organizationalUnitName = My Subunit of Large Organization # please update kamailio-4.0.4/etc/tls/rootCA/0000755000000000000000000000000012223032460014541 5ustar rootrootkamailio-4.0.4/etc/tls/rootCA/serial0000644000000000000000000000000312223032460015734 0ustar rootroot02 kamailio-4.0.4/etc/tls/rootCA/private/0000755000000000000000000000000012223032460016213 5ustar rootrootkamailio-4.0.4/etc/tls/rootCA/private/cakey.pem0000644000000000000000000000331712223032460020016 0ustar rootroot-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,4FF5A11F3774B0A0 55A4nhjLcx9PgAAnxRoYwkzCtM1D/2Rb3cLtUo6GTG8E+RzsKgo9PIZDDSDJAy9k 39UXsC69C7jxmqP7oBQgU8M15yAKQwX9zIsi82tRb/oh58f331fK3vtCzvZrJCwQ 5ZWUcmLCrtxVoDj2N9pItB3OizHcUYGmPVxhwWzLyA0Wnq5/EScVaIVEEUQUHZtU 3iJCTUkgc8tFDsFAXEyfO9Y5MabQ3AKSYBgUklGJs0+4facYyCgfYWDwuoanxveQ 2XIVtv2dcV0WOklGA/G+2tfwtRa0G0GBl4Jf49/bv2fONrhGX4KE4aJb7jp8LTZA nFokO8L/OPHQQDatZ4r9gqIJMzY8uMtzRdkccZvV+xecDieZ6PN4MW36s/AzMp3q oS72x413GSlDOLlrmDyy7NWo81yD5aYUCgoW6QOOtDCMwFGIJ+lEbt7vPasVp0GJ Qpsni2Kz7knpjZJHoAyARfm7rPX9Dsk60A16Up9/4LHyy2vsNfD8uXwRcYSTx3nF d5ClZYtwQp88cfe2qK2L5pegN+YoghXukE191OruZ3cU/ialPqGHefHTd46JyWsZ k1k0IxXwyAyLbWcFRJ0IKogYK/p/Jz2vAggGS/4utH6OWPkDObo6mclKO3nQk/AK YGtcAvbN+MOB9US6v/gqHSbh3FvjKtdeSdzkgOqJD0v9sYoshk9XlAcMKljPAqxt QuFlsNC221z9jpIPVWDoHiufVtuX2/qif0cYhy0T3ey1crgZ1Wf5Xt2VxRk8xCGs FN5bOmpQcFHhEAmeBS0/cRTM+P0RZ0qLoJUWgedxF9UHweNgpu/JgE8ReF7xIfbV OHi2ZEziydYuqAQaLi5rDditDJVz+8lnH9k8qWk9WKbFE+Oz/7i5/f10t+062aC2 0pTg6dptI/X49p0TWuxrI/UTqQjreauTaNaR32pOZlKuCWngtcxLTg/XGfTahsLN EUKEdQk4T/2OzCICwStkwlayjs+O2osNEE2n79e9IxPG/L7KzWA4RF6dwuJ2Zh5J 8xF/dAiL9C7EBIOQsPxLpjqQUUipedophsndkR5ADP4R3a97d2ZVsk6N0gGMfXHH 6UghbSAcDQ3xj1eSBXYmb2cq36A0g6j3B9Clkh0yO07H7C6jlHeUbGw/5uaa69pX H9kyPDpPP/sJ26jIBDCC1hzWX2v3/wl7CZRBgf9OQ6lY3x1Mk6eQbxW+G0itI5T2 MNWV0ae8Nr+vAi9OVmVUo3s0D2kUGtMRXC0ECwJUhXq68GDfLUy9y0ixeVrLnkix P1OHIu7VLawDvhWx+qQJRI6i0apAEWPTw+A9DiiwtrmILDTQJJWDHky+lja3zkt1 heVVfeSuyPeS/EgzQgN2kkpr+3ZgNcLSRuKpFdpN2olSyxXt+fRcTj0kluZqJkBn 7UpojLvzl6ODLZvIYFhHPrlZGVPer+YyuEQGt8Ni4NgFooJmOhzNlUOMQtEvo3kO lsll1jPUGO/5TIxoteHepTT/2nLhlbd2g6xsMNEMqqzmH/Q7OnLknI8IVE7AjkEv u138yr3yQfdZ+Q9/KDL9RICy/swCZct/dyIhZ6u6NGun2v6Kzzb3lw== -----END RSA PRIVATE KEY----- kamailio-4.0.4/etc/tls/rootCA/index.txt0000644000000000000000000000017212223032460016411 0ustar rootrootV 061028191629Z 01 unknown /C=IP/ST=SIP/O=OpenSER project/OU=OpenSER TLS tester/CN=OpenSER/emailAddress=team@openser.org kamailio-4.0.4/etc/tls/rootCA/cacert.pem0000644000000000000000000000242612223032460016511 0ustar rootroot-----BEGIN CERTIFICATE----- MIIDlTCCAn2gAwIBAgIJAIajPSGooWsRMA0GCSqGSIb3DQEBBQUAMGQxEDAOBgNV BAMTB09wZW5TRVIxDDAKBgNVBAgTA1NJUDELMAkGA1UEBhMCSVAxHzAdBgkqhkiG 9w0BCQEWEHRlYW1Ab3BlbnNlci5vcmcxFDASBgNVBAoTC29wZW5zZXIub3JnMB4X DTA1MTAyODE5MDk0NFoXDTA2MTAyODE5MDk0NFowZDEQMA4GA1UEAxMHT3BlblNF UjEMMAoGA1UECBMDU0lQMQswCQYDVQQGEwJJUDEfMB0GCSqGSIb3DQEJARYQdGVh bUBvcGVuc2VyLm9yZzEUMBIGA1UEChMLb3BlbnNlci5vcmcwggEiMA0GCSqGSIb3 DQEBAQUAA4IBDwAwggEKAoIBAQCtOHaQSReSuBpet7hGSJtAtqoKzPYpYdoIijiw 9tlB/ODBFWjz583MKJuUxIWt7cqnUheBNgBGvrtZYVB6bUxjyTS4m3g8NrrIcWuw ASTYqYWRmdWbiyLraC6MeoNd8fUG50kbfZa6fh/pkBzZDQb/4Z1cfqIEJ4VpVTLO CRDreGNfTmG1BREtX+DnzGiKKpGCJVUs/ffX+pTViVC9Y9TDwinJyV9fdtwtI42U pAxJA+iiG1oOTuk5yjxKOkxKlHju4DIKND3XnPriU3cjdh/93XhMQDxb/jNZhL6R Y/XhcEsS567ZL3VMUw2VxaDiA+exjAaS/wA8b/rHR8af8+svAgMBAAGjSjBIMAwG A1UdEwQFMAMBAf8wGwYDVR0RBBQwEoEQdGVhbUBvcGVuc2VyLm9yZzAbBgNVHRIE FDASgRB0ZWFtQG9wZW5zZXIub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAabH/C3EiJ 1Dc3uq9KlF7hp2OODAI4928Yy6+pzcbppGoOhAzmDaEoXmlhIMomgeoD031rsESg +WNavXs2IFCD0U4ocpCa7L0AKDYr8U+5lUtEi7DzXYkqNW17TB+HxkaQVdzvhGrh +R3WasX0AIc+inxvgkXgzjm5M6IyRUE0FK3wDcACNfnjrDzzXVvdccrM0ZpCFr5T NWJjQYpGcqDB1HDXVBMPFxQ7XyqKOTOgDCMh4J9g9Ye8eL8uukKJx1eEjHoG4RbM uQmCYQcCWO0Z/oqiI0FObzsZigf8Xoi9GjK2RjdPEukG3QOflnAm7IjPOLMGzFBn peqgBkp+jZv5 -----END CERTIFICATE----- kamailio-4.0.4/etc/tls/rootCA/certs/0000755000000000000000000000000012223032460015661 5ustar rootrootkamailio-4.0.4/etc/tls/rootCA/certs/01.pem0000644000000000000000000000562512223032460016614 0ustar rootrootCertificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha1WithRSAEncryption Issuer: CN=OpenSER, ST=SIP, C=IP/emailAddress=team@openser.org, O=openser.org Validity Not Before: Oct 28 19:16:29 2005 GMT Not After : Oct 28 19:16:29 2006 GMT Subject: C=IP, ST=SIP, O=OpenSER project, OU=OpenSER TLS tester, CN=OpenSER/emailAddress=team@openser.org Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (512 bit) Modulus (512 bit): 00:b6:13:f8:54:99:2a:c3:39:2d:fa:b0:5a:cc:4d: ca:8b:d0:53:9d:c9:59:ce:17:1e:ba:0a:8e:82:eb: 9b:c2:69:33:93:3a:b1:68:aa:da:40:bd:de:b5:6f: c2:5e:99:72:59:f4:68:75:4c:01:05:94:1b:ba:1d: f2:bb:10:67:d7 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Signature Algorithm: sha1WithRSAEncryption 3d:41:b5:28:a4:10:c7:c8:de:29:6f:2e:ed:a8:30:28:2f:9e: 3c:a9:95:c4:df:73:7b:2e:1c:51:84:a2:bd:ff:56:94:6f:5b: ac:e2:8f:77:31:74:82:29:8d:e7:a8:c7:da:14:7d:6c:62:dc: 2f:2e:70:0c:eb:53:67:fa:1b:0a:e5:e8:58:41:5e:dd:84:3d: 3d:22:c2:c3:b5:69:e5:11:86:2a:a6:4c:f3:07:98:00:f5:cf: c8:f1:ea:a3:62:f6:40:ef:08:74:93:de:5b:f2:dc:01:dc:0f: 2a:81:e3:03:56:d1:ef:ca:22:fc:18:29:4f:b0:45:b1:d0:30: 6b:63:1b:72:ef:9d:ae:bf:ef:b3:0d:fa:39:49:25:48:46:6d: 68:a1:12:7a:23:1e:ba:53:8e:a5:a2:38:8e:3b:0f:df:b1:b6: 1e:61:69:80:57:c1:f1:8d:62:69:e0:85:e9:6b:e0:10:4d:37: b0:3e:98:cc:b5:b5:ea:db:2f:a2:02:51:85:27:1d:65:74:2e: e3:f4:1f:0c:52:3e:f8:86:6b:50:f1:38:1d:23:97:53:3c:84: 03:4e:25:a0:66:3a:16:aa:94:77:f2:c8:65:db:ce:c7:0d:c2: 44:7a:8e:af:ee:c5:bc:4e:aa:2f:29:c5:02:33:ea:c7:78:76: 02:d4:b4:ca -----BEGIN CERTIFICATE----- MIICqjCCAZKgAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMRAwDgYDVQQDEwdPcGVu U0VSMQwwCgYDVQQIEwNTSVAxCzAJBgNVBAYTAklQMR8wHQYJKoZIhvcNAQkBFhB0 ZWFtQG9wZW5zZXIub3JnMRQwEgYDVQQKEwtvcGVuc2VyLm9yZzAeFw0wNTEwMjgx OTE2MjlaFw0wNjEwMjgxOTE2MjlaMIGFMQswCQYDVQQGEwJJUDEMMAoGA1UECBMD U0lQMRgwFgYDVQQKEw9PcGVuU0VSIHByb2plY3QxGzAZBgNVBAsTEk9wZW5TRVIg VExTIHRlc3RlcjEQMA4GA1UEAxMHT3BlblNFUjEfMB0GCSqGSIb3DQEJARYQdGVh bUBvcGVuc2VyLm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC2E/hUmSrDOS36 sFrMTcqL0FOdyVnOFx66Co6C65vCaTOTOrFoqtpAvd61b8JemXJZ9Gh1TAEFlBu6 HfK7EGfXAgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADggEBAD1B tSikEMfI3ilvLu2oMCgvnjyplcTfc3suHFGEor3/VpRvW6zij3cxdIIpjeeox9oU fWxi3C8ucAzrU2f6Gwrl6FhBXt2EPT0iwsO1aeURhiqmTPMHmAD1z8jx6qNi9kDv CHST3lvy3AHcDyqB4wNW0e/KIvwYKU+wRbHQMGtjG3Lvna6/77MN+jlJJUhGbWih EnojHrpTjqWiOI47D9+xth5haYBXwfGNYmnghelr4BBNN7A+mMy1terbL6ICUYUn HWV0LuP0HwxSPviGa1DxOB0jl1M8hANOJaBmOhaqlHfyyGXbzscNwkR6jq/uxbxO qi8pxQIz6sd4dgLUtMo= -----END CERTIFICATE----- kamailio-4.0.4/etc/tls/request.conf0000644000000000000000000000214712223032460015715 0ustar rootroot# # Default configuration to use when one # is not provided on the command line. # [ ca ] default_ca = CA_request # # Default location of directories and # files needed to generate certificates. # [ CA_request ] dir = ./rootCA database = $dir/index.txt new_certs_dir = $dir/certs certificate = $dir/cacert.pem serial = $dir/serial private_key = $dir/private/cakey.pem # # Default expiration and encryption # policies for certificates. # default_days = 365 default_crl_days = 1825 default_md = sha1 policy = req_policy # # Information to be moved from # request to the certificate # nameopt = ca_default certopt = ca_default copy_extensions = copy x509_extensions = cert_extensions # # The default policy to use when # generating the certificate. # [ req_policy ] countryName = supplied stateOrProvinceName = optional organizationName = supplied organizationalUnitName = optional commonName = supplied emailAddress = supplied [ cert_extensions ] basicConstraints = CA:false kamailio-4.0.4/etc/tls/ca.conf0000644000000000000000000000400112223032460014577 0ustar rootroot# # Default configuration to use when one # is not provided on the command line. # [ ca ] default_ca = local_ca # # Default location of directories and # files needed to generate certificates. # [ local_ca ] dir = ./rootCA certificate = $dir/cacert.pem database = $dir/index.txt new_certs_dir = $dir/certs private_key = $dir/private/cakey.pem serial = $dir/serial # # Default expiration and encryption # policies for certificates. # default_crl_days = 365 default_days = 1825 default_md = sha1 policy = local_ca_policy x509_extensions = local_ca_extensions # # Default policy to use when generating # server certificates. The following # fields must be defined in the server # certificate. # [ local_ca_policy ] commonName = supplied stateOrProvinceName = supplied countryName = supplied emailAddress = supplied organizationName = supplied organizationalUnitName = supplied # # x509 extensions to use when generating # server certificates. # [ local_ca_extensions ] #subjectAltName = DNS:altname.somewhere.com basicConstraints = CA:false nsCertType = server # # The default policy to use when # generating the root certificate. # [ req ] default_bits = 2048 default_keyfile = ./private/cakey.pem default_md = sha1 prompt = no distinguished_name = root_ca_distinguished_name x509_extensions = root_ca_extensions # # Root Certificate Authority distin- # guished name. Changes these fields to # your local environment. # [ root_ca_distinguished_name ] commonName = Your_NAME # please update stateOrProvinceName = Your_STATE # please update countryName = CO # please update emailAddress = YOUR_EMAIL # please update organizationName = YOUR_ORG_NAME # please update [ root_ca_extensions ] basicConstraints = CA:true subjectAltName = email:copy issuerAltName = issuer:copy kamailio-4.0.4/etc/im_gw.cfg0000644000000000000000000001063112223032460014334 0ustar rootroot### ----- IM Gateway [SMS+Jabber] config file for 'bat.iptel.org' ---- # ----------- global configuration parameters ------------------------ debug=9 # debug level (cmd line: -dddddddddd) #fork=yes #fork=no #log_stderror=no # (cmd line: -E) log_stderror=yes # (cmd line: -E) check_via=yes # (cmd. line: -v) dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) port=5070 children=2 # advertise IP address in Via (as opposed to advertising DNS name # which is annoying for downstream servers and some phones can # not handle DNS at all) listen=195.37.77.100 # ------------------ module loading ---------------------------------- loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/print/print.so" loadmodule "../sip_router/modules/tm/tm_mod.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/sms/sms.so" loadmodule "../sip_router/modules/textops/textops.so" loadmodule "../sip_router/modules/mysql/mysql.so" loadmodule "../sip_router/modules/jabber/jabber.so" loadmodule "../sip_router/modules/pike/pike.so" # ----------------- setting module-specific parameters --------------- # -- sms params -- modparam("sms","modems","Falcom [d=/dev/ttyS0;b=9600;p=9254;m=new;l=10;r=2]") modparam("sms","networks","D1[c=491710765000;m=10]") modparam("sms","links","Falcom[D1]") modparam("sms","domain","iptel.org") modparam("sms","max_sms_parts",3) modparam("sms","use_contact",1) # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 10 ) modparam("tm", "wt_timer", 10 ) # -- pike params -- # no more than minimum 5 or maximum 5*3 msg per 10 secs - only for sms modparam("pike","sampling_time_unit",60) modparam("pike","reqs_density_per_unit",5) modparam("pike","removel_latency",30) # -- jabber params -- modparam("jabber","db_url","mysql://s2jgw:47s2jgw11@127.0.0.1/sip_jab") modparam("jabber","jaddress","bat.iptel.org") modparam("jabber","jport",5222) modparam("jabber","workers",2) modparam("jabber","max_jobs",10) modparam("jabber","delay_time",17) modparam("jabber","cache_time",1800) # ------------------------- request routing logic ------------------- # main routing logic route{ # filter too old messages log("LOG: Checking maxfwd\n"); if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; # messages too large are denied if (len_gt( max_len )) { sl_send_reply("513", "Riesengross -- Message too large"); break; }; # accept only req coming from iptel.org if (!src_ip==195.37.77.101 | !( uri=~"iptel.org" | uri=~"195\.37\.77\.100" )) { sl_send_reply("403","Forbidden"); log("SER:Forbidden request: wrong src_ip or req_uri\n"); break; }; # we are not interested in non-MESSAGE requests if (!method=="MESSAGE") { sl_send_reply("501","Not Implemented"); break; }; # error occured ... if (! t_newtran()) { sl_reply_error(); break; }; # do what you want to do - first, it's for SMS or JABBER ? if ( search("To:.*@icq\.iptel\.org") | search("To:.*@msn\.iptel\.org") | search("To:.*@aim\.iptel\.org") | search("To:.*@yahoo\.iptel\.org") ) { ### ----- JABBER GATEWAY ------ log("MESSAGE received -> sending as JABBER\n"); if (jab_send_message()) { if (!t_reply("202","Accepted")) { # if replying failed, retry statelessly sl_reply_error(); }; }else{ if (!t_reply("502","Bad gateway - IM error")) { # if replying failed, retry statelessly sl_reply_error(); }; }; break; # End of Jabber }; ### ------ SMS GATEWAY -------- # let's block ips that send a lot of request!! if (!pike_check_req() ) { break; }; # SMS expects only the numbers as follows + if (!(uri=~"sip:+[^0][0-9]{3}")) { log("SER: invalid number format!!\n"); if (!t_reply("502","Bad gateway - invalid number")) { # if replying failed, retry statelessly sl_reply_error(); }; break; }; if (sms_send_msg_to_net("D1")) { # for sending replies, we woun't use the statefull # function because it is not able to add the Contact # header (in fact to add any rpl_lump :-( ) # things went well, send ok upstream if (!t_reply("202", "yes sir, SMS sent over")) { # if replying failed, retry statelessly sl_reply_error(); }; } else { if (!t_reply("502", "Bad gateway - SMS error")) { # if replying failed, retry statelessly sl_reply_error(); }; break; }; } kamailio-4.0.4/etc/sip-router.cfg.m40000644000000000000000000004620612223032460015671 0ustar rootroot### m4 macros to make the configuration easier include(`rules.m4') define(`SER_IP', `192.168.0.1') define(`SER_HOSTNAME', `foo.bar') define(`GW_IP_1', `192.168.0.2') define(`GW_IP_2', `192.168.0.3') declare(flags, ACC_FLAG, MISSED_FLAG, VM_FLAG, NAT_FLAG) declare(route, PSTN_ROUTE, NAT_ROUTE, VOICEMAIL_ROUTE, PSTN2_ROUTE) declare(onreply, NAT_REPLY) declare(failure, PSTN_FAILURE, _1_FAILURE) ### End of m4 macro section # # $Id$ # # sip-router.cfg m4 template # # # Set the following in your CISCO PSTN gateway: # sip-ua # nat symmetric role passive # nat symmetric check-media-src # fork=yes port=5060 log_stderror=no fifo="/tmp/sip-router_fifo" # uncomment to enter testing mode /* fork=no port=5064 log_stderror=yes fifo="/tmp/sip-router_fifox" */ debug=3 memlog=4 # memlog set high (>debug) -- no final time-consuming memory reports on exit mhomed=yes listen=SER_IP alias="SER_HOSTNAME" check_via=yes dns=yes rev_dns=no children=16 # if changing fifo mode to a more restrictive value, put # decimal value in there, e.g. dec(rw|rw|rw)=dec(666)=438 fifo_mode=0666 loadmodule "/usr/local/lib/sip-router/modules/tm.so" loadmodule "/usr/local/lib/sip-router/modules/sl.so" loadmodule "/usr/local/lib/sip-router/modules/acc.so" loadmodule "/usr/local/lib/sip-router/modules/rr.so" loadmodule "/usr/local/lib/sip-router/modules/maxfwd.so" loadmodule "/usr/local/lib/sip-router/modules/mysql.so" loadmodule "/usr/local/lib/sip-router/modules/usrloc.so" loadmodule "/usr/local/lib/sip-router/modules/registrar.so" loadmodule "/usr/local/lib/sip-router/modules/auth.so" loadmodule "/usr/local/lib/sip-router/modules/auth_db.so" loadmodule "/usr/local/lib/sip-router/modules/textops.so" loadmodule "/usr/local/lib/sip-router/modules/uri.so" loadmodule "/usr/local/lib/sip-router/modules/group.so" loadmodule "/usr/local/lib/sip-router/modules/msilo.so" loadmodule "/usr/local/lib/sip-router/modules/nathelper.so" loadmodule "/usr/local/lib/sip-router/modules/enum.so" loadmodule "/usr/local/lib/sip-router/modules/domain.so" #loadmodule "/usr/local/lib/sip-router/modules/permissions.so" modparam("usrloc|acc|auth_db|group|msilo", "db_url", "sql://sip-router:heslo@localhost/sip-router") # -- usrloc params -- /* 0 -- dont use mysql, 1 -- write_through, 2--write_back */ modparam("usrloc", "db_mode", 2) modparam("usrloc", "timer_interval", 10) # -- auth params -- modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "plain_password_column", "password") #modparam("auth_db", "use_rpid", 1) modparam("auth", "nonce_expire", 300) modparam("auth", "rpid_prefix", ";party=calling;id-type=subscriber;screen=yes;privacy=off") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # -- acc params -- # report ACKs too for sake of completeness -- as we account PSTN # destinations which are RR, ACKs should show up modparam("acc", "report_ack", 1) modparam("acc", "log_level", 1) # if BYE fails (telephone is dead, record-routing broken, etc.), generate # a report nevertheless -- otherwise we would have no STOP event; => 1 modparam("acc", "failed_transactions", 1) # that is the flag for which we will account -- don't forget to # set the same one :-) # Usage of flags is as follows: # 1 == should account(all to gateway), # 3 == should report on missed calls (transactions to iptel.org's users), # 4 == destination user wishes to use voicemail # 6 == nathelper # modparam("acc", "log_flag", ACC_FLAG) modparam("acc", "db_flag", ACC_FLAG) modparam("acc", "log_missed_flag", MISSED_FLAG) modparam("acc", "db_missed_flag", MISSED_FLAG) # report to syslog: From, i-uri, status, digest id, method modparam("acc", "log_fmt", "fisum") # -- tm params -- modparam("tm", "fr_timer", 20) modparam("tm", "fr_inv_timer", 90) modparam("tm", "wt_timer", 20) # -- msilo params modparam("msilo", "registrar", "sip:registrar@SER_HOSTNAME") # -- enum params -- modparam("enum", "domain_suffix", "e164.arpa.") # -- multi-domain modparam("domain", "db_mode", 1) # NAT features turned off -- smartnat available only in nat-capable release # We will you flag 6 to mark NATed contacts modparam("registrar", "nat_flag", NAT_FLAG) # Enable NAT pinging modparam("nathelper", "natping_interval", 15) # Ping only contacts that are known to be behind NAT modparam("nathelper", "ping_nated_only", 1) # --------------------- request routing logic ------------------- route { if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483", "Alas Too Many Hops"); break; }; if (msg:len >= max_len) { sl_send_reply("513", "Message too large"); break; }; # special handling for natted clients; first, nat test is # executed: it looks for via!=received and RFC1918 addresses # in Contact (may fail if line-folding used); also, # the received test should, if complete, should check all # vias for presence of received if (nat_uac_test("3")) { # allow RR-ed requests, as these may indicate that # a NAT-enabled proxy takes care of it; unless it is # a REGISTER if (method == "REGISTER" || !search("^Record-Route:")) { log("LOG: Someone trying to register from private IP, rewriting\n"); # This will work only for user agents that support symmetric # communication. We tested quite many of them and majority is # smart smart enough to be symmetric. In some phones, like # it takes a configuration option. With Cisco 7960, it is # called NAT_Enable=Yes, with kphone it is called # "symmetric media" and "symmetric signaling". (The latter # not part of public released yet.) fix_nated_contact(); # Rewrite contact with source IP of signalling if (method == "INVITE") { fix_nated_sdp("1"); # Add direction=active to SDP }; force_rport(); # Add rport parameter to topmost Via setflag(NAT_FLAG); # Mark as NATed append_to_reply("P-NATed-Caller: Yes\r\n"); }; }; # anti-spam -- if somene claims to belong to our domain in From, # challenge him (skip REGISTERs -- we will chalenge them later) if (search("(From|F):.*@SER_HOST_REGEX")) { # invites forwarded to other domains, like FWD may cause subsequent # request to come from there but have iptel in From -> verify # only INVITEs (ignore FIFO/UAC's requests, i.e. src_ip==fox) if ((method == "INVITE" || method == "SUBSCRIBE") && !(FROM_MYSELF || FROM_GW)) { if (!(proxy_authorize("DIGEST_REALM", "subscriber"))) { proxy_challenge("DIGEST_REALM", "0"); break; }; # to maintain outside credibility of our proxy, we enforce # username in From to equal digest username; user with # "john.doe" id could advertise "bill.gates" in From otherwise; if (!check_from()) { log("LOG: From Cheating attempt in INVITE\n"); sl_send_reply("403", "That is ugly -- use From=id next time (OB)"); break; }; # we better don't consume credentials -- some requests may be # spiraled through our server (sfo@iptel->7141@iptel) and the # subsequent iteration may challenge too, for example because of # iptel claim in From; UACs then give up because they # already submitted credentials for the given realm #consume_credentials(); }; # non-REGISTER from other domain } else if ((method == "INVITE" || method == "SUBSCRIBE" || method=="REGISTER" ) && !(uri == myself || uri =~ "TO_GW")) { # and we serve our gateway too (we RR requests to it, so that # its address may show up in subsequent requests after loose_route sl_send_reply("403", "No relaying"); break; }; # By default we record route everything except REGISTERs if (!(method=="REGISTER")) record_route(); # if route forces us to forward to some explicit destination, do so # # loose_route returns true in case that a request included # route header fields instructing SER where to relay a request; # if that is the case, stop script processing and just forward there; # one could alternatively ignore the return value and treat the # request as if it was an outbound one; that would not work however # with broken UAs which strip RR parameters from Route. (What happens # is that with two RR /tcp2udp, spirals, etc./ and stripped parameters, # SER a) rewrites r-uri with RR1 b) matches uri==myself against RR1 # c) applies mistakenly user-lookup to RR1 in r-uri if (loose_route()) { # check if someone has not introduced a pre-loaded INVITE -- if so, # verify caller's privileges before accepting rr-ing if ((method=="INVITE" || method=="ACK" || method=="CANCEL") && uri =~ "TO_GW") { route(PSTN_ROUTE); # Forward to PSTN gateway } else { append_hf("P-hint: rr-enforced\r\n"); # account all BYEs if (method=="BYE") setflag(ACC_FLAG); route(NAT_ROUTE); # Generic forward }; break; }; # ------- check for requests targeted out of our domain... ------- if (!(uri == myself || uri =~ "TO_GW")) { # ... and we serve our gateway too (we RR requests to it, so that # its address may show up in subsequent requests after # rewriteFromRoute append_hf("P-hint: OUTBOUND\r\n"); route(NAT_ROUTE); break; }; # ------- now, the request is for sure for our domain ----------- # registers always MUST be authenticated to # avoid stealing incoming calls if (method == "REGISTER") { /* if (!allow_register("register.allow", "register.deny")) { log(1, "LOG: alert: Forbidden IP in Contact\n"); sl_send_reply("403", "Forbidden"); break; }; */ # prohibit attempts to grab someone else's To address # using valid credentials; if (!www_authorize("DIGEST_REALM", "subscriber")) { # challenge if none or invalid credentials www_challenge("DIGEST_REALM", "0"); break; }; if (!check_to()) { log("LOG: To Cheating attempt\n"); sl_send_reply("403", "That is ugly -- use To=id in REGISTERs"); break; }; # it is an authenticated request, update Contact database now if (!save("location")) { sl_reply_error(); }; m_dump(); break; }; # some UACs might be fooled by Contacts our UACs generate to make MSN # happy (web-im, e.g.) -- tell its urneachable if (uri =~ "sip:daemon@") { sl_send_reply("410", "Daemon is gone"); break; }; # aliases # note: through a temporary error in provisioning interface, there # are now aliases 905xx ... they take precedence overy any PSTN numbers # as they are resolved first lookup("aliases"); # check again, if it is still for our domain after aliases if (!(uri == myself || uri =~ "TO_GW")) { append_hf("P-hint: ALIASED-OUTBOUND\r\n"); route(NAT_ROUTE); break; }; # Remove leading + if it is a number begining with + if (uri =~ "^[a-zA-Z]+:\+[0-9]+@") { strip(1); prefix("00"); }; if (!does_uri_exist()) { # Try numeric destinations through the gateway if (uri =~ "^[a-zA-Z]+:[0-9]+@") { route(PSTN_ROUTE); } else { sl_send_reply("604", "Does Not Exist Anywhere"); }; break; }; # does the user wish redirection on no availability? (i.e., is he # in the voicemail group?) -- determine it now and store it in # flag 4, before we rewrite the flag using UsrLoc if (is_user_in("Request-URI", "voicemail")) { setflag(VM_FLAG); }; # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { # handle user which was not found route(VOICEMAIL_ROUTE); break; }; # check whether some inventive user has uploaded gateway # contacts to UsrLoc to bypass our authorization logic if (uri =~ "TO_GW") { log(1, "LOG: Weird! Gateway address in UsrLoc!\n"); route(PSTN_ROUTE); break; }; # if user is on-line and is in voicemail group, enable redirection /* no voicemail currently activated if (method == "INVITE" && isflagset(VM_FLAG)) { t_on_failure(_1_FAILURE); # failure_route() not defined }; */ # ... and also report on missed calls ... note that reporting # on missed calls is mutually exclusive with silent C timer setflag(MISSED_FLAG); # we now know we may, we know where, let it go out now! append_hf("P-hint: USRLOC\r\n"); route(NAT_ROUTE); } # # Forcing media relay if necesarry # route[NAT_ROUTE] { if (uri=~"[@:](192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)" && !search("^Route:")) { sl_send_reply("479", "We don't forward to private IP addresses"); break; }; if (isflagset(NAT_FLAG)) { if (!is_present_hf("P-RTP-Proxy")) { force_rtp_proxy(); append_hf("P-RTP-Proxy: YES\r\n"); }; append_hf("P-NATed-Calee: Yes\r\n"); }; # nat processing of replies; apply to all transactions (for example, # re-INVITEs from public to private UA are hard to identify as # natted at the moment of request processing); look at replies t_on_reply(NAT_REPLY); if (!t_relay()) { sl_reply_error(); break; }; } onreply_route[NAT_REPLY] { # natted transaction ? if (isflagset(NAT_FLAG) && status =~ "(183)|2[0-9][0-9]") { fix_nated_contact(); force_rtp_proxy(); # otherwise, is it a transaction behind a NAT and we did not # know at time of request processing? (RFC1918 contacts) } else if (nat_uac_test("1")) { fix_nated_contact(); }; # keep Cisco gateway sending keep-alives if (isflagset(7) && status=~"2[0-9][0-9]") { # flag(7) is mentioned NAT_FLAG ?? remove_hf("Session-Expires"); append_hf("Session-Expires: 60;refresher=UAC\r\n"); fix_nated_sdp("1"); }; } # # logic for calls to the PSTN # route[PSTN_ROUTE] { # discard non-PSTN methods if (!(method == "INVITE" || method == "ACK" || method == "CANCEL" || method == "OPTIONS" || method == "BYE")) { sl_send_reply("500", "only VoIP methods accepted for GW"); break; }; # turn accounting on setflag(ACC_FLAG); # continue with requests to PSTN gateway ... # no authentication needed if the destination is on our free-pstn # list or if the caller is the digest-less gateway # # apply ACLs only to INVITEs -- we don't need to protect other # requests, as they don't imply charges; also it could cause troubles # when a call comes in via PSTN and goes to a party that can't # authenticate (voicemail, other domain) -- BYEs would fail then if (method == "INVITE") { if (!is_user_in("Request-URI", "free-pstn")) { if (!proxy_authorize("DIGEST_REALM", "subscriber")) { proxy_challenge("DIGEST_REALM", "0"); break; }; # let's check from=id ... avoids accounting confusion if (!check_from()) { log("LOG: From Cheating attempt\n"); sl_send_reply("403", "That is ugly -- use From=id next time (gw)"); break; }; } else { # Allow free-pstn destinations without any checks route(PSTN2_ROUTE); break; }; if (uri =~ "^sip:00[1-9][0-9]+@") { if (!is_user_in("credentials", "int")) { sl_send_reply("403", "International numbers not allowed"); break; }; route(PSTN2_ROUTE); } else { sl_send_reply("403", "Invalid Number"); break; }; }; # authorized PSTN break; } route[PSTN2_ROUTE] { rewritehostport("GW_IP_1:5060"); consume_credentials(); append_hf("P-Hint: GATEWAY\r\n"); # Try alternative gateway on failure t_on_failure(PSTN_FAILURE); # Our PSTN gateway is symmetric and can handle direction=active flag # properly, therefore we don't have to use RTP proxy t_relay(); } failure_route[PSTN_FAILURE] { rewritehostport("GW_IP_2:5060"); append_branch(); t_relay(); } # ------------- handling of unavailable user ------------------ route[VOICEMAIL_ROUTE] { # message store if (method == "MESSAGE") { if (!t_newtran()) { sl_reply_error(); break; }; if (m_store("0")) { t_reply("202", "Accepted for Later Delivery"); break; }; t_reply("503", "Service Unavailable"); break; }; # non-Voip -- just send "off-line" if (!(method == "INVITE" || method == "ACK" || method == "CANCEL")) { sl_send_reply("404", "Not Found"); break; }; if (t_newtran()) { if (method == "ACK") { log(1, "CAUTION: strange thing: ACK passed t_newtran\n"); break; }; t_reply("404", "Not Found"); }; # we account missed incoming calls; previous statteful processing # guarantees that retransmissions are not accounted if (method == "INVITE") { acc_log_request("404 missed call\n"); acc_db_request("404 missed call", "missed_calls"); }; } kamailio-4.0.4/etc/dictionary.kamailio0000644000000000000000000000332112223032460016424 0ustar rootroot# # $Id$ # # SIP RADIUS attributes # # Proprietary indicates an attribute that hasn't # been standardized # # # NOTE: All standard (IANA registered) attributes are # defined in the default dictionary of the # radiusclient-ng library. # #### Attributes ### ATTRIBUTE Sip-Uri-User 208 string # Proprietary, auth_radius ATTRIBUTE Sip-Group 211 string # Proprietary, group_radius ATTRIBUTE Sip-Rpid 213 string # Proprietary, auth_radius ATTRIBUTE SIP-AVP 225 string # Proprietary, avp_radius ### Acct-Status-Type Values ### #VALUE Acct-Status-Type Failed 15 # RFC2866, acc ### Service-Type Values ### #VALUE Service-Type Call-Check 10 # RFC2865, uri_radius VALUE Service-Type Group-Check 12 # Proprietary, group_radius ##VALUE Service-Type Sip-Session 15 # Schulzrinne, acc, auth_radius VALUE Service-Type SIP-Caller-AVPs 30 # Proprietary, avp_radius VALUE Service-Type SIP-Callee-AVPs 31 # Proprietary, avp_radius ### Sip-Method Values ### VALUE Sip-Method Undefined 0 VALUE Sip-Method Invite 1 VALUE Sip-Method Cancel 2 VALUE Sip-Method Ack 4 VALUE Sip-Method Bye 8 VALUE Sip-Method Info 16 VALUE Sip-Method Options 32 VALUE Sip-Method Update 64 VALUE Sip-Method Register 128 VALUE Sip-Method Message 256 VALUE Sip-Method Subscribe 512 VALUE Sip-Method Notify 1024 VALUE Sip-Method Prack 2048 VALUE Sip-Method Refer 4096 VALUE Sip-Method Other 8192 kamailio-4.0.4/etc/sr0000755000000000000000000000400712223032460013123 0ustar rootroot#!/bin/sh # # $Id$ # # # 3w-xxxx: Starts the sip_router process # # Version: @(#) /etc/rc.d/init.d/3w-xxxx # # chkconfig: 2345 20 80 # description: controls execution of SIP router # processname: sr # config: /etc/sip-router/iptel.cfg # Source function library. . /etc/rc.d/init.d/functions # we use a sip-router symlink -- that allows us to have a different name # in process table so that killalls does not start other sip-routers # run from somewhere else BINNAME=sr HM=/home/srouter SERDIR=$HM/sip_router ETC=/etc/sip-router/iptel.cfg PIDFILE=/var/run/sr.pid NOTIFY=sr@iptel.org USR=510 GRP=510 MONIT=/usr/local/bin/monit MONITRC=/usr/local/etc/monitrc RETVAL=0 BIN=$HM/bin/$BINNAME MYDIR=$HM/core CORE=$MYDIR/core TMP=/tmp/srcore.$$ sip-router_start() { if [ -r $BIN -a -r $CORE ] ; then echo "before startup sip-router core found on `date` at $HOSTNAME" > $TMP echo "----------------------------------" >> $TMP DATE=`date "+%Y-%m-%d--%H-%M"` NEWCORE=$MYDIR/core.$DATE mv $CORE $NEWCORE echo core stored in $NEWCORE >> $TMP gdb $BIN $NEWCORE -x test/bt.gdb -batch >> $TMP chmod a+r $NEWCORE cd $SERDIR; tar czf $MYDIR/sip-router.$DATE.tgz . mail -s "sip-router core found" $NOTIFY < $TMP rm -f $TMP fi cd $MYDIR #ulimit -c 1000000 echo "Starting SIP router: " $BIN -f $ETC -w $MYDIR -P $PIDFILE RETVAL=$? echo } sip-router_stop() { echo "Stopping SIP router: " killproc $BINNAME RETVAL=$? echo } monit_start() { echo "Command Monit to start Ser..." ${MONIT} -c ${MONITRC} start sip-router RETVAL=$? echo } monit_stop() { echo "Command Monit to stop Ser..." ${MONIT} -c ${MONITRC} stop sip-router RETVAL=$? echo } # See how we were called. case "$1" in serstart) sip-router_start ;; sip-routerstop) sip-router_stop ;; sip-routerrestart) sip-router_stop echo sip-router_start ;; start) monit_start ;; stop) monit_stop ;; restart) monit_stop sleep 1 monit_start ;; *) echo "Usage: $0 {start|stop|restart}" exit 1 esac exit $RETVAL kamailio-4.0.4/etc/obsoleted/0000755000000000000000000000000012223032460014530 5ustar rootrootkamailio-4.0.4/etc/obsoleted/backup.cfg0000644000000000000000000001112412223032460016455 0ustar rootroot# # $Id$ # # iptel.org real world configuration # # ----------- global configuration parameters ------------------------ debug=4 # debug level (cmd line: -dddddddddd) fork=no #log_stderror=no # (cmd line: -E) log_stderror=yes # (cmd line: -E) #check_via=yes # (cmd. line: -v) #check_via=0 dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) port=5069 #port=8060 children=1 # advertise IP address in Via (as opposed to advertising DNS name # which is annoying for downstream servers and some phones can # not handle DNS at all) listen=195.37.77.100 #listen=bat.iptel.org # ------------------ module loading ---------------------------------- loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/print/print.so" #loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/acc/acc.so" loadmodule "../sip_router/modules/rr/rr.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/mysql/mysql.so" loadmodule "../sip_router/modules/usrloc/usrloc.so" loadmodule "../sip_router/modules/auth/auth.so" loadmodule "../sip_router/modules/cpl/cpl.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- modparam("usrloc", "use_database", 1) modparam("usrloc", "table", "location") modparam("usrloc", "user_column", "user") modparam("usrloc", "contact_column", "contact") modparam("usrloc", "expires_column", "expires") modparam("usrloc", "q_column", "q") modparam("usrloc", "callid_column", "callid") modparam("usrloc", "cseq_column", "cseq") modparam("usrloc", "flush_interval", 60) modparam("usrloc", "db_url", "sql://root:@localhost/ser") # -- auth params -- modparam("auth", "db_url", "sql://root:@localhost/ser") modparam("auth", "user_column", "user") # nonce generation secret; particularly useful if multiple servers # in a proxy farm are configured to authenticate modparam("auth", "secret", "439tg8h349g8hq349t9384hg") # calculate_ha1=false means password column includes ha1 strings; # if it was false, plain-text passwords would be assumed # the database credentials in hashed form modparam("auth", "calculate_ha1", false) modparam("auth", "password_column", "ha1") # password_column, realm_column, group_table, group_user_column, # group_group_column are set to their default values # password_column_2 allows to deal with clients who put domain name # in authentication credentials when calculate_ha1=false (if true, # it works); if set to a value and USER_DOMAIN_HACK was enabled # in defs.h, authentication will still work modparam("auth", "password_column_2", "ha1b") # the database in plain-text alternative: #modparam("auth", "calculate_ha1", true ) #modparam("auth", "password_column", "password") modparam("auth", "nonce_expire", 300) modparam("auth", "retry_count", 3) # -- acc params -- # report ACKs too for sake of completeness -- as we account PSTN # destinations which are RR, ACKs should show up modparam("acc", "report_ack", 1) # don't bother me with early media reports (I don't like 183 # too much anyway...ever thought of timer C hitting after # listening to music-on-hold for five minutes?) modparam("acc", "early_media", 0) modparam("acc", "log_level", 1) # that is the flag for which we will account -- don't forget to # set the same one :-) modparam("acc", "acc_flag", 1 ) # we are interested only in succesful transactions modparam("acc", "failed_transactions", 0 ) # -- tm params -- modparam("tm", "fr_timer", 30 ) modparam("tm", "fr_inv_timer", 60 ) # ------------------------- request routing logic ------------------- # main routing logic route{ # filter local stateless ACK generated by authentication of mf replies sl_filter_ACK(); # filter too old messages log("LOG: Checking maxfwd\n"); if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; if (method=="REGISTER") { log("LOG Request is REGISTER\n"); if (!www_authorize( "bat.iptel.org" /* realm */, "subscriber" /* table name */ )) { log("LOG: REGISTER has no credentials, sending challenge\n"); www_challenge( "bat.iptel.org" /* realm */, "0" /* no qop -- M$ can't deal with it */); break; }; # prohibit attempts to grab someone else's To address # using valid credentials if (!is_user("replicator")) { log("LOG: To Cheating attempt\n"); sl_send_reply("403", "That is ugly -- use To=id next time"); break; }; # update Contact database log("LOG: REGISTER is authorized, saving location\n"); save_contact("location"); break; }; } kamailio-4.0.4/etc/obsoleted/sercheck0000755000000000000000000000251212223032460016245 0ustar rootroot#!/bin/sh # # $Id$ # # use to check whether all ser processes are running; if not, restart # and issue an alert; run from cron daemon for best results :-) # NOTIFY=sr@iptel.org SERHOME=/home/srouter export SERDIR=$SERHOME/sip_router BIN=$SERDIR/ser COREDIR=$SERHOME/core CORE=$COREDIR/core ETC=/etc/sr.cfg PROCCNT=`ps -C sr --no-headers -o pid | wc -l` CH=`grep "^children" $ETC | awk -F= ' { print $2 } '` ALL=`expr $CH + 1` TMP=/tmp/seralert.$$ if [ $PROCCNT -ne $ALL ] ; then # try again first -- it might have been a temporary # failure during 'sr restart' # two seconds longer failure is not too bad...UAs will # just retransmit and user are used to longer call # set-up times from GSM... sleep 2 PROCCNT=`ps -C sr --no-headers -o pid | wc -l` fi if [ $PROCCNT -ne $ALL ] ; then cd $SERDIR echo "Alarm: ser restart occured on `date` at $HOSTNAME" > $TMP if [ -r $BIN -a -r $CORE ] ; then echo "----------------------------------" >> $TMP DATE=`date "+%Y-%m-%d--%H-%M"` NEWCORE=$COREDIR/core.$DATE mv $CORE $NEWCORE echo core stored in $NEWCORE >> $TMP gdb $BIN $NEWCORE -x test/bt.gdb -batch >> $TMP chmod a+r $NEWCORE ( cd $SERDIR; tar cf - . ) | gzip > $COREDIR/ser.$DATE.tgz else echo "no core found" >> $TMP fi /etc/init.d/sr restart mail -s "ser restart occured" $NOTIFY < $TMP rm -f $TMP fi kamailio-4.0.4/etc/obsoleted/secondary.cfg0000644000000000000000000001676612223032460017220 0ustar rootroot# # iptel.org real world configuration for secondary host # # $Id$ # debug=3 # debug level (cmd line: -dddddddddd) #fork=yes fork=no #log_stderror=no # (cmd line: -E) log_stderror=yes # (cmd line: -E) check_via=yes # (cmd. line: -v) dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) port=5060 children=1 # advertise IP address in Via (as opposed to advertising DNS name # which is annoying for downstream servers and some phones can # not handle DNS at all) listen=195.37.77.100 loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/print/print.so" loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/acc/acc.so" loadmodule "../sip_router/modules/rr/rr.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/mysql/mysql.so" loadmodule "../sip_router/modules/usrloc/usrloc.so" loadmodule "../sip_router/modules/auth/auth.so" loadmodule "../sip_router/modules/cpl/cpl.so" modparam("usrloc", "use_database", 1) modparam("usrloc", "use_database", 0) modparam("usrloc", "table", "location") modparam("usrloc", "user_column", "user") modparam("usrloc", "contact_column", "contact") modparam("usrloc", "expires_column", "expires") modparam("usrloc", "q_column", "q") modparam("usrloc", "callid_column", "callid") modparam("usrloc", "cseq_column", "cseq") modparam("usrloc", "flush_interval", 60) modparam("usrloc", "db_url", "sql://csps:47csps11@dbhost/csps107") modparam("auth", "db_url", "sql://csps:47csps11@dbhost/csps107") modparam("auth", "table", "subscriber") modparam("auth", "user_column", "user") route{ # filter local stateless ACK generated by authentication of mf replies sl_filter_ACK(); # filter too old messages log("Checking maxfwd\n"); if (!mf_process_maxfwd_header("10")) { log("Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; # Do strict routing if route headers present rewriteFromRoute(); # divert voicemail requests if (uri=~"mail\.iptel\.org" | uri=~":5066") { log("Request is for voicemail\n"); sethost("iptel.org"); t_relay_to("fox.iptel.org", "5066"); break; }; # process requests for iptel.org (note the wildcard in # the regex end -- that captures URIs which have port # numbers or parameters in them); also include gateway # here too -- we RR to the gateway which means that its # address shows up in d-uri of subsequent requests after # having called rewriteFromRoute and we want the requests # to go through our PSTN authorization code if (uri=~"[@:]iptel\.org([;:].*)*" | uri=~"[@:]195\.37\.77\.101([;:].*)*" | uri=~"@195\.37\.77\.110([;:].*)*" ) { log("Request is for iptel.org\n"); # registers always MUST be authenticated to # avoid stealing incoming calls if (method=="REGISTER") { log("Request is REGISTER\n"); if (!authorize("iptel.org")) { log("REGISTER has no credentials, sending challenge\n"); challenge("iptel.org", "noqop"); break; }; # prohibit attempts to grab someone else's To address # using valid credentials if (!check_to()) { log("Cheating attempt\n"); sl_send_reply("403", "What a nasty guy you are"); break; }; # update Contact database log("REGISTER is authorized, saving location\n"); save_contact("location"); break; }; # various aliases (might use a database in future) if (uri=~"sip:9040@.*") { setuser("jiri"); }; # special measure for our US friends if (uri=~"sip:17@") { seturi("sip:henry@siptest.wcom.com"); }; # if (uri=~"sip:jiri@.*" & method=="INVITE") { # t_fork_to("001795061546@195.37.77.110"); # }; # if (uri=~"sip:jiri@.*" & method=="INVITE") { # t_fork_to("195.37.77.110", "5060"); # }; # now it's about PSTN destinations through our gateway; # note that 8.... is exempted for numerical destinations if (uri=~"sip:[0-79][0-9]*@.*") { # label this transaction to be accounted t_setflag("acc"); # free call destinations ... no authentication needed if (uri=~"sip:001795061546@.*" | uri=~"sip:0016097265544.*" | uri=~"sip:[79][0-9][0-9][0-9]@.*") { log("Free PSTN\n"); } else { # all other PSTN destinations only for authenticated users # (Cisco GW, which has no digest support, is authenticated # by its IP address -- that's for sure not very strong; # wth confirmed that we filter packets coming from outside # and bearing SRC IP address of a Fokus network) if (!(src_ip==195.37.77.110) & !(authorize("iptel.org"))) { challenge("iptel.org", "noqop"); break; }; # authorize only for INVITEs -- RR/Contact may result in weird # things showing up in d-uri that would break our logic; our # major concern is INVITE which causes PSTN costs anyway if (method=="INVITE") { # does the authenticated user have a permission for local # calls? (i.e., is he in the "local" group?) if (uri=~"sip:0[1-9][0-9]+@.*") { if (!is_in_group("local")) { sl_send_reply("403", "Local Toodle Noodle..."); break; }; # the same for long-distance } else if (uri=~"sip:00[1-9][0-9]+@.*") { if (uri=~"sip:001[089]" | uri=~"sip:00900.*" ) { sl_send_reply("403", "Added Value Destinations not permitted..."); break; }; if (!is_in_group("ld")) { sl_send_reply("403", "LD Toodle Noodle..."); break; }; # the same for international calls } else if (uri=~"sip:000[1-9][0-9]+@.*") { if (!is_in_group("int")) { sl_send_reply("403", "International Toodle Noodle..."); break; }; # everything else (e.g., interplanetary calls) is denied } else { sl_send_reply("403", "interplanetary Toodle Noodle..."); break; }; }; ; }; # requests to gateway must be record-route because the GW accepts # only reqeusts coming from our proxy if (method=="INVITE") addRecordRoute(); # if you have passed through all the checks, let your call go to GW! rewritehostport("195.37.77.110:5060"); } else { /* added by Bogdan for cpl demo - Dorgham request*/ if (uri=~"sip:test@.*" && method=="INVITE") { log("SER : runing CPL!! :)\n"); if ( !cpl_run_script() ) { log("SER : Error during running CPL script!\n"); }else{ if ( cpl_is_response_reject() ) { log("SER: reject"); sl_send_reply("603","I am not available!"); break; }else if ( cpl_is_response_redirect() ) { log("SER : redirect\n"); cpl_update_contact(); sl_send_reply("302","Moved temporarily"); break; }; }; }; # native SIP destinations are handled using our USRLOC DB if (!lookup_contact("location")) { log("Unable to lookup contact, sending 404\n"); sl_send_reply("404", "Not Found"); break; }; # requests from gateway should be RR-ed too if (src_ip==195.37.77.110 && method=="INVITE") { addRecordRoute(); }; }; } else { # outbound requests are allowed only for our users -- we don't # support relaying and don't like strangers bothering us # with resolving DNS log("that's a request to outside"); if (!(src_ip==195.37.77.110) & !(authorize("iptel.org"))) { challenge("iptel.org", "noqop"); break; }; # there should be check_from here too -- but I'm to tired # to test it tonight }; # we now know we may, we now where, let it go out now! t_relay(); } kamailio-4.0.4/etc/obsoleted/sms.cfg0000644000000000000000000000434012223032460016014 0ustar rootroot# # iptel.org real world configuration # # $Id$ # # ----------- global configuration parameters ------------ debug=4 # debug level (cmd line: -dddddddddd) #fork=yes fork=no #log_stderror=no # (cmd line: -E) log_stderror=yes # (cmd line: -E) check_via=yes # (cmd. line: -v) dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) port=5060 children=1 # advertise IP address in Via (as opposed to advertising DNS name # which is annoying for downstream servers and some phones can # not handle DNS at all) listen=195.37.77.100 # ------------------ module loading ---------------- loadmodule "../sip_router/modules/print/print.so" #loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" # ----------------- setting module-specific parameters ------- # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 10 ) modparam("tm", "wt_timer", 10 ) route{ # filter local stateless ACK generated by authentication of mf replies sl_filter_ACK(); if (len_gt( max_len )) { # if (len_gt( 100 )) { sl_send_reply("513", "Riesengross -- Message too large"); log("XXX Riessengross: dropped\n"); break; }; # filter too old messages # log("LOG: Checking maxfwd\n"); # if (!mf_process_maxfwd_header("0")) { # log("LOG: Too many hops\n"); # sl_send_reply("483","Too Many Hops"); # break; # }; # UAS script implementation # if that is not a new transaction... (t_newtran is a new # function which atomicaly adds a transaction if there is # none) if (! t_newtran()) { # retransmit whatever we have t_retransmit_reply(); } else { # do what you want to do # if (send_sms()) { # this is just a thing which stands for sth real if (len_gt( max_len )) { # things went well, send ok upstream if (!t_send_reply("200", "yes sir, SMS sent over")) { # if replying failed, retry statelessly sl_reply_error(); }; } else { if (!t_send_reply("500", "SMS error :-(")) { # if replying failed, retry statelessly sl_reply_error(); }; }; # transaction conclude it -- junk it now (it will # stay there until WAIT timer hits) t_release(); }; t_unref(); } kamailio-4.0.4/etc/obsoleted/use_jabs.cfg0000644000000000000000000000351112223032460017004 0ustar rootroot# # configuration for Jabber module testing # # $ID: daniel $ # debug=3 # debug level (cmd line: -dddddddddd) fork=yes # (cmd. line: -D) #fork=no #log_stderror=yes # (cmd line: -E) log_stderror=no # (cmd line: -E) children=2 check_via=no # (cmd. line: -v) dns=off # (cmd. line: -r) rev_dns=off # (cmd. line: -R) port=5080 #listen=10.0.0.179 #listen=192.168.57.33 loop_checks=0 # for more info: sip_router -h #modules loadmodule "modules/print/print.so" loadmodule "modules/textops/textops.so" loadmodule "modules/tm/tm.so" #loadmodule "modules/rr/rr.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/sl/sl.so" loadmodule "modules/mysql/mysql.so" #loadmodule "modules/dbexample/dbexample.so" loadmodule "modules/jabber/jabber.so" #loadmodule "modules/cpl/cpl.so" #loadmodule "modules/pike/pike.so" modparam("jabber","contact","sip:bat.iptel.org:5080") modparam("jabber","db_url","sql://s2jgw:47s2jgw11@127.0.0.1/sip_jab") modparam("jabber","jaddress","bat.iptel.org") modparam("jabber","jport",5222) modparam("jabber","workers",2) modparam("jabber","max_jobs",10) #modparam("pike","timeout",30) route{ sl_filter_ACK(); if ( !mf_process_maxfwd_header("10") ) { sl_send_reply("483","To Many Hops"); drop(); }; if ((search("To:.*@icq\.iptel\.org")) || (search("To:.*@icq\.bat\.iptel\.org")) || (search("To:.*@sms\.bat\.iptel\.org")) || (search("To:.*@msn\.iptel\.org")) || (search("To:.*@msn\.bat\.iptel\.org"))) { if (method=="MESSAGE") { log("MESSAGE received -> sending as JABBER\n"); if (jab_send_message()) { sl_send_reply("202","Accepted"); }else{ sl_send_reply("502","Bad gateway"); }; }else{ log("NON_Message request received for JABBER gateway->dropt!\n"); sl_send_reply("501","Not implemented"); }; break; }; forward(uri:host,uri:port); } kamailio-4.0.4/etc/obsoleted/mobile66.cfg0000644000000000000000000000417512223032460016643 0ustar rootroot# # $Id$ # # iptel.org real world configuration # # ----------- global configuration parameters ------------------------ debug=9 # debug level (cmd line: -dddddddddd) fork=yes children=2 #log_stderror=no # (cmd line: -E) log_stderror=yes # (cmd line: -E) check_via=yes # (cmd. line: -v) dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) port=5060 #listen=192.168.57.66 #listen=192.168.50.66 #listen=3ffe:400:190:50:201:2ff:fedd:5050 listen=3ffe:400:190:56:201:2ff:fedd:52b5 # ------------------ module loading ---------------------------------- loadmodule "modules/sl/sl.so" loadmodule "modules/print/print.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/usrloc/usrloc.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- modparam("usrloc", "use_database", 0) # ------------------------- request routing logic ------------------- # main routing logic route{ # filter local stateless ACK generated by authentication of mf replies sl_filter_ACK(); # filter too old messages log("LOG: Checking maxfwd\n"); if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; # sign of our domain: there is @ (username), : # (nothing) or . (host) in front of our domain name if (!(uri=~"[@:\.]lugduf-1\.mobis\.ip6([;:].*)*")) { route(2); # break from route (2) return -- stop then ! break; }; # here we continue with requests for our domain... if (method=="REGISTER") { log("LOG Request is REGISTER\n"); # update Contact database log("LOG: REGISTER -> saving location\n"); save_contact("location"); break; }; # native SIP destinations are handled using our USRLOC DB if (!lookup_contact("location")) { if (method=="ACK") { log("Ooops -- an ACK made it here -- probably UAC screwed up to-tags\n"); break; }; log("LOG: Unable to lookup contact, sending 404\n"); sl_send_reply("404", "Not Found"); break; }; forward(uri:host,uri:port); } # routing logic for outbound requests targeted out of our domain route[2] { forward("xhosa.mobis.ip6",5060); } kamailio-4.0.4/etc/obsoleted/sip2jabd0000755000000000000000000001100112223032460016145 0ustar rootroot#!/bin/sh # # 3w-xxxx: Starts the sip2jab process # # Version: @(#) /etc/rc.d/init.d/3w-xxxx # # chkconfig: 2345 20 85 # description: controls execution of SIP2Jabber gateway # processname: sip2jab # config: /etc/use_jabs.cfg # User "dcm" is used if is the script is run by root # JABBER JDIR="/home/dcm/jabber-1.4.2" JABBERD="${JDIR}/jabberd/jabberd" JICQEXT="${JDIR}/icqv7ext.sh" CONF="${JDIR}/conf/single.xml" JUID="dcm" JPID="${JDIR}/spool/jabbers.pid" JOUT="${JDIR}/spool/jabberd.log" # SER BIN=sip2jab HM=/home/dcm/sip_router ETC=$HM/etc/use_jabs.cfg USR="dcm" GRP="dcm" MYDIR=$HM/core CORE=$MYDIR/core RETVAL=0 ############ ==== ############# # Test if Jabber exists test -f ${JABBERD} -a -f ${CONF} || exit 0 # Source function library. . /etc/rc.d/init.d/functions # Get config. . /etc/sysconfig/network # Check that networking is up. [ "${NETWORKING}" = "no" ] && exit 0 # Current user CRT_USER=`whoami` ### Stop Jabber server stop_jab() { echo "Stopping ICQ transport - external component ..." killproc ${JICQEXT} # echo echo "Stopping Jabber server ..." killproc ${JABBERD} RETVAL=$? # echo # kill `cat ${JPID}` # echo " OK." } ### Start Jabber server start_jab() { if [ -f ${JPID} ] then echo "Cleaning Jabber from a previous dirty crash ..." stop_jab sleep 1 rm -f ${JPID} fi if [ `id -u` = 0 ] then #### Run by root echo "Starting Jabber server ..." su - ${JUID} -c "cd ${JDIR}; ${JABBERD} -B -c ${CONF} > /dev/null 2>&1" > /dev/null 2>&1 # echo sleep 2 echo "Starting ICQ transport - external component ..." su - ${JUID} -c "cd ${JDIR}; ${JICQEXT} > /dev/null 2>&1 &" > /dev/null 2>&1 RETVAL=$? # echo else ### Run by other users echo "Starting Jabber server ..." cd ${JDIR} ${JABBERD} -B -c ${CONF} > /dev/null 2>&1 # echo sleep 2 echo "Starting ICQ transport - external component ..." ${JICQEXT} > /dev/null 2>&1 & RETVAL=$? # echo fi sleep 1 ### Checking if processes are started if [ `ps auxw | grep ${JABBERD} | head --lines=1 | awk '{print $11}'` = ${JABBERD} ] then echo "Jabber server: [[ STARTED ]]" else echo "Jabber server: [[ NOT STARTED ]]" fi if [ `ps auxw | grep ${JICQEXT} | head --lines=1 | awk '{print $11}'` != "grep" ] then echo "ICQ transport: [[ STARTED ]]" else echo "ICQ transport: [[ NOT STARTED ]]" fi } ### Stop SER stop_ser() { echo -n "Stopping SIP router ..." killproc ${BIN} RETVAL=$? echo # killall ser # echo " [ OK ]" } ### Start SER start_ser() { echo "Starting SIP router ..." if [ `id -u` = 0 ] then #### Run by root su - ${USR} -c "cd ${HM}; ${HM}/${BIN} -f ${ETC} -w ${MYDIR}" > /dev/null 2>&1 RETVAL=$? else #### Run by other users #cd $MYDIR # core timestamping moved to sercheck; -jiri # if [ -f $CORE ] ; then # chmod a+r $CORE # DATE=`date "+%Y-%m-%d--%H-%M"` # mv $CORE $CORE.$DATE # ( cd ../sip_router; tar cf - . ) | gzip > ser.$DATE.tgz # fi cd ${HM} ${HM}/${BIN} -f ${ETC} -w ${MYDIR} RETVAL=$? # man setuid: If uid is different from the old effective uid, # the process will be forbidden from eaving core dumps. # -> don't set uid, we want to collect core dumps # -u $USR -g $GRP fi sleep 1 ### Checking if processes are started if [ `ps auxw | grep ${HM}/${BIN} | head --lines=1 | awk '{print $11}'` = ${HM}/${BIN} ] then echo "SIP router: [[ STARTED ]]" else echo "SIP router: [[ NOT STARTED ]]" fi } ### Check check_run() { ### Checking if Jabber server is running if [ `ps auxw | grep ${JABBERD} | head --lines=1 | awk '{print $11}'` = ${JABBERD} ] then echo "Jabber server: [[ RUNNING ]]" else echo "Jabber server: [[ NOT RUNNING ]]" fi ### Checking if ICQ transport is running if [ `ps auxw | grep ${JICQEXT} | head --lines=1 | awk '{print $11}'` != "grep" ] then echo "ICQ transport: [[ RUNNING ]]" else echo "ICQ transport: [[ NOT RUNNING ]]" fi ### Checking if SIP router is running if [ `ps auxw | grep ${HM}/${BIN} | head --lines=1 | awk '{print $11}'` = ${HM}/${BIN} ] then echo "SIP router: [[ RUNNING ]]" else echo "SIP router: [[ NOT RUNNING ]]" fi } case "$1" in start) start_jab start_ser ;; stop) stop_ser stop_jab ;; restart) echo "Restarting SIP2Jabber ..." stop_ser stop_jab sleep 2 echo start_jab start_ser ;; check) check_run ;; *) N=$0 echo "Usage: $N {start|stop|restart|check}" >&2 exit 1 ;; esac echo exit $RETVAL kamailio-4.0.4/etc/obsoleted/register.cfg0000644000000000000000000000444212223032460017041 0ustar rootroot# # $Id$ # # # ----------- global configuration parameters ------------------------ debug=9 # debug level (cmd line: -dddddddddd) fork=no children=2 #log_stderror=no # (cmd line: -E) log_stderror=yes # (cmd line: -E) check_via=yes # (cmd. line: -v) dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) #port=5070 listen=193.175.132.164 #hope #listen=193.175.135.190 #oxany # ------------------ module loading ---------------------------------- loadmodule "modules/sl/sl.so" loadmodule "modules/print/print.so" loadmodule "modules/maxfwd/maxfwd.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/rr/rr.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- modparam("usrloc", "use_database", 0) # ------------------------- request routing logic ------------------- # main routing logic route{ # filter local stateless ACK generated by authentication of mf replies sl_filter_ACK(); # filter too old messages log("LOG: Checking maxfwd\n"); if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; # Do strict routing if route headers present rewriteFromRoute(); # sign of our domain: there is @ (username), : # (nothing) or . (host) in front of our domain name if (!(uri=~"[@:\.]hope\.fokus\.gmd\.de([;:].*)*")) { route(2); # break from route (2) return -- stop then ! break; }; # here we continue with requests for our domain... if (method=="REGISTER") { log("LOG Request is REGISTER\n"); # update Contact database log("LOG: REGISTER -> saving location\n"); save_contact("location"); break; }; if (method=="MESSAGE") { addRecordRoute(); if (uri=~"sip:[0-9]+@") { rewritehostport("fesarius.fokus.gmd.de:5070"); forward(uri:host,uri:port); break; }; }; # native SIP destinations are handled using our USRLOC DB if (!lookup_contact("location")) { if (method=="ACK") { log("Ooops -- an ACK made it here -- probably UAC screwed up to-tags\n"); break; }; log("LOG: Unable to lookup contact, sending 404\n"); sl_send_reply("404", "Not Found"); break; }; forward(uri:host,uri:port); } # routing logic for outbound requests targeted out of our domain route[2] { forward(uri:host,uri:port); } kamailio-4.0.4/etc/obsoleted/bat.cfg0000644000000000000000000000462312223032460015764 0ustar rootroot# # iptel.org real world configuration for secondary host # # $Id$ # debug=4 # debug level (cmd line: -dddddddddd) #fork=yes fork=no #log_stderror=no # (cmd line: -E) log_stderror=yes # (cmd line: -E) check_via=yes # (cmd. line: -v) dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) port=5060 children=1 # advertise IP address in Via (as opposed to advertising DNS name # which is annoying for downstream servers and some phones can # not handle DNS at all) listen=195.37.77.100 loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/print/print.so" loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/acc/acc.so" loadmodule "../sip_router/modules/rr/rr.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/mysql/mysql.so" loadmodule "../sip_router/modules/usrloc/usrloc.so" loadmodule "../sip_router/modules/auth/auth.so" loadmodule "../sip_router/modules/cpl/cpl.so" modparam("usrloc", "use_database", 1) modparam("usrloc", "use_database", 0) modparam("usrloc", "table", "location") modparam("usrloc", "user_column", "user") modparam("usrloc", "contact_column", "contact") modparam("usrloc", "expires_column", "expires") modparam("usrloc", "q_column", "q") modparam("usrloc", "callid_column", "callid") modparam("usrloc", "cseq_column", "cseq") modparam("usrloc", "flush_interval", 60) modparam("usrloc", "db_url", "sql://csps:47csps11@dbhost/csps107") modparam("auth", "db_url", "sql://csps:47csps11@dbhost/csps107") modparam("auth", "table", "subscriber") modparam("auth", "user_column", "user") modparam("acc", "report_ack", 1) modparam("acc", "early_media", 1) modparam("acc", "log_level", 1) modparam("acc", "acc_flag", 1 ) modparam("acc", "failed_transactions", 1 ) modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 30 ) route{ # filter local stateless ACK generated by authentication of mf replies sl_filter_ACK(); # filter too old messages log("Checking maxfwd\n"); if (!mf_process_maxfwd_header("10")) { log("Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; # Do strict routing if route headers present rewriteFromRoute(); sethost("iptel.org"); if (uri=~"sip:0") setflag(1); if (method=="INVITE") addRecordRoute(); # we now know we may, we now where, let it go out now! t_relay(); } kamailio-4.0.4/etc/obsoleted/to_fox.cfg0000644000000000000000000000441612223032460016514 0ustar rootroot# # iptel.org real world configuration # # $Id$ # # ----------- global configuration parameters ------------ debug=4 # debug level (cmd line: -dddddddddd) #fork=yes fork=no #log_stderror=no # (cmd line: -E) log_stderror=yes # (cmd line: -E) #check_via=yes # (cmd. line: -v) check_via=no# (cmd. line: -v) dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) port=5060 children=1 # advertise IP address in Via (as opposed to advertising DNS name # which is annoying for downstream servers and some phones can # not handle DNS at all) listen=195.37.77.100 #listen=bat.iptel.org # ------------------ module loading ---------------- loadmodule "../sip_router/modules/print/print.so" #loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" # ----------------- setting module-specific parameters ------- # -- tm params -- modparam("tm", "fr_timer", 30 ) modparam("tm", "fr_inv_timer", 30 ) modparam("tm", "wt_timer", 3 ) modparam("tm", "retr_timer2", 4 ) modparam("tm", "retr_timer1p1", 4 ) modparam("tm", "retr_timer1p2", 4 ) modparam("tm", "retr_timer1p3", 4 ) route{ # filter local stateless ACK generated by authentication of mf replies sl_filter_ACK(); if (len_gt( max_len )) { # if (len_gt( 100 )) { sl_send_reply("513", "Riesengross -- Message too large"); log("XXX Riessengross: dropped\n"); break; }; # filter too old messages log("LOG: Checking maxfwd\n"); if (!mf_process_maxfwd_header("0")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; # if (uri=~"jiri@") { # seturi("sip:jiri2@bat.iptel.org"); # log("jiri"); # }; seturi("sip:jirim@iptel.org"); # t_fork_on_no_response("sip:jirim@iptel.org"); # sethost("iptel.org"); # seturi("sip:devnull@iptel.org:9"); # t_fork_to_uri("sip:200@bat.iptel.org:5064"); # if (not method=="ACK") seturi("sip:jirim@iptel.org:5060"); if (!t_relay()) { # if (!t_relay_to("iptel.org", "5060")) { sl_reply_error(); break; }; # THERE IT IS # seturi("sip:7271@iptel.org:5060"); # t_fork_on_no_response("sip:jirim@iptel.org"); # t_relay(); # t_relay_to("iptel.org","5060"); # forward( uri:host, uri:port ); # strip(9); # t_relay_to( "localhost", "9" ); } kamailio-4.0.4/etc/obsoleted/imgw.cfg0000644000000000000000000001147712223032460016166 0ustar rootroot### ----- IM Gateway [SMS+Jabber] config file for 'bat.iptel.org' ---- # ----------- global configuration parameters ------------------------ debug=3 # debug level (cmd line: -dddddddddd) fork=yes #fork=no log_stderror=no # (cmd line: -E) #log_stderror=yes # (cmd line: -E) check_via=yes # (cmd. line: -v) dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) port=5080 children=2 # advertise IP address in Via (as opposed to advertising DNS name # which is annoying for downstream servers and some phones can # not handle DNS at all) listen=195.37.77.100 # ------------------ module loading ---------------------------------- loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/print/print.so" loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/sms/sms.so" loadmodule "../sip_router/modules/textops/textops.so" loadmodule "../sip_router/modules/mysql/mysql.so" loadmodule "../sip_router/modules/jabber/jabber.so" # ----------------- setting module-specific parameters --------------- # -- sms params -- modparam("sms","modems","Falcom [d=/dev/ttyS0;b=9600;p=9254;m=new;l=10;r=2]") modparam("sms","networks","D1[c=491710765000;m=10]") modparam("sms","links","Falcom[D1]") modparam("sms","domain","iptel.org") modparam("sms","max_sms_parts",3) modparam("sms","use_contact",1) # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 10 ) modparam("tm", "wt_timer", 10 ) # -- jabber params -- modparam("jabber","db_url","sql://s2jgw:47s2jgw11@127.0.0.1/sip_jab") modparam("jabber","jaddress","bat.iptel.org") modparam("jabber","jport",5222) modparam("jabber","workers",2) modparam("jabber","max_jobs",10) modparam("jabber","delay_time",15) modparam("jabber","cache_time",1800) # ------------------------- request routing logic ------------------- # main routing logic route{ # filter too old messages log("LOG: Checking maxfwd\n"); if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; if ((search("To:.*@icq\.iptel\.org")) || (search("To:.*@icq\.bat\.iptel\.org")) || (search("To:.*@msn\.iptel\.org")) || (search("To:.*@msn\.bat\.iptel\.org")) || (search("To:.*@aim\.iptel\.org")) || (search("To:.*@aim\.bat\.iptel\.org")) || (search("To:.*@yahoo\.iptel\.org")) || (search("To:.*@yahoo\.bat\.iptel\.org"))) { ### ----- JABBER GATEWAY ------ if (! t_newtran()) { # retransmit whatever we have # it's useless to do any retransmision, because we haven't # sent any statefull reply. (bogdan) #t_retransmit_reply(); break; } else { if (method=="MESSAGE") { log("MESSAGE received -> sending as JABBER\n"); if (jab_send_message()) { sl_send_reply("202","Accepted"); }else{ sl_send_reply("502","Bad gateway"); }; }else{ log("NON_Message request received for JABBER gateway->dropt!\n"); sl_send_reply("501","Not implemented"); }; # transaction conclude it -- junk it now (it will # stay there until WAIT timer hits) t_release(); }; t_unref(); } else { ### ----- SMS GATEWAY ------ if (len_gt( max_len )) { sl_send_reply("513", "Riesengross -- Message too large"); break; }; # accept only req coming from iptel.org if (!src_ip==195.37.77.101 | !( uri=~"iptel.org" | uri=~"195\.37\.77\.100" )) { log("SER:Forbidden request: wrong src_ip or req_uri\n"); sl_send_reply("403","Forbidden"); break; }; #accept only MESSAGE requests if (!method=="MESSAGE") { sl_send_reply("501","Not Implemented"); break; }; # SMS expects the numbers as follows # align numbering to it /* if (uri=~"sip:001" ) { strip(2); prefix("49"); } else if (uri=~"sip:\+491") { strip(1); } else if (uri=~"sip:000491") { strip(3); } else { sl_send_reply("403", "SMS only to German 01* networks"); break; }; */ if (! t_newtran()) { # retransmit whatever we have # it's useless to do any retransmision, because we haven't # sent any statefull reply. (bogdan) #t_retransmit_reply(); break; } else { # do what you want to do if (sms_send_msg_to_net("D1")) { # for sending replies, we woun't use the statefull # function because it is not able to add the Contact # header (in fact to add any rpl_lump :-( ) # things went well, send ok upstream if (!sl_send_reply("202", "yes sir, SMS sent over")) { # if replying failed, retry statelessly sl_reply_error(); }; } else { if (!sl_send_reply("502", "Bad gateway - SMS error")) { # if replying failed, retry statelessly sl_reply_error(); }; }; # transaction conclude it -- junk it now (it will # stay there until WAIT timer hits) t_release(); }; t_unref(); }; } kamailio-4.0.4/etc/obsoleted/blb.cfg0000644000000000000000000000446512223032460015761 0ustar rootroot# # iptel.org real world configuration # # $Id$ # # ----------- global configuration parameters ------------ debug=4 # debug level (cmd line: -dddddddddd) #fork=yes fork=no #log_stderror=no # (cmd line: -E) log_stderror=yes # (cmd line: -E) #check_via=yes # (cmd. line: -v) check_via=no# (cmd. line: -v) dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) port=5060 children=1 # advertise IP address in Via (as opposed to advertising DNS name # which is annoying for downstream servers and some phones can # not handle DNS at all) listen=195.37.77.100 #listen=bat.iptel.org # ------------------ module loading ---------------- loadmodule "../sip_router/modules/print/print.so" #loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" # ----------------- setting module-specific parameters ------- # -- tm params -- modparam("tm", "fr_timer", 12 ) modparam("tm", "fr_inv_timer", 12 ) modparam("tm", "wt_timer", 3 ) modparam("tm", "retr_timer2", 1 ) modparam("tm", "retr_timer1p1", 1 ) modparam("tm", "retr_timer1p2", 1 ) modparam("tm", "retr_timer1p3", 1 ) route{ # filter local stateless ACK generated by authentication of mf replies sl_filter_ACK(); if (len_gt( max_len )) { # if (len_gt( 100 )) { sl_send_reply("513", "Riesengross -- Message too large"); log("XXX Riessengross: dropped\n"); break; }; # filter too old messages log("LOG: Checking maxfwd\n"); if (!mf_process_maxfwd_header("0")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; # if (uri=~"jiri@") { # seturi("sip:jiri2@bat.iptel.org"); # log("jiri"); # }; # seturi("sip:100@bat.iptel.org:5064"); seturi("sip:jirim@iptel.org"); # t_fork_on_no_response("sip:jirim@iptel.org"); # sethost("iptel.org"); # seturi("sip:devnull@iptel.org:9"); t_fork_to_uri("sip:200@bat.iptel.org:5064"); # if (not method=="ACK") seturi("sip:jirim@iptel.org:5060"); if (!t_relay()) { # if (!t_relay_to("iptel.org", "5060")) { sl_reply_error(); break; }; # THERE IT IS # seturi("sip:7271@iptel.org:5060"); # t_fork_on_no_response("sip:jirim@iptel.org"); # t_relay(); # t_relay_to("iptel.org","5060"); # forward( uri:host, uri:port ); # strip(9); # t_relay_to( "localhost", "9" ); } kamailio-4.0.4/etc/obsoleted/tmtest.cfg0000644000000000000000000001601712223032460016536 0ustar rootroot# # $Id$ # # iptel.org real world configuration # # ----------- global configuration parameters ------------------------ #debug=8 # debug level (cmd line: -dddddddddd) debug=3 #fork=yes fork=no children=2 #log_stderror=no # (cmd line: -E) log_stderror=yes # (cmd line: -E) check_via=yes # (cmd. line: -v) dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) port=5060 #port=8060 # advertise IP address in Via (as opposed to advertising DNS name # which is annoying for downstream servers and some phones can # not handle DNS at all) listen=195.37.77.100 # ------------------ module loading ---------------------------------- loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/print/print.so" #loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/acc/acc.so" loadmodule "../sip_router/modules/rr/rr.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" #loadmodule "../sip_router/modules/mysql/mysql.so" loadmodule "../sip_router/modules/usrloc/usrloc.so" #loadmodule "../sip_router/modules/auth/auth.so" loadmodule "../sip_router/modules/cpl/cpl.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- modparam("usrloc", "use_database", 0) modparam("usrloc", "flush_interval", 3600) # -- acc params -- # report ACKs too for sake of completeness -- as we account PSTN # destinations which are RR, ACKs should show up modparam("acc", "report_ack", 1) # don't bother me with early media reports (I don't like 183 # too much anyway...ever thought of timer C hitting after # listening to music-on-hold for five minutes?) modparam("acc", "early_media", 0) modparam("acc", "log_level", 1) # that is the flag for which we will account -- don't forget to # set the same one :-) modparam("acc", "acc_flag", 3 ) # we are interested only in succesful transactions modparam("acc", "failed_transactions", 0 ) # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 10 ) modparam("tm", "wt_timer", 1000 ) modparam("tm", "noisy_ctimer", 1 ) # ------------------------- request routing logic ------------------- # main routing logic route{ #t_uac(); # filter local stateless ACK generated by authentication of mf replies sl_filter_ACK(); # filter too old messages log("LOG: Checking maxfwd\n"); if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; # len_gt *after* max_fwd, otherwise an "INVITE sip:sos@0.0.0.0" # will cause "message too big" for a short message if (len_gt( max_len )) { sl_send_reply("513", "Riesengross -- Message too large"); break; }; # Do strict routing if route headers present rewriteFromRoute(); # look at whether we need record-routing; # - we need it for calls from gateways (otherwise, subsequent # requests from the other # party will attempt to contact gateway # directly through blocked ports) # - we need it for Windows Messanger's IM sessions to cross # firewalls -- we force all MESSAGEs to go via our server # to avoid blocking port numbers (some firewalls can do # standard SIP but are puzzled by Microsoft's proprietary # messaging session model) # - some other places may decide to set the record-routing # flag (2 chosen) too; particularly, INVITEs to our gw if ( (src_ip==195.37.77.110 & method=="INVITE") || method=="MESSAGE" || method=="INFO" ) { setflag(2); }; # if this request is not for our domain, fall over to # outbound request processing; include gateway's address # in matching too -- we RR requests to it, so that # its address may show up in subsequent requests # after rewriteFromRoute # sign of our domain: there is @ (username), : # (nothing) or . (host) in front of our domain name if (!(uri=~"bat\.iptel\.org([;:].*)*" | uri=~"[@:\.]195\.37\.77\.101([;:].*)*" | uri=~"@195\.37\.77\.110([;:].*)*" )) { route(2); # break from route (2) return -- stop then ! break; }; # here we continue with requests for our domain... # registers always MUST be authenticated to # avoid stealing incoming calls if (method=="REGISTER") { log("LOG Request is REGISTER\n"); # prohibit attempts to grab someone else's To address # using valid credentials; the only exception is the user # 'replciator' permitted to generate 3-rd party registrations # update Contact database log("LOG: REGISTER is authorized, saving location\n"); save_contact("location"); break; }; # now check if it's about PSTN destinations through our gateway; # note that 8.... is exempted for numerical destinations if (uri=~"sip:\+?[0-79][0-9]*@.*") { route(3); # break from route (3) return -- stop then ! break; }; # native SIP destinations are handled using our USRLOC DB if (!lookup_contact("location")) { if (method=="ACK") { log("Ooops -- an ACK made it here -- probably UAC screwed up to-tags\n"); break; }; log("LOG: Unable to lookup contact, sending 404\n"); sl_send_reply("404", "Not Found"); break; }; # requests from gateway should be RR-ed too if (isflagset(2)) { addRecordRoute(); }; # we now know we may, we know where, let it go out now! if (!t_relay()) { sl_reply_error(); break; }; } #--------------------------------------------------------------------- # routing logic for outbound requests targeted out of our domain # (beware, messages to our users can end up here too: for example, # an INVITE may be UsrLoc-ed, then the other party uses outbound # proxy with r-uri=the usr_loced addredd (typically IP)) route[2] { # requests from gateway should be RR-ed too if (isflagset(2)) { addRecordRoute(); }; if (!t_relay()) { sl_reply_error(); break; }; } #--------------------------------------------------------------------- # logic for calls through our PSTN gateway route[3] { # if it is a MESSAGE pass it "as is" over to our SMS gateway # (which unfortunately lives at a different host due to # lack of serial interfaces) if (method=="MESSAGE") { # note that we don't do any admission control yet: # anyone can SMS anywhere; setflag(1); rewritehostport("195.37.77.100:5070"); if (!t_relay()) { sl_reply_error(); }; break; }; # continue with requests to PSTN gateway ... # the international + prefix if (uri=~"sip:\+" ) { strip(1); prefix("000"); }; # free call destinations ... no authentication needed if (uri=~"sip:001795061546@.*" | uri=~"sip:0016097265544.*" | uri=~"sip:[79][0-9][0-9][0-9]@.*" | uri=~"sip:98[0-9][0-9][0-9][0-9]") { log("LOG: Free PSTN\n"); # let's log free calls for now too ... setflag(1); } else { # we passed all authorization checks for PSTN -- move on! # tag this transaction for accounting setflag(1); }; # authorized PSTN # requests to gateway must be record-routed because the GW accepts # only reqeusts coming from our proxy if (isflagset(2) || method=="INVITE") addRecordRoute(); # if you have passed through all the checks, let your call go to GW! rewritehostport("fox.iptel.org:5060"); if (!t_relay()) { sl_reply_error(); break; }; } kamailio-4.0.4/etc/obsoleted/test.cfg0000644000000000000000000002462212223032460016176 0ustar rootroot# # $Id$ # # iptel.org real world configuration # # ----------- global configuration parameters ------------------------ debug=3 # debug level (cmd line: -dddddddddd) #fork=yes fork=no #log_stderror=no # (cmd line: -E) log_stderror=yes # (cmd line: -E) check_via=yes # (cmd. line: -v) dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) #port=5060 port=8060 children=1 # advertise IP address in Via (as opposed to advertising DNS name # which is annoying for downstream servers and some phones can # not handle DNS at all) listen=195.37.77.100 # ------------------ module loading ---------------------------------- loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/print/print.so" loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/acc/acc.so" loadmodule "../sip_router/modules/rr/rr.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/mysql/mysql.so" loadmodule "../sip_router/modules/usrloc/usrloc.so" loadmodule "../sip_router/modules/auth/auth.so" loadmodule "../sip_router/modules/cpl/cpl.so" # ----------------- setting module-specific parameters --------------- # -- usrloc params -- modparam("usrloc", "use_database", 1) modparam("usrloc", "table", "location") modparam("usrloc", "user_column", "user") modparam("usrloc", "contact_column", "contact") modparam("usrloc", "expires_column", "expires") modparam("usrloc", "q_column", "q") modparam("usrloc", "callid_column", "callid") modparam("usrloc", "cseq_column", "cseq") modparam("usrloc", "flush_interval", 60) modparam("usrloc", "db_url", "sql://csps:47csps11@dbhost/csps107") # -- auth params -- modparam("auth", "db_url", "sql://csps:47csps11@dbhost/csps107") modparam("auth", "user_column", "user") # nonce generation secret; particularly useful if multiple servers # in a proxy farm are configured to authenticate modparam("auth", "secret", "439tg8h349g8hq349t9384hg") # calculate_ha1=false means password column includes ha1 strings; # if it was false, plain-text passwords would be assumed modparam("auth", "calculate_ha1", false) modparam("auth", "nonce_expire", 300) modparam("auth", "retry_count", 5) # password_column, realm_column, group_table, group_user_column, # group_group_column are set to their default values # password_column_2 allows to deal with clients who put domain name # in authentication credentials when calculate_ha1=false (if true, # it works); if set to a value and USER_DOMAIN_HACK was enabled # in defs.h, authentication will still work # -- acc params -- # report ACKs too for sake of completeness -- as we account PSTN # destinations which are RR, ACKs should show up modparam("acc", "report_ack", 1) # don't bother me with early media reports (I don't like 183 # too much anyway...ever thought of timer C hitting after # listening to music-on-hold for five minutes?) modparam("acc", "early_media", 0) modparam("acc", "log_level", 1) # that is the flag for which we will account -- don't forget to # set the same one :-) modparam("acc", "acc_flag", 1 ) # we are interested only in succesful transactions modparam("acc", "failed_transactions", 0 ) # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 30 ) # ------------------------- request routing logic ------------------- # main routing logic route{ # filter local stateless ACK generated by authentication of mf replies sl_filter_ACK(); # filter too old messages log("Checking maxfwd\n"); if (!mf_process_maxfwd_header("10")) { log("Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; # Do strict routing if route headers present rewriteFromRoute(); # divert voicemail requests if (uri=~"mail\.iptel\.org" | uri=~":5066") { log("Request is for voicemail\n"); sethost("iptel.org"); t_relay_to("fox.iptel.org", "5066"); break; }; # if this request is not for our domain, fall over to # outbound request processing; include gateway's address # in matching too -- we RR requests to it, so that # its address may show up in subsequent requests # after rewriteFromRoute if (!(uri=~"[@:]iptel\.org([;:].*)*" | uri=~"[@:]195\.37\.77\.101([;:].*)*" | uri=~"@195\.37\.77\.110([;:].*)*" )) { route(2); }; # here we continue with requests for our domain... # various aliases (might use a database in future) if (uri=~"sip:9040@") { seturi("jiri@iptel.org"); }; if (uri=~"sip:17@") { seturi("sip:henry@siptest.wcom.com"); }; # check again, if it is still for our domain after aliases if ( !(uri=~"[@:]iptel\.org([;:].*)*" | uri=~"[@:]195\.37\.77\.101([;:].*)*" | uri=~"@195\.37\.77\.110([;:].*)*" )) { route(2); }; log("Request is for iptel.org\n"); # registers always MUST be authenticated to # avoid stealing incoming calls if (method=="REGISTER") { log("Request is REGISTER\n"); if (!www_authorize( "iptel.org" /* realm */, "subscriber" /* table name */ )) { log("REGISTER has no credentials, sending challenge\n"); www_challenge( "iptel.org" /* realm */, "0" /* no qop -- M$ can't deal with it */); break; }; # prohibit attempts to grab someone else's To address # using valid credentials if (!check_to()) { log("To Cheating attempt\n"); sl_send_reply("403", "That is ugly -- use To=id next time"); break; }; # update Contact database log("REGISTER is authorized, saving location\n"); save_contact("location"); break; }; # now check if it's about PSTN destinations through our gateway; # note that 8.... is exempted for numerical destinations if (uri=~"sip:[0-79][0-9]*@.*") { route(3); }; # ---------- demo - begin -------------- /* added by Bogdan for cpl demo - Dorgham request*/ if (uri=~"sip:test@.*" && method=="INVITE") { log("SER : runing CPL!! :)\n"); if ( !cpl_run_script() ) { log("SER : Error during running CPL script!\n"); }else{ if ( cpl_is_response_reject() ) { log("SER: reject"); sl_send_reply("603","I am not available!"); break; }else if ( cpl_is_response_redirect() ) { log("SER : redirect\n"); cpl_update_contact(); sl_send_reply("302","Moved temporarily"); break; }; }; }; # -------------- demo - end ------------- # native SIP destinations are handled using our USRLOC DB if (!lookup_contact("location")) { log("Unable to lookup contact, sending 404\n"); sl_send_reply("404", "Not Found"); break; }; # check whether some inventive user has uploaded gateway # contacts to UsrLoc to bypass our authorization logic if (uri=~"@195\.37\.77\.110([;:].*)*" ) { log("Weird! Gateway address in UsrLoc!\n"); route(3); }; # requests from gateway should be RR-ed too if (src_ip==195.37.77.110 && method=="INVITE") { addRecordRoute(); }; # we now know we may, we know where, let it go out now! t_relay(); } # routing logic for outbound requests targeted out of our domain route[2] { # outbound requests are allowed only for our users -- we don't # support relaying and don't like strangers bothering us # with resolving DNS log("that's a request to outside"); if (!(src_ip==195.37.77.110) & !(proxy_authorize( "iptel.org" /* realm */, "subscriber" /* table name */ ))) { # see comments bellow on these ACK/CANCEL exceptions if (method=="ACK" ) { log("failed outbound authentication for ACK granted"); } else if (method=="CANCEL") { log("failed outbound authentication for ACK granted"); } else proxy_challenge("iptel.org" /* realm */, "0" /* no-qop */); break; }; # to maintain credibility of our proxy, we check From to be # equal of credential id -- all outbound request leaving our # proxy are guaranteed to be generated by persons in "From" if (!check_from()) { log("From Cheating attempt\n"); sl_send_reply("403", "That is ugly -- use From=id next time"); break; }; t_relay(); } # logic for calls through our PSTN gateway route[3] { # free call destinations ... no authentication needed if (uri=~"sip:001795061546@.*" | uri=~"sip:0016097265544.*" | uri=~"sip:[79][0-9][0-9][0-9]@.*") { log("Free PSTN\n"); } else { # all other PSTN destinations only for authenticated users # (Cisco GW, which has no digest support, is authenticated # by its IP address -- that's for sure not very strong; # wth confirmed that we filter packets coming from outside # and bearing SRC IP address of our network) if (!(src_ip==195.37.77.110) & !(proxy_authorize( "iptel.org" /* realm */, "subscriber" /* table name */))) { # we are forgiving and ignore improper credentials # for ACK/CANCEL as bis-09 is somewhat cryptic about # its use and many UACs have not gotten it right if (method=="ACK" ) { log("failed gw authentication for ACK granted"); } else if (method=="CANCEL") { log("failed gw authentication for ACK granted"); } else proxy_challenge( "iptel.org" /* realm */, "0" /* no qop */ ); break; }; # authorize only for INVITEs -- RR/Contact may result in weird # things showing up in d-uri that would break our logic; our # major concern is INVITE which causes PSTN costs anyway if (method=="INVITE") { # does the authenticated user have a permission for local # calls? (i.e., is he in the "local" group?) if (uri=~"sip:0[1-9][0-9]+@.*") { if (!is_in_group("local")) { sl_send_reply("403", "Local Toodle Noodle..."); break; }; # the same for long-distance } else if (uri=~"sip:00[1-9][0-9]+@.*") { if (uri=~"sip:001[089]" | uri=~"sip:00900.*" ) { sl_send_reply("403", "Added Value Destinations not permitted..."); break; }; if (!is_in_group("ld")) { sl_send_reply("403", "LD Toodle Noodle..."); break; }; # the same for international calls } else if (uri=~"sip:000[1-9][0-9]+@.*") { if (!is_in_group("int")) { sl_send_reply("403", "International Toodle Noodle..."); break; }; # everything else (e.g., interplanetary calls) is denied } else { sl_send_reply("403", "interplanetary Toodle Noodle..."); break; }; }; # INVITE to authorized PSTN }; # authorized PSTN # requests to gateway must be record-route because the GW accepts # only reqeusts coming from our proxy if (method=="INVITE") addRecordRoute(); # if you have passed through all the checks, let your call go to GW! rewritehostport("195.37.77.110:5060"); # tag this transaction for accounting setflag(1); t_relay(); } kamailio-4.0.4/etc/obsoleted/smsgw.cfg0000644000000000000000000000635312223032460016360 0ustar rootroot# # $Id$ # # bat.iptel.org (sms gateway) real world configuration # # ----------- global configuration parameters ------------------------ debug=3 # debug level (cmd line: -dddddddddd) fork=yes #fork=no log_stderror=no # (cmd line: -E) #log_stderror=yes # (cmd line: -E) check_via=yes # (cmd. line: -v) dns=on # (cmd. line: -r) rev_dns=yes # (cmd. line: -R) port=5070 children=2 # advertise IP address in Via (as opposed to advertising DNS name # which is annoying for downstream servers and some phones can # not handle DNS at all) listen=195.37.77.100 # ------------------ module loading ---------------------------------- loadmodule "../sip_router/modules/sl/sl.so" loadmodule "../sip_router/modules/print/print.so" loadmodule "../sip_router/modules/tm/tm.so" loadmodule "../sip_router/modules/maxfwd/maxfwd.so" loadmodule "../sip_router/modules/sms/sms.so" # ----------------- setting module-specific parameters --------------- # -- sms params -- modparam("sms","modems","Falcom [d=/dev/ttyS0;b=9600;p=9254;m=new;l=10;r=2]") modparam("sms","networks","D1[c=491710765000;m=10]") modparam("sms","links","Falcom[D1]") modparam("sms","domain","iptel.org") modparam("sms","max_sms_parts",3) modparam("sms","use_contact",1) # -- tm params -- modparam("tm", "fr_timer", 10 ) modparam("tm", "fr_inv_timer", 10 ) modparam("tm", "wt_timer", 10 ) # ------------------------- request routing logic ------------------- # main routing logic route{ if (len_gt( max_len )) { sl_send_reply("513", "Riesengross -- Message too large"); break; }; # filter too old messages log("LOG: Checking maxfwd\n"); if (!mf_process_maxfwd_header("10")) { log("LOG: Too many hops\n"); sl_send_reply("483","Too Many Hops"); break; }; # accept only req coming from iptel.org if (!src_ip==195.37.77.101 | !( uri=~"iptel.org" | uri=~"195\.37\.77\.100" )) { log("SER:Forbidden request: wrong src_ip or req_uri\n"); sl_send_reply("403","Forbidden"); break; }; #accept only MESSAGE requests if (!method=="MESSAGE") { sl_send_reply("501","Not Implemented"); break; }; # SMS expects the numbers as follows # align numbering to it if (uri=~"sip:001" ) { strip(2); prefix("49"); } else if (uri=~"sip:\+491") { strip(1); } else if (uri=~"sip:000491") { strip(3); } else { sl_send_reply("403", "SMS only to German 01* networks"); break; }; if (! t_newtran()) { # retransmit whatever we have # it's useless to do any retransmision, because we haven't # sent any statefull reply. (bogdan) #t_retransmit_reply(); break; } else { # do what you want to do if (sms_send_msg_to_net("D1")) { # for sending replies, we woun't use the statefull # function because it is not able to add the Contact # header (in fact to add any rpl_lump :-( ) # things went well, send ok upstream if (!sl_send_reply("202", "yes sir, SMS sent over")) { # if replying failed, retry statelessly sl_reply_error(); }; } else { if (!sl_send_reply("502", "Bad gateway - SMS error")) { # if replying failed, retry statelessly sl_reply_error(); }; }; # transaction conclude it -- junk it now (it will # stay there until WAIT timer hits) t_release(); }; t_unref(); } kamailio-4.0.4/local_timer.h0000644000000000000000000000403012223032460014435 0ustar rootroot/* * $Id$ * * Copyright (C) 2007 iptelorg GmbH * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* local timer routines * WARNING: this should be used only from within the same process. * The local timers are not multi-process or multi-thread safe * (there are no locks) * * History: * -------- * 2007-11-22 created by andrei */ #ifndef _local_timer_h #define _local_timer_h #include "timer_ticks.h" #include "timer_funcs.h" struct local_timer { /* private timer information */ ticks_t prev_ticks; /* last time we ran the timer */ struct timer_lists timer_lst; /* actual timer lists */ }; #define local_timer_init(tl, fun, param, flgs) timer_init(tl, fun, param, flgs) #define local_timer_reinit(tl) timer_reinit((tl)) int init_local_timer(struct local_timer *lt_handle, ticks_t crt_ticks); void destroy_local_timer(struct local_timer* lt_handle); int local_timer_add(struct local_timer* h, struct timer_ln* tl, ticks_t delta, ticks_t crt_ticks); void local_timer_del(struct local_timer* h, struct timer_ln* tl); void local_timer_run(struct local_timer* lt, ticks_t crt_ticks); #endif /* _local_timer_h */ kamailio-4.0.4/raw_sock.h0000644000000000000000000000354212223032460013762 0ustar rootroot/* * $Id$ * * Copyright (C) 2010 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** raw socket functions. * @file raw_sock.c * @ingroup core * Module: @ref core */ /* * History: * -------- * 2010-06-07 initial version (from older code) andrei */ #ifndef _raw_sock_h #define _raw_sock_h #include "ip_addr.h" /** filter for limiting packets received on raw sockets. */ struct raw_filter{ struct net dst; unsigned short port1; unsigned short port2; char proto; }; extern int raw_ipip; int raw_socket(int proto, struct ip_addr* ip, str* iface, int iphdr_incl); int raw_udp4_socket(struct ip_addr* ip, str* iface, int iphdr_incl); int recvpkt4(int sock, char* buf, int len, union sockaddr_union* from, union sockaddr_union* to); int raw_udp4_recv(int rsock, char** buf, int len, union sockaddr_union* from, union sockaddr_union* to, struct raw_filter* rf); int raw_udp4_send(int rsock, char* buf, unsigned int len, union sockaddr_union* from, union sockaddr_union* to); int raw_iphdr_udp4_send(int rsock, char* buf, unsigned int len, union sockaddr_union* from, union sockaddr_union* to, unsigned short mtu); #endif /* _raw_sock_h */ kamailio-4.0.4/raw_listener.h0000644000000000000000000000241612223032460014647 0ustar rootroot/* * Copyright (C) 2010 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** raw socket udp listen functions. * @file raw_listener.h * @ingroup core * Module: @ref core */ /* * History: * -------- * 2010-06-09 initial version (from older code) andrei */ #ifndef _raw_listener_h #define _raw_listener_h #include "ip_addr.h" /** default raw socket used for sending on udp ipv4 */ struct socket_info* raw_udp_sendipv4; int raw_listener_init(struct socket_info* si, str* iface, int iphdr_incl); int raw_udp4_rcv_loop(int rsock, int port1, int port2); #endif /* _raw_listener_h */ kamailio-4.0.4/daemonize.h0000644000000000000000000000274312223032457014135 0ustar rootroot/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * * History: * -------- * 2004-02-20 created by andrei * 2007-06-07 added mem_lock_pages() (andrei) * 2010-08-19 send status via pipe code derived from 9167c1 (ibc) (andrei) */ #ifndef _daemonize_h #define _daemonize_h int daemonize(char* name, int daemon_status_fd_input); int do_suid(void); int increase_open_fds(int target); int set_core_dump(int enable, long unsigned int size); int mem_lock_pages(void); int set_rt_prio(int prio, int policy); void daemon_status_init(void); void daemon_status_on_fork_cleanup(void); int daemon_status_send(char status); void daemon_status_no_wait(void); void daemon_status_on_fork_cleanup(void); #endif /*_daemonize_h */ /* vi: set ts=4 sw=4 tw=79:ai:cindent: */ kamailio-4.0.4/str.h0000644000000000000000000001142612223032460012762 0ustar rootroot/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef str_h #define str_h /** @defgroup str_string Counted-Length Strings * @{ * * Implementation of counted-length strings. In SER and its modules, strings * are often stored in the ::str structure. In addition to the pointer * pointing to the first character of the string, the structure also contains * the length of the string. * * @section motivation Motivation * Storing the length of the string together with the pointer to the string * has two advantages. First, it makes many string operations faster because * it is not necessary to count the number of characters at runtime. Second, * the pointer can point to arbitrary substrings within a SIP message (which * itself is stored as one long string spanning the whole message) without the * need to make a zero-terminated copy of it. * * @section drawbacks Drawbacks * Note well that the fact that string stored * using this data structure are not zero terminated makes them a little * incovenient to use with many standard libc string functions, because these * usually expect the input to be zero-terminated. In this case you have to * either make a zero-terminated copy or inject the terminating zero behind * the actuall string (if possible). Note that injecting a zero terminating * characters is considered to be dangerous. */ /** @file * This header field defines the ::str data structure that is used across * SER sources to store counted-length strings. The file also defines several * convenience macros. */ /** Data structure used across SER sources to store counted-length strings. * This is the data structure that is used to store counted-length * strings in SER core and modules. */ struct _str{ char* s; /**< Pointer to the first character of the string */ int len; /**< Length of the string */ }; /** Data structure used across SER sources to store counted-length strings. * @see _str */ typedef struct _str str; /** Initializes static ::str string with string literal. * This is a convenience macro that can be used to initialize * static ::str strings with string literals like this: * \code static str var = STR_STATIC_INIT("some_string"); \endcode * @param v is a string literal * @sa STR_NULL */ #define STR_STATIC_INIT(v) {(v), sizeof(v) - 1} /* kamailio compatibility macro (same thing as above) */ #define str_init(v) STR_STATIC_INIT(v) /** Initializes ::str string with NULL pointer and zero length. * This is a convenience macro that can be used to initialize * ::str string variable to NULL string with zero length: * \code str var = STR_NULL; \endcode * @sa STR_STATIC_INIT */ #define STR_NULL {0, 0} /** Formats ::str string for use in printf-like functions. * This is a macro that prepares a ::str string for use in functions which * use printf-like formatting strings. This macro is necessary because * ::str strings do not have to be zero-terminated and thus it is necessary * to provide printf-like functions with the number of characters in the * string manually. Here is an example how to use the macro: * \code printf("%.*s\n", STR_FMT(var));\endcode Note well that the correct * sequence in the formatting string is %.*, see the man page of printf for * more details. */ #define STR_FMT(_pstr_) \ ((_pstr_ != (str *)0) ? (_pstr_)->len : 0), \ ((_pstr_ != (str *)0) ? (_pstr_)->s : "") /** Compares two ::str strings. * This macro implements comparison of two strings represented using ::str * structures. First it compares the lengths of both string and if and only * if they are same then both strings are compared using memcmp. * @param x is first string to be compared * @param y is second string to be compared * @return 1 if strings are same, 0 otherwise */ #define STR_EQ(x,y) (((x).len == (y).len) && \ (memcmp((x).s, (y).s, (x).len) == 0)) /** @} */ #endif kamailio-4.0.4/lock_ops_init.h0000644000000000000000000000175112223032460015006 0ustar rootroot/* * $Id$ * * Copyright (C) 2007 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * lock_ops init functions */ /* * History: * -------- * 2007-05-14 created by andrei */ #ifndef __lock_ops_init_h #define __lock_ops_init_h int init_lock_ops(void); void destroy_lock_ops(void); #endif kamailio-4.0.4/mem/0000755000000000000000000000000012223032460012553 5ustar rootrootkamailio-4.0.4/mem/mem.c0000644000000000000000000000562612223032460013506 0ustar rootroot/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of sip-router, a free SIP server. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * * History: * -------- * 2003-04-08 init_mallocs split into init_{pkg,shm}_malloc (andrei) * */ /** * \file * \brief Main definitions for memory manager * * Main definitions for memory manager, like malloc, free and realloc * \ingroup mem */ #include #include #include "../config.h" #include "../dprint.h" #include "../globals.h" #include "mem.h" #ifdef PKG_MALLOC #include "q_malloc.h" #endif #ifdef SHM_MEM #include "shm_mem.h" #endif #ifdef PKG_MALLOC #ifndef DL_MALLOC char* mem_pool = 0; #endif #ifdef F_MALLOC struct fm_block* mem_block = 0; #elif defined DL_MALLOC /* don't need this */ #else struct qm_block* mem_block = 0; #endif #endif /** * \brief Initialize private memory pool * \return 0 if the memory allocation was successful, -1 otherwise */ int init_pkg_mallocs(void) { #ifdef PKG_MALLOC /*init mem*/ #ifndef DL_MALLOC if (pkg_mem_size == 0) pkg_mem_size = PKG_MEM_POOL_SIZE; mem_pool = malloc(pkg_mem_size); #endif #ifdef F_MALLOC if (mem_pool) mem_block=fm_malloc_init(mem_pool, pkg_mem_size); #elif DL_MALLOC /* don't need this */ #else if (mem_pool) mem_block=qm_malloc_init(mem_pool, pkg_mem_size); #endif #ifndef DL_MALLOC if (mem_block==0){ LOG(L_CRIT, "could not initialize memory pool\n"); fprintf(stderr, "Too much pkg memory demanded: %ld bytes\n", pkg_mem_size); return -1; } #endif #endif return 0; } /** * \brief Destroy private memory pool */ void destroy_pkg_mallocs(void) { #ifdef PKG_MALLOC #ifndef DL_MALLOC if (mem_pool) { free(mem_pool); mem_pool = 0; } #endif #endif /* PKG_MALLOC */ } /** * \brief Initialize shared memory pool * \param force_alloc Force allocation of memory, e.g. initialize complete block with zero * \return 0 if the memory allocation was successful, -1 otherwise */ int init_shm_mallocs(int force_alloc) { #ifdef SHM_MEM if (shm_mem_init(force_alloc)<0) { LOG(L_CRIT, "could not initialize shared memory pool, exiting...\n"); fprintf(stderr, "Too much shared memory demanded: %ld\n", shm_mem_size ); return -1; } #endif return 0; } kamailio-4.0.4/mem/memdbg.h0000644000000000000000000000251012223032460014155 0ustar rootroot/* * Copyright (C) 2006 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * History: * -------- * 2006-04-07 created by andrei */ /** * \file * \brief Malloc debug messages * \ingroup mem */ #ifndef _memdbg_h #define _memdbg_h #include "../cfg/cfg.h" /* memdbg*/ extern int memdbg; #ifdef NO_DEBUG #ifdef __SUNPRO_C #define MDBG(...) #else #define MDBG(fmt, args...) #endif #else /* NO_DEBUG */ #ifdef __SUNPRO_C #define MDBG(...) LOG(cfg_get(core, core_cfg, memdbg), __VA_ARGS__) #else #define MDBG(fmt, args...) \ LOG(cfg_get(core, core_cfg, memdbg), fmt, ## args) #endif #endif /* NO_DEBUG */ #endif kamailio-4.0.4/mem/q_malloc.c0000644000000000000000000005731212223032460014516 0ustar rootroot/* * Copyright (C) 2001-2003 FhG Fokus * * This file is part of sip-router, a free SIP server. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * History: * -------- * ????-??-?? created by andrei * 2003-04-14 more debugging added in DBG_QM_MALLOC mode (andrei) * 2003-06-29 added qm_realloc (andrei) * 2004-07-19 fragments book keeping code and support for 64 bits * memory blocks (64 bits machine & size>=2^32) (andrei) * GET_HASH s/ 4Gb mem., switched to long (andrei) * 2005-03-02 added qm_info() (andrei) * 2005-12-12 fixed realloc shrink real_used & used accounting; * fixed initial size (andrei) * 2006-02-03 fixed realloc out of mem. free bug (andrei) * 2006-04-07 s/DBG/MDBG (andrei) * 2007-02-23 added fm_available() (andrei) * 2009-09-28 added fm_sums() (patch from Dragos Vingarzan) */ /** * \file * \brief Simple & fast malloc library * \ingroup mem */ #if !defined(q_malloc) && !(defined F_MALLOC) #define q_malloc #include #include #include "q_malloc.h" #include "../dprint.h" #include "../globals.h" #include "memdbg.h" #include "../cfg/cfg.h" /* memlog */ #ifdef MALLOC_STATS #include "../events.h" #endif /*useful macros*/ #define FRAG_END(f) \ ((struct qm_frag_end*)((char*)(f)+sizeof(struct qm_frag)+ \ (f)->size)) #define FRAG_NEXT(f) \ ((struct qm_frag*)((char*)(f)+sizeof(struct qm_frag)+(f)->size+ \ sizeof(struct qm_frag_end))) #define FRAG_PREV(f) \ ( (struct qm_frag*) ( ((char*)(f)-sizeof(struct qm_frag_end))- \ ((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))->size- \ sizeof(struct qm_frag) ) ) #define PREV_FRAG_END(f) \ ((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end))) #define FRAG_OVERHEAD (sizeof(struct qm_frag)+sizeof(struct qm_frag_end)) #define ROUNDTO_MASK (~((unsigned long)ROUNDTO-1)) #define ROUNDUP(s) (((s)+(ROUNDTO-1))&ROUNDTO_MASK) #define ROUNDDOWN(s) ((s)&ROUNDTO_MASK) /* finds the hash value for s, s=ROUNDTO multiple*/ #define GET_HASH(s) ( ((unsigned long)(s)<=QM_MALLOC_OPTIMIZE)?\ (unsigned long)(s)/ROUNDTO: \ QM_MALLOC_OPTIMIZE/ROUNDTO+big_hash_idx((s))- \ QM_MALLOC_OPTIMIZE_FACTOR+1 ) #define UN_HASH(h) ( ((unsigned long)(h)<=(QM_MALLOC_OPTIMIZE/ROUNDTO))?\ (unsigned long)(h)*ROUNDTO: \ 1UL<<((h)-QM_MALLOC_OPTIMIZE/ROUNDTO+\ QM_MALLOC_OPTIMIZE_FACTOR-1)\ ) /* mark/test used/unused frags */ #define FRAG_MARK_USED(f) #define FRAG_CLEAR_USED(f) #define FRAG_WAS_USED(f) (1) /* other frag related defines: * MEM_COALESCE_FRAGS * MEM_FRAG_AVOIDANCE */ #define MEM_FRAG_AVOIDANCE /* computes hash number for big buckets*/ inline static unsigned long big_hash_idx(unsigned long s) { int idx; /* s is rounded => s = k*2^n (ROUNDTO=2^n) * index= i such that 2^i > s >= 2^(i-1) * * => index = number of the first non null bit in s*/ idx=sizeof(long)*8-1; for (; !(s&(1UL<<(sizeof(long)*8-1))) ; s<<=1, idx--); return idx; } #ifdef DBG_QM_MALLOC #define ST_CHECK_PATTERN 0xf0f0f0f0 #define END_CHECK_PATTERN1 0xc0c0c0c0 #define END_CHECK_PATTERN2 0xabcdefed static void qm_debug_frag(struct qm_block* qm, struct qm_frag* f) { if (f->check!=ST_CHECK_PATTERN){ LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) " "beginning overwritten(%lx)!\n", f, (char*)f+sizeof(struct qm_frag), f->check); qm_status(qm); abort(); }; if ((FRAG_END(f)->check1!=END_CHECK_PATTERN1)|| (FRAG_END(f)->check2!=END_CHECK_PATTERN2)){ LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p)" " end overwritten(%lx, %lx)!\n", f, (char*)f+sizeof(struct qm_frag), FRAG_END(f)->check1, FRAG_END(f)->check2); qm_status(qm); abort(); } if ((f>qm->first_frag)&& ((PREV_FRAG_END(f)->check1!=END_CHECK_PATTERN1) || (PREV_FRAG_END(f)->check2!=END_CHECK_PATTERN2) ) ){ LOG(L_CRIT, "BUG: qm_*: prev. fragm. tail overwritten(%lx, %lx)[%p:%p]!" "\n", PREV_FRAG_END(f)->check1, PREV_FRAG_END(f)->check2, f, (char*)f+sizeof(struct qm_frag)); qm_status(qm); abort(); } } #endif static inline void qm_insert_free(struct qm_block* qm, struct qm_frag* frag) { struct qm_frag* f; struct qm_frag* prev; int hash; hash=GET_HASH(frag->size); for(f=qm->free_hash[hash].head.u.nxt_free; f!=&(qm->free_hash[hash].head); f=f->u.nxt_free){ if (frag->size <= f->size) break; } /*insert it here*/ prev=FRAG_END(f)->prev_free; prev->u.nxt_free=frag; FRAG_END(frag)->prev_free=prev; frag->u.nxt_free=f; FRAG_END(f)->prev_free=frag; qm->free_hash[hash].no++; } /* init malloc and return a qm_block*/ struct qm_block* qm_malloc_init(char* address, unsigned long size) { char* start; char* end; struct qm_block* qm; unsigned long init_overhead; int h; /* make address and size multiple of 8*/ start=(char*)ROUNDUP((unsigned long) address); DBG("qm_malloc_init: QM_OPTIMIZE=%lu, /ROUNDTO=%lu\n", QM_MALLOC_OPTIMIZE, QM_MALLOC_OPTIMIZE/ROUNDTO); DBG("qm_malloc_init: QM_HASH_SIZE=%lu, qm_block size=%lu\n", QM_HASH_SIZE, (long)sizeof(struct qm_block)); DBG("qm_malloc_init(%p, %lu), start=%p\n", address, size, start); if (sizesize=size; qm->real_used=init_overhead; qm->max_real_used=qm->real_used; size-=init_overhead; qm->first_frag=(struct qm_frag*)(start+ROUNDUP(sizeof(struct qm_block))); qm->last_frag_end=(struct qm_frag_end*)(end-sizeof(struct qm_frag_end)); /* init initial fragment*/ qm->first_frag->size=size; qm->last_frag_end->size=size; #ifdef DBG_QM_MALLOC qm->first_frag->check=ST_CHECK_PATTERN; qm->last_frag_end->check1=END_CHECK_PATTERN1; qm->last_frag_end->check2=END_CHECK_PATTERN2; #endif /* init free_hash* */ for (h=0; hfree_hash[h].head.u.nxt_free=&(qm->free_hash[h].head); qm->free_hash[h].tail.prev_free=&(qm->free_hash[h].head); qm->free_hash[h].head.size=0; qm->free_hash[h].tail.size=0; } /* link initial fragment into the free list*/ qm_insert_free(qm, qm->first_frag); /*qm->first_frag->u.nxt_free=&(qm->free_lst); qm->last_frag_end->prev_free=&(qm->free_lst); */ return qm; } static inline void qm_detach_free(struct qm_block* qm, struct qm_frag* frag) { struct qm_frag *prev; struct qm_frag *next; prev=FRAG_END(frag)->prev_free; next=frag->u.nxt_free; prev->u.nxt_free=next; FRAG_END(next)->prev_free=prev; } #ifdef DBG_QM_MALLOC static inline struct qm_frag* qm_find_free(struct qm_block* qm, unsigned long size, int *h, unsigned int *count) #else static inline struct qm_frag* qm_find_free(struct qm_block* qm, unsigned long size, int* h) #endif { int hash; struct qm_frag* f; for (hash=GET_HASH(size); hashfree_hash[hash].head.u.nxt_free; f!=&(qm->free_hash[hash].head); f=f->u.nxt_free){ #ifdef DBG_QM_MALLOC *count+=1; /* *count++ generates a warning with gcc 2.9* -Wall */ #endif if (f->size>=size){ *h=hash; return f; } } /*try in a bigger bucket*/ } /* not found */ return 0; } /* returns 0 on success, -1 on error; * new_size < size & rounded-up already!*/ static inline #ifdef DBG_QM_MALLOC int split_frag(struct qm_block* qm, struct qm_frag* f, unsigned long new_size, const char* file, const char* func, unsigned int line) #else int split_frag(struct qm_block* qm, struct qm_frag* f, unsigned long new_size) #endif { unsigned long rest; struct qm_frag* n; struct qm_frag_end* end; rest=f->size-new_size; #ifdef MEM_FRAG_AVOIDANCE if ((rest> (FRAG_OVERHEAD+QM_MALLOC_OPTIMIZE))|| (rest>=(FRAG_OVERHEAD+new_size))){/* the residue fragm. is big enough*/ #else if (rest>(FRAG_OVERHEAD+MIN_FRAG_SIZE)){ #endif f->size=new_size; /*split the fragment*/ end=FRAG_END(f); end->size=new_size; n=(struct qm_frag*)((char*)end+sizeof(struct qm_frag_end)); n->size=rest-FRAG_OVERHEAD; FRAG_END(n)->size=n->size; FRAG_CLEAR_USED(n); /* never used */ qm->real_used+=FRAG_OVERHEAD; #ifdef DBG_QM_MALLOC end->check1=END_CHECK_PATTERN1; end->check2=END_CHECK_PATTERN2; /* frag created by malloc, mark it*/ n->file=file; n->func=func; n->line=line; n->check=ST_CHECK_PATTERN; #endif /* reinsert n in free list*/ qm_insert_free(qm, n); return 0; }else{ /* we cannot split this fragment any more */ return -1; } } #ifdef DBG_QM_MALLOC void* qm_malloc(struct qm_block* qm, unsigned long size, const char* file, const char* func, unsigned int line) #else void* qm_malloc(struct qm_block* qm, unsigned long size) #endif { struct qm_frag* f; int hash; #ifdef DBG_QM_MALLOC unsigned int list_cntr; list_cntr = 0; MDBG("qm_malloc(%p, %lu) called from %s: %s(%d)\n", qm, size, file, func, line); #endif /*size must be a multiple of 8*/ size=ROUNDUP(size); if (size>(qm->size-qm->real_used)) return 0; /*search for a suitable free frag*/ #ifdef DBG_QM_MALLOC if ((f=qm_find_free(qm, size, &hash, &list_cntr))!=0){ #else if ((f=qm_find_free(qm, size, &hash))!=0){ #endif /* we found it!*/ /*detach it from the free list*/ #ifdef DBG_QM_MALLOC qm_debug_frag(qm, f); #endif qm_detach_free(qm, f); /*mark it as "busy"*/ f->u.is_free=0; qm->free_hash[hash].no--; /* we ignore split return */ #ifdef DBG_QM_MALLOC split_frag(qm, f, size, file, "fragm. from qm_malloc", line); #else split_frag(qm, f, size); #endif qm->real_used+=f->size; qm->used+=f->size; if (qm->max_real_usedreal_used) qm->max_real_used=qm->real_used; #ifdef MALLOC_STATS sr_event_exec(SREV_PKG_SET_USED, (void*)qm->used); sr_event_exec(SREV_PKG_SET_REAL_USED, (void*)qm->real_used); #endif #ifdef DBG_QM_MALLOC f->file=file; f->func=func; f->line=line; f->check=ST_CHECK_PATTERN; /* FRAG_END(f)->check1=END_CHECK_PATTERN1; FRAG_END(f)->check2=END_CHECK_PATTERN2;*/ MDBG("qm_malloc(%p, %lu) returns address %p frag. %p (size=%lu) on %d" " -th hit\n", qm, size, (char*)f+sizeof(struct qm_frag), f, f->size, list_cntr ); #endif return (char*)f+sizeof(struct qm_frag); } return 0; } #ifdef DBG_QM_MALLOC void qm_free(struct qm_block* qm, void* p, const char* file, const char* func, unsigned int line) #else void qm_free(struct qm_block* qm, void* p) #endif { struct qm_frag* f; unsigned long size; #ifdef MEM_JOIN_FREE struct qm_frag* next; struct qm_frag* prev; #endif /* MEM_JOIN_FREE*/ #ifdef DBG_QM_MALLOC MDBG("qm_free(%p, %p), called from %s: %s(%d)\n", qm, p, file, func, line); #endif if (p==0) { #ifdef DBG_QM_MALLOC LOG(L_WARN, "WARNING:qm_free: free(0) called from %s: %s(%d)\n", file, func, line); #else LOG(L_WARN, "WARNING:qm_free: free(0) called\n"); #endif return; } #ifdef DBG_QM_MALLOC if (p>(void*)qm->last_frag_end || p<(void*)qm->first_frag){ LOG(L_CRIT, "BUG: qm_free: bad pointer %p (out of memory block!)" " called from %s: %s(%d) - aborting\n", p, file, func, line); if(likely(cfg_get(core, core_cfg, mem_safety)==0)) abort(); else return; } #endif f=(struct qm_frag*) ((char*)p-sizeof(struct qm_frag)); #ifdef DBG_QM_MALLOC qm_debug_frag(qm, f); if (f->u.is_free){ LOG(L_CRIT, "BUG: qm_free: freeing already freed pointer (%p)," " called from %s: %s(%d), first free %s: %s(%ld) - aborting\n", p, file, func, line, f->file, f->func, f->line); if(likely(cfg_get(core, core_cfg, mem_safety)==0)) abort(); else return; } MDBG("qm_free: freeing frag. %p alloc'ed from %s: %s(%ld)\n", f, f->file, f->func, f->line); #endif size=f->size; qm->used-=size; qm->real_used-=size; #ifdef MALLOC_STATS sr_event_exec(SREV_PKG_SET_USED, (void*)qm->used); sr_event_exec(SREV_PKG_SET_REAL_USED, (void*)qm->real_used); #endif #ifdef MEM_JOIN_FREE if(unlikely(cfg_get(core, core_cfg, mem_join)!=0)) { next=prev=0; /* mark this fragment as used (might fall into the middle of joined frags) to give us an extra chance of detecting a double free call (if the joined fragment has not yet been reused) */ f->u.nxt_free=(void*)0x1L; /* bogus value, just to mark it as free */ /* join packets if possible*/ next=FRAG_NEXT(f); if (((char*)next < (char*)qm->last_frag_end) && (next->u.is_free)){ /* join next packet */ #ifdef DBG_QM_MALLOC qm_debug_frag(qm, next); #endif qm_detach_free(qm, next); size+=next->size+FRAG_OVERHEAD; qm->real_used-=FRAG_OVERHEAD; qm->free_hash[GET_HASH(next->size)].no--; /* FIXME slow */ } if (f > qm->first_frag){ prev=FRAG_PREV(f); /* (struct qm_frag*)((char*)f - (struct qm_frag_end*)((char*)f- sizeof(struct qm_frag_end))->size);*/ if (prev->u.is_free){ /* join prev packet */ #ifdef DBG_QM_MALLOC qm_debug_frag(qm, prev); #endif qm_detach_free(qm, prev); size+=prev->size+FRAG_OVERHEAD; qm->real_used-=FRAG_OVERHEAD; qm->free_hash[GET_HASH(prev->size)].no--; /* FIXME slow */ f=prev; } } f->size=size; FRAG_END(f)->size=f->size; } /* if cfg_core->mem_join */ #endif /* MEM_JOIN_FREE*/ #ifdef DBG_QM_MALLOC f->file=file; f->func=func; f->line=line; #endif qm_insert_free(qm, f); } #ifdef DBG_QM_MALLOC void* qm_realloc(struct qm_block* qm, void* p, unsigned long size, const char* file, const char* func, unsigned int line) #else void* qm_realloc(struct qm_block* qm, void* p, unsigned long size) #endif { struct qm_frag* f; unsigned long diff; unsigned long orig_size; struct qm_frag* n; void* ptr; #ifdef DBG_QM_MALLOC MDBG("qm_realloc(%p, %p, %lu) called from %s: %s(%d)\n", qm, p, size, file, func, line); if ((p)&&(p>(void*)qm->last_frag_end || p<(void*)qm->first_frag)){ LOG(L_CRIT, "BUG: qm_free: bad pointer %p (out of memory block!) - " "aborting\n", p); abort(); } #endif if (size==0) { if (p) #ifdef DBG_QM_MALLOC qm_free(qm, p, file, func, line); #else qm_free(qm, p); #endif return 0; } if (p==0) #ifdef DBG_QM_MALLOC return qm_malloc(qm, size, file, func, line); #else return qm_malloc(qm, size); #endif f=(struct qm_frag*) ((char*)p-sizeof(struct qm_frag)); #ifdef DBG_QM_MALLOC qm_debug_frag(qm, f); MDBG("qm_realloc: realloc'ing frag %p alloc'ed from %s: %s(%ld)\n", f, f->file, f->func, f->line); if (f->u.is_free){ LOG(L_CRIT, "BUG:qm_realloc: trying to realloc an already freed " "pointer %p , fragment %p -- aborting\n", p, f); abort(); } #endif /* find first acceptable size */ size=ROUNDUP(size); if (f->size > size){ orig_size=f->size; /* shrink */ #ifdef DBG_QM_MALLOC MDBG("qm_realloc: shrinking from %lu to %lu\n", f->size, size); if(split_frag(qm, f, size, file, "fragm. from qm_realloc", line)!=0){ MDBG("qm_realloc : shrinked successful\n"); #else if(split_frag(qm, f, size)!=0){ #endif /* update used sizes: freed the splited frag */ /* split frag already adds FRAG_OVERHEAD for the newly created free frag, so here we only need orig_size-f->size for real used */ qm->real_used-=(orig_size-f->size); qm->used-=(orig_size-f->size); #ifdef MALLOC_STATS sr_event_exec(SREV_PKG_SET_USED, (void*)qm->used); sr_event_exec(SREV_PKG_SET_REAL_USED, (void*)qm->real_used); #endif } }else if (f->size < size){ /* grow */ #ifdef DBG_QM_MALLOC MDBG("qm_realloc: growing from %lu to %lu\n", f->size, size); #endif orig_size=f->size; diff=size-f->size; n=FRAG_NEXT(f); if (((char*)n < (char*)qm->last_frag_end) && (n->u.is_free)&&((n->size+FRAG_OVERHEAD)>=diff)){ /* join */ qm_detach_free(qm, n); qm->free_hash[GET_HASH(n->size)].no--; /*FIXME: slow*/ f->size+=n->size+FRAG_OVERHEAD; qm->real_used-=FRAG_OVERHEAD; FRAG_END(f)->size=f->size; /* end checks should be ok */ /* split it if necessary */ if (f->size > size ){ #ifdef DBG_QM_MALLOC split_frag(qm, f, size, file, "fragm. from qm_realloc", line); #else split_frag(qm, f, size); #endif } qm->real_used+=(f->size-orig_size); qm->used+=(f->size-orig_size); #ifdef MALLOC_STATS sr_event_exec(SREV_PKG_SET_USED, (void*)qm->used); sr_event_exec(SREV_PKG_SET_REAL_USED, (void*)qm->real_used); #endif }else{ /* could not join => realloc */ #ifdef DBG_QM_MALLOC ptr=qm_malloc(qm, size, file, func, line); #else ptr=qm_malloc(qm, size); #endif if (ptr){ /* copy, need by libssl */ memcpy(ptr, p, orig_size); #ifdef DBG_QM_MALLOC qm_free(qm, p, file, func, line); #else qm_free(qm, p); #endif } p=ptr; } }else{ /* do nothing */ #ifdef DBG_QM_MALLOC MDBG("qm_realloc: doing nothing, same size: %lu - %lu\n", f->size, size); #endif } #ifdef DBG_QM_MALLOC MDBG("qm_realloc: returning %p\n", p); #endif return p; } void qm_check(struct qm_block* qm) { struct qm_frag* f; long fcount = 0; int memlog; memlog=cfg_get(core, core_cfg, memlog); LOG(memlog, "DEBUG: qm_check()\n"); f = qm->first_frag; while ((char*)f < (char*)qm->last_frag_end) { fcount++; /* check struct qm_frag */ #ifdef DBG_QM_MALLOC if (f->check!=ST_CHECK_PATTERN){ LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) " "beginning overwritten(%lx)!\n", f, (char*)f + sizeof(struct qm_frag), f->check); qm_status(qm); abort(); }; #endif if (f + sizeof(struct qm_frag) + f->size + sizeof(struct qm_frag_end) > qm->first_frag + qm->size) { LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) " "bad size: %lu (frag end: %p > end of block: %p)\n", f, (char*)f + sizeof(struct qm_frag) + sizeof(struct qm_frag_end), f->size, f + sizeof(struct qm_frag) + f->size, qm->first_frag + qm->size); qm_status(qm); abort(); } /* check struct qm_frag_end */ if (FRAG_END(f)->size != f->size) { LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) " "size in qm_frag and qm_frag_end does not match: frag->size=%lu, frag_end->size=%lu)\n", f, (char*)f + sizeof(struct qm_frag), f->size, FRAG_END(f)->size); qm_status(qm); abort(); } #ifdef DBG_QM_MALLOC if ((FRAG_END(f)->check1 != END_CHECK_PATTERN1) || (FRAG_END(f)->check2 != END_CHECK_PATTERN2)) { LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p)" " end overwritten(%lx, %lx)!\n", f, (char*)f + sizeof(struct qm_frag), FRAG_END(f)->check1, FRAG_END(f)->check2); qm_status(qm); abort(); } #endif f = FRAG_NEXT(f); } LOG(memlog, "DEBUG: qm_check: %lu fragments OK\n", fcount); } void qm_status(struct qm_block* qm) { struct qm_frag* f; int i,j; int h; int unused; int memlog; int mem_summary; memlog=cfg_get(core, core_cfg, memlog); mem_summary=cfg_get(core, core_cfg, mem_summary); LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "(%p):\n", qm); if (!qm) return; LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "heap size= %lu\n", qm->size); LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "used= %lu, used+overhead=%lu, free=%lu\n", qm->used, qm->real_used, qm->size-qm->real_used); LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "max used (+overhead)= %lu\n", qm->max_real_used); if (mem_summary & 16) return; LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "dumping all alloc'ed. fragments:\n"); for (f=qm->first_frag, i=0;(char*)f<(char*)qm->last_frag_end;f=FRAG_NEXT(f) ,i++){ if (! f->u.is_free){ LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", " %3d. %c address=%p frag=%p size=%lu used=%d\n", i, (f->u.is_free)?'a':'N', (char*)f+sizeof(struct qm_frag), f, f->size, FRAG_WAS_USED(f)); #ifdef DBG_QM_MALLOC LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", " %s from %s: %s(%ld)\n", (f->u.is_free)?"freed":"alloc'd", f->file, f->func, f->line); LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", " start check=%lx, end check= %lx, %lx\n", f->check, FRAG_END(f)->check1, FRAG_END(f)->check2); #endif } } LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "dumping free list stats :\n"); for(h=0,i=0;hfree_hash[h].head.u.nxt_free,j=0; f!=&(qm->free_hash[h].head); f=f->u.nxt_free, i++, j++){ if (!FRAG_WAS_USED(f)){ unused++; #ifdef DBG_QM_MALLOC LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "unused fragm.: hash = %3d, fragment %p," " address %p size %lu, created from %s: %s(%lu)\n", h, f, (char*)f+sizeof(struct qm_frag), f->size, f->file, f->func, f->line); #endif } } if (j) LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "hash= %3d. fragments no.: %5d, unused: %5d\n" "\t\t bucket size: %9lu - %9ld (first %9lu)\n", h, j, unused, UN_HASH(h), ((h<=QM_MALLOC_OPTIMIZE/ROUNDTO)?1:2)*UN_HASH(h), qm->free_hash[h].head.u.nxt_free->size ); if (j!=qm->free_hash[h].no){ LOG(L_CRIT, "BUG: qm_status: different free frag. count: %d!=%lu" " for hash %3d\n", j, qm->free_hash[h].no, h); } } LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "-----------------------------\n"); } /* fills a malloc info structure with info about the block * if a parameter is not supported, it will be filled with 0 */ void qm_info(struct qm_block* qm, struct mem_info* info) { int r; long total_frags; total_frags=0; memset(info,0, sizeof(*info)); info->total_size=qm->size; info->min_frag=MIN_FRAG_SIZE; info->free=qm->size-qm->real_used; info->used=qm->used; info->real_used=qm->real_used; info->max_used=qm->max_real_used; for(r=0;rfree_hash[r].no; } info->total_frags=total_frags; } /* returns how much free memory is available * it never returns an error (unlike fm_available) */ unsigned long qm_available(struct qm_block* qm) { return qm->size-qm->real_used; } #ifdef DBG_QM_MALLOC typedef struct _mem_counter{ const char *file; const char *func; unsigned long line; unsigned long size; int count; struct _mem_counter *next; } mem_counter; static mem_counter* get_mem_counter(mem_counter **root, struct qm_frag* f) { mem_counter *x; if (!*root) goto make_new; for(x=*root;x;x=x->next) if (x->file == f->file && x->func == f->func && x->line == f->line) return x; make_new: x = malloc(sizeof(mem_counter)); x->file = f->file; x->func = f->func; x->line = f->line; x->count = 0; x->size = 0; x->next = *root; *root = x; return x; } void qm_sums(struct qm_block* qm) { struct qm_frag* f; int i; mem_counter *root, *x; int memlog; root=0; if (!qm) return; memlog=cfg_get(core, core_cfg, memlog); LOG_(DEFAULT_FACILITY, memlog, "qm_sums: ", "summarizing all alloc'ed. fragments:\n"); for (f=qm->first_frag, i=0;(char*)f<(char*)qm->last_frag_end; f=FRAG_NEXT(f),i++){ if (! f->u.is_free){ x = get_mem_counter(&root,f); x->count++; x->size+=f->size; } } x = root; while(x){ LOG_(DEFAULT_FACILITY, memlog, "qm_sums: ", " count=%6d size=%10lu bytes from %s: %s(%ld)\n", x->count,x->size, x->file, x->func, x->line ); root = x->next; free(x); x = root; } LOG_(DEFAULT_FACILITY, memlog, "qm_sums: ", "-----------------------------\n"); } #endif /* DBG_QM_MALLOC */ #endif kamailio-4.0.4/mem/dl_config.h0000644000000000000000000000032612223032460014651 0ustar rootroot#ifndef _DL_CONFIG_H #define _DL_CONFIG_H #define MSPACES 1 #define USE_DL_PREFIX 1 #define MALLOC_ALIGNMENT 16 /* enable FOOTERS for extra consistency checks */ /* #define FOOTERS 1 */ #endif /* _DL_CONFIG_H */ kamailio-4.0.4/mem/ll_malloc.h0000644000000000000000000001165012223032460014665 0ustar rootroot/* $Id$ * * shared memory, multi-process safe, pool based, mostly lockless version of * f_malloc * * Copyright (C) 2007 iptelorg GmbH * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * History: * -------- * 2003-05-21 on sparc64 roundto 8 even in debugging mode (so malloc'ed * long longs will be 64 bit aligned) (andrei) * 2004-07-19 support for 64 bit (2^64 mem. block) and more info * for the future de-fragmentation support (andrei) * 2004-11-10 support for > 4Gb mem., switched to long (andrei) * 2007-06-11 forked from the sf_malloc code (andrei) */ #if !defined(ll_malloc_h) #define ll_malloc_h #include "meminfo.h" #include "../lock_ops.h" #include "../atomic_ops.h" #include "../compiler_opt.h" /* defs*/ #ifdef GEN_LOCK_T_UNLIMITED #define SFM_LOCK_PER_BUCKET #else #define SFM_ONE_LOCK #endif #ifdef DBG_SF_MALLOC #if defined(__CPU_sparc64) || defined(__CPU_sparc) /* tricky, on sun in 32 bits mode long long must be 64 bits aligned * but long can be 32 bits aligned => malloc should return long long * aligned memory */ #define SF_ROUNDTO sizeof(long long) #else #define SF_ROUNDTO sizeof(void*) /* size we round to, must be = 2^n, and sizeof(sfm_frag) must be multiple of SF_ROUNDTO !*/ #endif #else /* DBG_SF_MALLOC */ #define SF_ROUNDTO 8UL #endif #define SF_MIN_FRAG_SIZE SF_ROUNDTO #define SFM_POOLS_NO 4U /* the more the better, but higher initial mem. consumption */ #define SF_MALLOC_OPTIMIZE_FACTOR 14UL /*used below */ #define SF_MALLOC_OPTIMIZE (1UL<

dlg.list Print all dialogs
dlg.list_ctx Print all dialogs with associated context
dlg.dlg_list Print dialog based on callid and fromtag
dlg.dlg_list_ctx Print dialog with associated context based on callid and fromtag
dlg.end_dlg End a given dialog based on [h_entry] [h_id]
dlg.profile_get_size Returns the number of dialogs belonging to a profile
dlg.profile_list Lists all the dialogs belonging to a profile
dlg.bridge_dlg Bridge two SIP addresses in a call using INVITE(hold)-REFER-BYE mechanism: to, from, [outbound SIP proxy]
kamailio-4.0.4/doc/rpc_list/docbook/rpc_cfg_rpc.xml0000644000000000000000000000672312223032457021010 0ustar rootroot RPC Exports for cfg_rpc
cfg.set Set the value of a configuration variable and commit the change immediately
cfg.set_now_int Set the value of a configuration variable and commit the change immediately
cfg.seti Set the value of a configuration variable and commit the change immediately
cfg.set_now_string Set the value of a configuration variable and commit the change immediately
cfg.sets Set the value of a configuration variable and commit the change immediately
cfg.del Delete the value of a configuration variable from a group instance and commit the change immediately
cfg.set_delayed Prepare the change of a configuration variable, but does not commit the new value yet
cfg.set_delayed_int Prepare the change of a configuration variable, but does not commit the new value yet
cfg.set_delayed_string Prepare the change of a configuration variable, but does not commit the new value yet
cfg.del_delayed Prepare the deletion of the value of a configuration variable from a group instance, but does not commit the change yet
cfg.commit Commit the previously prepared configuration changes
cfg.rollback Drop the prepared configuration changes
cfg.get Get the value of a configuration variable
cfg.help Print the description of a configuration variable
cfg.list List the configuration variables
cfg.diff List the pending configuration changes that have not been committed yet
cfg.add_group_inst Add a new instance to an existing configuration group
cfg.del_group_inst Delte an instance of a configuration group
kamailio-4.0.4/doc/rpc_list/docbook/rpc_presence_b2b.xml0000644000000000000000000000107512223032461021724 0ustar rootroot RPC Exports for presence_b2b
presence_b2b.test Testing events.
presence_b2b.trace Trace events.
kamailio-4.0.4/doc/rpc_list/docbook/rpc_pdt.xml0000644000000000000000000000134012223032461020155 0ustar rootroot RPC Exports for pdt
pdt.add Add new prefix/domain translation rule.
pdt.delete Delete prefix/domain translation rule.
pdt.list List existin prefix/domain translation rules Returns an array.
kamailio-4.0.4/doc/rpc_list/docbook/rpc_gflags.xml0000644000000000000000000000215312223032461020634 0ustar rootroot RPC Exports for gflags
gflags.set Load a CPL script to the server.
gflags.is_set Load a CPL script to the server.
gflags.reset Load a CPL script to the server.
gflags.flush Load a CPL script to the server.
gflags.dump Load a CPL script to the server.
global.reload Reload global attributes from database
kamailio-4.0.4/doc/rpc_list/docbook/rpc_cpl-c.xml0000644000000000000000000000124512223032461020370 0ustar rootroot RPC Exports for cpl-c
cpl.load Load a CPL script to the server.
cpl.remove Remove a CPL script from server.
cpl.get Return a CPL script.
kamailio-4.0.4/doc/rpc_list/docbook/rpc_debugger.xml0000644000000000000000000000124612223032461021157 0ustar rootroot RPC Exports for debugger
dbg.bp Documentation missing (dbg_rpc_bp_doc).
dbg.ls List debugging process array
dbg.trace Config trace command
kamailio-4.0.4/doc/rpc_list/docbook/rpc_malloc_test.xml0000644000000000000000000000700412223032457021704 0ustar rootroot RPC Exports for malloc_test
mt.mem_alloc Allocates the specified number of bytes (debugging/test function).Use b|k|m|g to specify the desired size unit
mt.mem_free Frees the specified number of bytes, previously allocated by one of the other malloc_test functions (e.g. mt.mem_alloc or the script mt_mem_alloc). Use b|k|m|g to specify the desired size unit.Returns the number of bytes freed (can be higher or smaller then the requested size)
mt.mem_realloc Reallocates the specified number of bytes from a pre-allocated randomly selected memory chunk. If no pre-allocated memory chunks exists, it will fail. Make sure mt.mem_used is non 0 or call mt.mem_alloc prior to calling this function. Returns the difference in bytes (<0 if bytes were freed, >0 if more bytes were allocated).Use b|k|m|g to specify the desired size unit
mt.mem_used Returns how many memory chunks and how many bytes are currently allocated via the mem_alloc module functions. Use b|k|m|g to specify the desired size unit.
mt.mem_rnd_alloc Takes 4 parameters: min, max, total_size and an optional unit (b|k|m|g). It will allocate total_size memory, in pieces of random size betweenmin .. max (inclusive).
mt.mem_test_start Takes 7 parameters: min, max, total_size, min_interval, max_interval, test_time and an optional size unit (b|k|m|g). All the time units are ms. It will run a memory allocation test for test_time ms. At a random interval between min_interval and max_interval ms. it will allocate a memory chunk with random size, between min and max. Each time total_size is reached, it will free all the memory allocated and start again.Returns the test id (integer)
mt.mem_test_stop Takes 1 parameter: the test id. It will stop the corresponding test.Note: the test is stopped, but not destroyed.
mt.mem_test_destroy Takes 1 parameter: the test id. It will destroy the corresponding test.
mt.mem_test_destroy_all It will destroy all the tests (running or stopped).
mt.mem_test_list If a test id parameter is provided it will list the corresponding test, else it will list all of them. Use b |k | m | g as a second parameter for the size units (default bytes)
kamailio-4.0.4/doc/rpc_list/docbook/rpc_sl.xml0000644000000000000000000000065312223032461020012 0ustar rootroot RPC Exports for sl
sl.stats Documentation missing (rpc_stats_doc).
kamailio-4.0.4/doc/rpc_list/docbook/rpc_db_flatstore.xml0000644000000000000000000000072112223032461022040 0ustar rootroot RPC Exports for db_flatstore
flatstore.rotate Documentation missing (flat_rotate_doc).
kamailio-4.0.4/doc/rpc_list/docbook/rpc_usrloc_k.xml0000644000000000000000000000065012223032461021212 0ustar rootroot RPC Exports for usrloc_k
ul.dump Dump user location tables
kamailio-4.0.4/doc/rpc_list/docbook/rpc_uac.xml0000644000000000000000000000067512223032457020155 0ustar rootroot RPC Exports for uac
uac.reg_dump Dump the contents of user registrations table.
kamailio-4.0.4/doc/rpc_list/docbook/rpc_counters.xml0000644000000000000000000000265312223032457021245 0ustar rootroot RPC Exports for counters
cnt.get get counter value (takes group and counter name as parameters)
cnt.reset reset counter (takes group and counter name as parameters)
cnt.get_raw get raw counter value (debugging version)
cnt.grps_list list all the counter group names Returns an array.
cnt.var_list list all the counters names in a specified group Returns an array.
cnt.grp_get_all list all counter names and values in a specified group
cnt.help print the description of a counter (group and counter name required).
kamailio-4.0.4/doc/rpc_list/docbook/rpc_domain_s.xml0000644000000000000000000000110712223032461021160 0ustar rootroot RPC Exports for domain_s
domain.reload Reload domain table from database
domain.dump Return the contents of domain table
kamailio-4.0.4/doc/rpc_list/docbook/rpc_domain.xml0000644000000000000000000000110312223032461020632 0ustar rootroot RPC Exports for domain
domain.reload Reload domain table from database
domain.dump Return the contents of domain table
kamailio-4.0.4/doc/rpc_list/docbook/rpc_prefix_route.xml0000644000000000000000000000115712223032461022107 0ustar rootroot RPC Exports for prefix_route
prefix_route.reload Documentation missing (rpc_reload_doc).
prefix_route.dump Documentation missing (rpc_dump_doc).
kamailio-4.0.4/doc/rpc_list/docbook/rpc_list.xml0000644000000000000000000000343212223032461020345 0ustar rootroot ] > RPC Exports List sip-router git-0ea931 Fri, 25 Nov 2011 23:59:50 +0100 Automatically generated by: make -C doc/rpc_list all kamailio-4.0.4/doc/rpc_list/docbook/rpc_core.xml0000644000000000000000000001664412223032461020333 0ustar rootroot RPC Exports for core
system.listMethods Lists all RPC methods supported by the server. Returns an array.
system.methodSignature Returns signature of given method.
system.methodHelp Print the help string for given method.
core.prints Returns the strings given as parameters. Returns an array.
core.printi Returns the integers given as parameters. Returns an array.
core.echo Returns back its parameters. Returns an array.
core.version Returns the version string of the server.
core.flags Returns the compile flags.
core.info Verbose info, including version number, compile flags, compiler,repository hash a.s.o.
core.uptime Returns uptime of SER server.
core.ps Returns the description of running SER processes. Returns an array.
core.psx Returns the detailed description of running SER processes.
core.pwd Returns the working directory of SER server. Returns an array.
core.arg Returns the list of command line arguments used on SER startup. Returns an array.
core.kill Sends the given signal to SER.
core.shmmem Returns shared memory info. It has an optional parameter that specifies the measuring unit: b - bytes (default), k or kb, m or mb, g or gb. Note: when using something different from bytes, the value is truncated.
core.tcp_info Returns tcp related info.
core.tcp_options Returns active tcp options.
core.sctp_options Returns active sctp options. With one parameter it returns the sctp options set in the kernel for a specific socket(debugging), with 0 filled in for non-kernel related options. The parameter can be: "default" | "first" | address[:port] . With no parameters it returns ser's idea of the current sctp options (intended non-debugging use).
core.sctp_info Returns sctp related info.
core.udp4_raw_info Returns udp4_raw related info.
dns.mem_info dns cache memory info.
dns.debug dns debug info.
dns.debug_all complete dns debug dump
dns.view dns cache dump in a human-readable format
dns.lookup perform a dns lookup
dns.delete_all deletes all the non-permanent entries from the DNS cache
dns.delete_all_force deletes all the entries from the DNS cache including the permanent ones
dns.add_a adds an A record to the DNS cache
dns.add_aaaa adds an AAAA record to the DNS cache
dns.add_srv adds an SRV record to the DNS cache
dns.delete_a deletes an A record from the DNS cache
dns.delete_aaaa deletes an AAAA record from the DNS cache
dns.delete_srv deletes an SRV record from the DNS cache
dns.delete_naptr deletes a NAPTR record from the DNS cache
dns.delete_cname deletes a CNAME record from the DNS cache
dns.delete_txt deletes a TXT record from the DNS cache
dns.delete_ebl deletes an EBL record from the DNS cache
dns.delete_ptr deletes an PTR record from the DNS cache
dst_blacklist.mem_info dst blacklist memory usage info.
dst_blacklist.debug dst blacklist debug info.
dst_blacklist.view dst blacklist dump in human-readable format.
dst_blacklist.delete_all Deletes all the entries from the dst blacklist except the permanent ones.
dst_blacklist.add Adds a new entry to the dst blacklist.
kamailio-4.0.4/doc/rpc_list/docbook/rpc_usrloc_s.xml0000644000000000000000000000310012223032461021213 0ustar rootroot RPC Exports for usrloc_s
usrloc.stats Documentation missing (rpc_stats_doc). Returns an array.
usrloc.delete_uid Documentation missing (rpc_delete_uid_doc).
usrloc.delete_contact Documentation missing (rpc_delete_contact_doc).
usrloc.dump Documentation missing (rpc_dump_doc).
usrloc.dump_file Documentation missing (rpc_dump_file_doc).
usrloc.flush Documentation missing (rpc_flush_doc).
usrloc.add_contact Documentation missing (rpc_add_contact_doc).
usrloc.show_contacts Documentation missing (rpc_show_contacts_doc). Returns an array.
kamailio-4.0.4/doc/rpc_list/docbook/rpc_ctl.xml0000644000000000000000000000125112223032457020156 0ustar rootroot RPC Exports for ctl
ctl.who list open connections
ctl.connections returns number of open connections
ctl.listen list ctl listen sockets
kamailio-4.0.4/doc/rpc_list/docbook/rpc_dispatcher_s.xml0000644000000000000000000000113412223032461022037 0ustar rootroot RPC Exports for dispatcher_s
dispatcher.dump Dump dispatcher set configuration
dispatcher.reload Reload dispatcher list from file
kamailio-4.0.4/doc/rpc_list/docbook/rpc_pike.xml0000644000000000000000000000062612223032461020324 0ustar rootroot RPC Exports for pike
pike.top pike.top doc.
kamailio-4.0.4/doc/rpc_list/docbook/rpc_tls.xml0000644000000000000000000000155212223032461020175 0ustar rootroot RPC Exports for tls
tls.reload Documentation missing (tls_reload_doc).
tls.list Documentation missing (tls_list_doc). Returns an array.
tls.info Documentation missing (tls_info_doc).
tls.options Documentation missing (tls_options_doc).
kamailio-4.0.4/doc/rpc_list/docbook/rpc_dialplan.xml0000644000000000000000000000111212223032461021147 0ustar rootroot RPC Exports for dialplan
dialplan.reload Reload dialplan table from database
dialplan.dump Perform dialplan translation
kamailio-4.0.4/doc/rpc_list/docbook/rpc_mi_rpc.xml0000644000000000000000000000152112223032457020645 0ustar rootroot RPC Exports for mi_rpc
mi Execute MI command Returns an array.
mi_fifo Execute MI command Returns an array.
mi_dg Execute MI command Returns an array.
mi_xmlrpc Execute MI command Returns an array.
kamailio-4.0.4/doc/rpc_list/docbook/rpc_usrloc.xml0000644000000000000000000000064412223032461020703 0ustar rootroot RPC Exports for usrloc
ul.dump Dump user location tables
kamailio-4.0.4/doc/rpc_list/docbook/rpc_kex.xml0000644000000000000000000000066412223032457020172 0ustar rootroot RPC Exports for kex
pkg.stats Private memory (pkg) statistics per process
kamailio-4.0.4/doc/rpc_list/docbook/rpc_tm.xml0000644000000000000000000000224612223032461020014 0ustar rootroot RPC Exports for tm
tm.cancel Documentation missing (rpc_cancel_doc).
tm.reply Documentation missing (rpc_reply_doc).
tm.stats Documentation missing (tm_rpc_stats_doc).
tm.hash_stats Documentation missing (tm_rpc_hash_stats_doc).
tm.t_uac_start Documentation missing (rpc_t_uac_start_doc).
tm.t_uac_wait Documentation missing (rpc_t_uac_wait_doc). Returns an array.
kamailio-4.0.4/doc/rpc_list/docbook/rpc_lcr.xml0000644000000000000000000000131712223032461020152 0ustar rootroot RPC Exports for lcr
lcr.reload Reload lcr tables from database.
lcr.dump_gws Dump the contents of lcr_gws table.
lcr.dump_rules Dump the contents of the lcr_rules table.
kamailio-4.0.4/doc/rpc_list/docbook/rpc_htable.xml0000644000000000000000000000066312223032461020634 0ustar rootroot RPC Exports for htable
htable.dump Dump the contents of hash table.
kamailio-4.0.4/doc/rpc_list/docbook/rpc_dispatcher.xml0000644000000000000000000000137612223032457021532 0ustar rootroot RPC Exports for dispatcher
dispatcher.reload Reload dispatcher destination sets
dispatcher.list Return the content of dispatcher sets
dispatcher.set_state Set the state of a destination address
kamailio-4.0.4/doc/rpc_list/docbook/Makefile0000644000000000000000000000025712223032457017453 0ustar rootroot docs = rpc_list.xml docbook_dir = ../../../docbook # no subsection listed in the main TOC xsltproc_flags+=--stringparam toc.section.depth 0 include $(docbook_dir)/Makefile kamailio-4.0.4/doc/rpc_list/docbook/rpc_ratelimit.xml0000644000000000000000000000415412223032457021373 0ustar rootroot RPC Exports for ratelimit
rl.stats Print ratelimit statistics: PIPE[<pipe_id>]: <last_counter>/<pipe_limit> (drop rate: <drop_rate>)
rl.get_pipes Print pipes info: PIPE[<pipe_id>]: <pipe_algo_id>:<pipe_algo> <last_counter>/<pipe_limit> (drop rate: <drop_rate>) [<current_counter>]
rl.set_pipe Sets a pipe params: <pipe_id> <pipe_algorithm> <pipe_limit>
rl.get_queues Print queues info: QUEUE[queue_id]: <pipe_id>:<queue_method>
rl.set_queue Sets queue params: <quue_id> <queue_method> <pipe_id>
rl.get_pid Print PID Controller parameters for the FEEDBACK algorithm: <ki> <kp> <kd>
rl.set_pid Sets the PID Controller parameters for the FEEDBACK algorithm: <ki> <kp> <kd>
rl.push_load Force the value of the load parameter for FEEDBACK algorithm: <load>
rl.set_dbg Sets the ratelimit debug/monitoing logs: 0-off 1-on
kamailio-4.0.4/doc/rpc_list/rpc_uac.txt0000644000000000000000000000026212223032457016544 0ustar rootrootRPC Exports for uac =================== [ this file is autogenerated, do not edit ] 1. uac.reg_dump Dump the contents of user registrations table. kamailio-4.0.4/doc/rpc_list/rpc_domain.txt0000644000000000000000000000035112223032461017235 0ustar rootrootRPC Exports for domain ====================== [ this file is autogenerated, do not edit ] 1. domain.reload Reload domain table from database 2. domain.dump Return the contents of domain table kamailio-4.0.4/doc/rpc_list/rpc_dispatcher_s.txt0000644000000000000000000000037212223032461020441 0ustar rootrootRPC Exports for dispatcher_s ============================ [ this file is autogenerated, do not edit ] 1. dispatcher.dump Dump dispatcher set configuration 2. dispatcher.reload Reload dispatcher list from file kamailio-4.0.4/doc/rpc_list/rpc_debugger.txt0000644000000000000000000000041412223032461017552 0ustar rootrootRPC Exports for debugger ======================== [ this file is autogenerated, do not edit ] 1. dbg.bp Documentation missing (dbg_rpc_bp_doc). 2. dbg.ls List debugging process array 3. dbg.trace Config trace command kamailio-4.0.4/doc/rpc_list/Makefile0000644000000000000000000003045312223032461016027 0ustar rootroot COREPATH=../.. #include $(COREPATH)/Makefile.defs CFG2TXT=../scripts/cdefs2doc/dump_rpcs.pl CFG2DOCBOOK=../scripts/cdefs2doc/dump_rpcs.pl # output directory for generated txt files txt_output_dir=. # output directory for generated docbook xml files docbook_output_dir=docbook # list of files containing rpc defs in the following format: # : # can be easily updated by adding the output of: # make diff-list (which obeys grp_exclude and file_exclude) # or completely regenerated by replacing files_list with the output of: # make gen-files-list # NOTE: suffix duplicated modules located in modules_s with '_s' to # avoid file naming conflicts # files_list= \ $(COREPATH)/core_cmd.c:core \ $(COREPATH)/modules/cfg_rpc/cfg_rpc.c:cfg_rpc \ $(COREPATH)/modules/counters/counters.c:counters \ $(COREPATH)/modules/ctl/ctl.c:ctl \ $(COREPATH)/modules/db_flatstore/flat_rpc.c:db_flatstore \ $(COREPATH)/modules/debugger/debugger_api.c:debugger \ $(COREPATH)/modules/dialplan/dialplan.c:dialplan \ $(COREPATH)/modules/lcr/lcr_rpc.c:lcr \ $(COREPATH)/modules/malloc_test/malloc_test.c:malloc_test \ $(COREPATH)/modules/mi_rpc/mi_rpc_mod.c:mi_rpc \ $(COREPATH)/modules/prefix_route/pr_rpc.c:prefix_route \ $(COREPATH)/modules/ratelimit/ratelimit.c:ratelimit \ $(COREPATH)/modules/sl/sl_stats.c:sl \ $(COREPATH)/modules/tls/tls_rpc.c:tls \ $(COREPATH)/modules/tm/tm.c:tm \ $(COREPATH)/modules_k/dialog/dialog.c:dialog \ $(COREPATH)/modules_k/dispatcher/dispatcher.c:dispatcher \ $(COREPATH)/modules_k/domain/domain_mod.c:domain \ $(COREPATH)/modules_k/htable/htable.c:htable \ $(COREPATH)/modules_k/kex/pkg_stats.c:kex \ $(COREPATH)/modules_k/uac/uac_reg.c:uac \ $(COREPATH)/modules_k/usrloc/ul_rpc.c:usrloc \ $(COREPATH)/modules_s/cpl-c/cpl_rpc.c:cpl-c \ $(COREPATH)/modules_s/dispatcher/ds_rpc.c:dispatcher_s \ $(COREPATH)/modules_s/domain/domain_rpc.c:domain_s \ $(COREPATH)/modules_s/gflags/gflags.c:gflags \ $(COREPATH)/modules_s/pdt/pdt.c:pdt \ $(COREPATH)/modules_s/pike/rpc.c:pike \ $(COREPATH)/modules_s/presence_b2b/rpc.c:presence_b2b \ $(COREPATH)/modules_s/usrloc/ul_rpc.c:usrloc_s # list of excluded groups grp_exclude=pa # list of file prefixes to exclude (full path needed) file_exclude=$(COREPATH)/modules_s/tls/ \ $(COREPATH)/obsolete # special per file group overrides # format= grp_filename=... ,where filename does not contain the extension # e.g.: # grp_f_tcp_options=tcp # grp_f_sctp_options=sctp # special per group group name overrides # e.g.: # grp_g_maxfwd=mf # override auto-detected group if set to 1 (else the group is used inside the # file only if it cannot be aut-odetected) ifeq ($(group_override),1) override force_grp=force- else override force_grp= endif # command used for gcc (contains extra includes) gcc=gcc #-I$(COREPATH)/lib -I$(COREPATH) -I/usr/include/libxml2 # defines used by gcc # -D__CPU_i386 -DARCH="i386" c_defsX= -D__CPU_x86_64 -D__OS_linux -DSER_VER=3003000 -DPKG_MALLOC -DSHM_MEM \ -DVERSION='\"3.3.0-dev2\"' -DARCH='\"x86_64\"' -DOS=linux_ -DOS_QUOTED='\"linux\"' \ -DSHM_MMAP -DDNS_IP_HACK -DUSE_IPV6 -DUSE_MCAST -DUSE_TCP \ -DUSE_DNS_CACHE -DUSE_DNS_FAILOVER -DUSE_DST_BLACKLIST -DUSE_NAPTR \ -DUSE_TLS -DTLS_HOOKS -DFAST_LOCK -DCC_GCC_LIKE_ASM \ -DHAVE_GETHOSTBYNAME2 -DHAVE_UNION_SEMUN -DHAVE_SCHED_YIELD \ -DHAVE_MSG_NOSIGNAL -DHAVE_MSGHDR_MSG_CONTROL -DHAVE_ALLOCA_H \ -DHAVE_SCHED_SETSCHEDULER -DHAVE_EPOLL -DUSE_SCTP -DNAME='\"ser\"' \ -DCFG_DIR='\"/tmp/\"' c_defs=$(subst ^^,='\",$(subst ",\"',$(subst =",^^,$(shell make -s -C ../.. printcdefs)))) # common makefile vars used in defs LOCALBASE=/usr/local SYSBASE=/usr filter_files=$(filter-out $(addsuffix %,$(file_exclude)),\ $(filter-out $(addprefix %:,$(grp_exclude)),$(1))) #filtered files list flist=$(call filter_files,$(files_list)) # throws an error if input is not in the format filename:grp check_fname_grp=$(if $(filter-out 2,$(words $(subst :, ,$(1)))),\ $(error bad format "$(1)", it should be filename:grp)) # get prereq from file:grp (get_prereq(file:grp) => file) get_prereq=$(firstword $(subst :, ,$(1))) # get grp from file:grp (get_grp(file:grp) => grp) get_listed_grp=$(word 2, $(subst :, ,$(1))) # get module interface define get_modiface=$(word 3, $(subst :, ,$(1))) find_modiface=$(if $(findstring modules,$(1)),$(shell make -s -C $(dir $(1)) printmiface),-DNONE) # get base file name from file:grp: get_bname(file:grp) # => basename(file) without extension (e.g. get_bname(foo/bar.c:x) => bar) # get_bname=$(basename $(notdir $(call get_prereq,$(1)))) #get grp from file:grp, using the overrides get_grp=$(strip $(if $(grp_f_$(call get_bname,$(1))), \ $(grp_f_$(call get_bname,$(1))),\ $(if $(grp_g_$(call get_listed_grp,$(1))),\ $(grp_g_$(call get_listed_grp,$(1))),\ $(call get_listed_grp,$(1))) ) ) # get target from file:grp (get_target(file:grp) => rpc_grp.txt) get_target=rpc_$(call get_grp,$(1)) # $(LF) definition (do not remove) define LF endef # get all the lines containing DEFS or INCLUDES definitions from the Makefile. # WARNING: does not work with all sed implementation (tested with GNU sed). # It uses a hack to restore the LFs (LFs are removed by $(shell)): LFs are # replaced with '^LF^' and then ^LF^ is subst'ed back to a real LF. get_make_idefs=$(subst ^LF^,$(LF),$(shell sed \ -ne '/^[\t ]*\(DEFS\|INCLUDES\)[\t ]*[+:]\?=.*[^\]$$/H'\ -ne '/^[\t ]*\(DEFS\|INCLUDES\)[\t ]*[+:]\?=.*\\$$/,/\(^$$\)\|\([^\]$$\)/H'\ -ne '$${g;s/\n/^LF^/g;p}'\ < $(1)/Makefile )) # get all the lines from the makefile containing variable definitions. # It will also return conditionals and try to filter out possible rules. # WARNING: does not work with all sed implementation (tested with GNU sed). # It uses a hack to restore the LFs (LFs are removed by $(shell)): LFs are # replaced with '^LF^' and then ^LF^ is subst'ed back to a real LF. get_make_vars=$(subst ^LF^,$(LF),$(shell sed -n \ -e ': start' \ -e '/^\(ifeq\|ifneq\|else\|endif\)[\t ]*\($$\|.*[^\]$$\)/{H;b end}' \ -e '/^\(ifeq\|ifneq\|else\|endif\)[\t ]\+.*[\]$$/,/[^\]$$/{H;b end}' \ -e '/^[a-zA-Z._/$$][a-zA-Z0-9._()/$$ \t-]*:\([^=]\|$$\)/b eat_rule' \ -e '/^[\t ]*[A-Za-z._][A-Za-z0-9._-]*[\t ]*[+:]\?=.*[^\]$$/{H;b end}' \ -e '/^[\t ]*[A-Za-z._][A-Za-z0-9._-]*[\t ]*[+:]\?=.*\\$$/,/\(^$$\)\|\([^\]$$\)/{H;b end}' \ -e ': end' \ -e '$${g;s/\n/^LF^/g;p}'\ -e 'b' \ -e ': eat_rule' \ -e '$$b end' \ -e 'n' \ -e '/^[a-zA-Z._/$$][a-zA-Z0-9._()/$$ \t-]*:\([^=]\|$$\)/b eat_rule' \ -e '/^[\t]/b eat_rule' \ -e 'b start' \ < $(1)/Makefile )) define mk_rules $(call check_fname_grp, $(1)) #$$(info generating rpc_$$(call get_grp,$(1)).txt: $$(call get_prereq,$(1))) DEFS:= INCLUDES:= # extract all the includes and defs from the module makefile and # evaluate them $$(eval $$(call get_make_vars,$$(dir $$(call get_prereq,$(1))))) # override COREPATH (we know better) COREPATH=../.. # save the result in a per group e_idefs_ var $$(eval e_idefs_$$(call get_grp,$(1)):=$$(DEFS) $$(INCLUDES)) # debugging: #$$(info eval: $$(call get_make_vars,$$(dir $$(call get_prereq,$(1))))) #$$(info e_idefs_$$(call get_grp,$(1))=$$(e_idefs_$$(call get_grp,$(1)))) $(txt_output_dir)/$$(call get_target,$(1)).txt: \ $$(call get_prereq,$(1)) Makefile $(CFG2TXT) $(CFG2TXT) --file $$< --$(force_grp)grp=$$(call get_grp,$(1)) \ --gcc="$(gcc)" --txt \ --defs="$(c_defs) $$(call get_modiface,$(1)) $$(e_idefs_$$(call get_grp,$(1)))" \ > "$$@" || (rm -f "$$@"; exit 1) $(docbook_output_dir)/$$(call get_target,$(1)).xml: \ $$(call get_prereq,$(1)) Makefile $(CFG2TXT) $(CFG2DOCBOOK) --file $$< --$(force_grp)grp=$$(call get_grp,$(1)) \ --gcc="$(gcc)" --docbook \ --defs="$(c_defs) $$(e_idefs_$$(call get_grp,$(1)))" \ > "$$@" || (rm -f "$$@"; exit 1) clean_$$(call get_target,$(1)).txt: rm -f "$(txt_output_dir)/$$(call get_target,$(1)).txt" clean_$$(call get_target,$(1)).xml: rm -f "$(docbook_output_dir)/$$(call get_target,$(1)).xml" txt: $(txt_output_dir)/$$(call get_target,$(1)).txt docbook: $(docbook_output_dir)/$$(call get_target,$(1)).xml clean_txt: clean_$$(call get_target,$(1)).txt clean_docbook: clean_$$(call get_target,$(1)).xml endef find_rpc_files_cmd= find $(COREPATH) -type f -name "*.c" \ -exec grep "rpc_export_t[ ][a-zA-Z0-9_]*\[\][ ]*=" /dev/null {} \; \ | cut -d: -f1 # shell command to generate a file:grp list from a list of files # grp will be the modulename if the file is in a module directory or # the file name with the extension and _cmd, cmd_ or _rpc stripped out of # it. # output: list of " "filename":"grpname gen_file_grp_cmd=\ sed -e "s!\(.*/modules[^/]*/\([^/][^/]*\)/.*\)! \1:\2!" \ -e "s!^\([^ ].*/\([^/.]*\)[^/]*$$\)!\1:\2!" \ -e "s!^\([^ :]*\):\(.*\)_cmd[_]*!\1:\2!" \ -e "s!^\([^ :]*\):\(.*\)cmd[_]*!\1:\2!" \ -e "s!^\([^ :]*\):\(.*\)_rpc[_]*!\1:\2!" # special vars for generating the list of files or updates found_lst=$(shell $(find_rpc_files_cmd) | $(gen_file_grp_cmd)) # filtered found lst f_found_lst=$(call filter_files,$(found_lst)) diff_lst=$(filter-out $(flist),$(f_found_lst)) get_core_files=$(filter-out $(COREPATH)/modules% $(COREPATH)/lib%,$(1)) sort_files=$(sort $(call get_core_files,$(1)))\ $(sort $(filter-out $(call get_core_files,$(1)),$(1))) # replace $(COREPATH) with the text "$(COREPATH)" subst_corepath=$(patsubst $(patsubst %/,%,$(COREPATH))/%,\$$(COREPATH)/%,$(1)) # help will be the default rule (on-purpose since without having a patched # GCC:TranslationUnit module, make all won't work) .PHONY: help help: @echo "To regenerate $(foreach f,$(flist),$(call get_target,$f).{txt,xml})" @echo "type: $(MAKE) all ." @echo "or to regenerate all the rpc lists by searching all" @echo " the source files for definitions, type: $(MAKE) autogen ." @echo "NOTE: you need the GCC:TranslationUnit perl module with an " @echo "extra patch applied (see $(CFG2TXT) --patch)." .PHONY: txt txt: .PHONY: docbook docbook: .PHONY: clean_txt clean_txt: .PHONY: clean_docbook clean_docbook: .PHONY: all all: txt $(docbook_output_dir)/rpc_list.xml .PHONY: clean clean: clean_txt clean_docbook @rm -f $(docbook_output_dir)/rpc_list.xml .PHONY: proper proper: @rm -f $(txt_output_dir)/rpc_*.txt @rm -f $(docbook_output_dir)/rpc_*.xml repo_ver="sip-router"\ "git-$(shell git rev-parse --verify --short=6 HEAD 2>/dev/null)" ifeq ($(repo_ver),git-) repo_ver="sip-router unknown" endif $(docbook_output_dir)/rpc_list.xml: Makefile \ $(foreach f,$(flist),$(docbook_output_dir)/$(call get_target,$f).xml) @echo '' >$@ @echo '' >>$@ @echo '>$@ @echo ' "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"' >>$@ @echo ' [ >$@ @echo " \"xmlns:xi CDATA #FIXED 'http://www.w3.org/2001/XInclude'\">]">>$@ @echo '>' >>$@ @echo '' >>$@ @echo ' RPC Exports List' >>$@ @echo ' ' >>$@ @echo ' '$(repo_ver)'' >>$@ @echo ' '`date -R`'' >>$@ @echo ' ' >>$@ @echo " Automatically generated by:">>$@ @echo " $(MAKE) -C doc/rpc_list $(MAKECMDGOALS)" >>$@ @echo ' ' >>$@ @echo ' ' >>$@ @$(foreach f,$(flist),\ echo ' ' \ >>$@ ; ) @echo '' >>$@ # finds all the files containing cfg defs .PHONY: find find: @$(find_rpc_files_cmd) # print the list of the autogenerated files .PHONY: print-lst print-lst: @$(find_rpc_files_cmd) | $(gen_file_grp_cmd) # .PHONY: gen-file-list .PHONY: gen-files_list .PHONY: gen_files_list gen-file-list gen-files-list gen_files_list: @$(foreach f,$(call subst_corepath,$(call sort_files,$(f_found_lst))),\ echo "$f \\";) print-modifaces: @$(foreach f,$(call sort_files,$(f_found_lst)),\ echo "$(call subst_corepath,$(f)):$(call find_modiface, $(f)) \\";) .PHONY: check-list .PHONY: update-list .PHONY: diff-list check-list update-list diff-list: @$(foreach f,$(call subst_corepath,$(call sort_files,$(diff_lst))),\ echo "$f \\";) # try to generate the docs from all the sources .PHONY: autogen autogen: @$(MAKE) all files_list="$(call sort_files,$(f_found_lst))" $(foreach f,$(flist),$(eval $(call mk_rules,$(f)))) kamailio-4.0.4/doc/rpc_list/rpc_domain_s.txt0000644000000000000000000000035512223032461017563 0ustar rootrootRPC Exports for domain_s ======================== [ this file is autogenerated, do not edit ] 1. domain.reload Reload domain table from database 2. domain.dump Return the contents of domain table kamailio-4.0.4/doc/rpc_list/rpc_tm.txt0000644000000000000000000000102712223032461016407 0ustar rootrootRPC Exports for tm ================== [ this file is autogenerated, do not edit ] 1. tm.cancel Documentation missing (rpc_cancel_doc). 2. tm.reply Documentation missing (rpc_reply_doc). 3. tm.stats Documentation missing (tm_rpc_stats_doc). 4. tm.hash_stats Documentation missing (tm_rpc_hash_stats_doc). 5. tm.t_uac_start Documentation missing (rpc_t_uac_start_doc). 6. tm.t_uac_wait Documentation missing (rpc_t_uac_wait_doc). Returns an array. kamailio-4.0.4/doc/rpc_list/rpc_dialog.txt0000644000000000000000000000137312223032457017237 0ustar rootrootRPC Exports for dialog ====================== [ this file is autogenerated, do not edit ] 1. dlg.list Print all dialogs 2. dlg.list_ctx Print all dialogs with associated context 3. dlg.dlg_list Print dialog based on callid and fromtag 4. dlg.dlg_list_ctx Print dialog with associated context based on callid and fromtag 5. dlg.end_dlg End a given dialog based on [h_entry] [h_id] 6. dlg.profile_get_size Returns the number of dialogs belonging to a profile 7. dlg.profile_list Lists all the dialogs belonging to a profile 8. dlg.bridge_dlg Bridge two SIP addresses in a call using INVITE(hold)-REFER-BYE mechanism: to, from, [outbound SIP proxy] kamailio-4.0.4/doc/parse_headers.txt0000644000000000000000000000264012223032457016120 0ustar rootroot# $Id$ # # parse headers api changes # # 2004.02.23 created (andrei) Starting with the ser version 0.10.99-dev1, the parse headers api has been slighlty changed. The reason for this change was performance improvement (now gcc can optimze much better all the switches involving header types) and dramatically reducing the memory impact of switching to 64 bits flags. The header flags (HDR_xxx) have been split into header types (hdr_types_t) and header flags (hdr_flags_t). Instead of the old HDR_xxx use now HDR_xxx_T when the header type is required and HDR_xxx_F or HDR_T2F(HDR_xxx_T) when the corresponding header flag is needed (e.g. in the parse_headers() call). The hdr_field structure and the parse_headers() function have also been changed. struct hdr_field's type member is now a hdr_types_t instead of an int and the flags parameter of parse_headers is now a hdr_flags_t. The change affects also the lump types for header operations. For example instead of del_lump( msg, via_offset, via_len, HDR_VIA) use del_lump( msg, via_offset, via_len, HDR_VIA_T). WARNING: don't use a header type (HDR_xxx_T) where a header flag (HDR_xxx_F) is expected or viceversa! Examples: An old call to parse_headers(msg, HDR_FOO, 0) will become parse_header(msg, HDR_FOO_F, 0). An old loop searching for a header field: while(hdr && (hdr->type!=HDR_FOO)) hdr=hdr->next; will become: while (hdr && (hdr->type!=HDR_FOO_T)) hdr=hdr->next; . kamailio-4.0.4/doc/management_api.txt0000644000000000000000000000331412223032457016257 0ustar rootrootManagement API's in SIP-router ------------------------------ SIP-router has an API based on RPC. This allows modules and core to expose commands that can be issued by external applications. Commands can change states, read status variables or expose internal structures. There are multiple ways to integrate your application to this interface. By adding modules, you can adapt the interface to one of several protocols supported: - XML-rpc - binrpc over various transports - Kamailio's MI - the management interface The MI interfaces can often run over Unix fifo sockets, UDP or TCP connections. The "sercmd" application is the command line tool used to connect to SIP-router over the command line. Use it to discover available commands. For backwards compatibilty, the kamctrl and kamdbctrl commands are still supported. Detailed information about these interfaces are to be found on the main sip-router.org web site in the wiki section. Modules ------- mi_rpc Exports Kamailio's MI interface over SIP-router's RPC interface xmlrpc Exports the RPC interface using XML-rpc ctl Implements the binrpc transport interface for SER rpcs Also implements the old SER fifo-based management interface. Kamailio modules (modules_k) ---------------------------- mi_datagram Kamailio MI interface- UDP version mi_fifo Kamailio MI interface- FIFO version mi_xmlrpc Kamailio MI interface- XML-rpc version The Kamailio MI interface is considered deprecated and will be supported only for backwards compatibility. New development is encouraged to use the RPC interface through one of the modules used to connect to it. The modules that currently only has MI interfaces will be changed to support the RPC interface in a coming release. kamailio-4.0.4/doc/timers.txt0000644000000000000000000002252212223032460014611 0ustar rootroot# $Id$ # # History: # -------- # 2005-11-30 created by andrei SIP-router :: timer interface ============================= 1. Introduction --------------- SIP-router's timer interface is based on a 3 level hierarchical timing wheel (see [1]). Most timeouts will go in the first "wheel" (all timeouts < 1<<14 ticks, which by default mean 1024 s). Each wheel acts as a circular buffer. The big advantage of this scheme is that most of the time you just run all the timer handlers from the current entry in the first wheel (no comparisons necessary). Each 2^14 ticks, all the timers in the second wheel's current entry are redistributed and each 2^23 ticks all the timers in the third wheel's current entry are redistributed. The timer interfaces allows adding timers dynamically, supports one shot and periodic timers, is precise and fast (very low overhead) and supports "fast" and "slow" timers. For now it uses setitimer to "generate" the ticks and from time to time it re-adjusts them based on the real system time. [1] - G. Varghese, T. Lauck, Hashed and Hierarchical Timing Wheels: Efficient Data Structures for Implementing a Timer Facility, 1996 2. include files ----------------- All the public functions are defined in timer.h. timer_ticks.h contains ticks conversion related macros (ticks to seconds, ms or viceversa). 3. Example usage ---------------- #include "../../timer.h" #include "../../timer_ticks.h" /* simple periodic timer handler */ static ticks_t timer_h(ticks_t ticks, struct timer_ln* tl, void* data) { DBG("timer habdler called at %d ticks, foo is %d \n", ticks, *(int*)data); return (ticks_t)(-1); /* periodical */ } struct timer_ln *t; int foo; t=timer_alloc(); if (t==0) goto error; timer_init(t, timer_handle, &foo, 0); foo=0; timer_add(t, MS_TO_TICKS(1500)); /* start it after 1500ms */ /* .... */ /* remove it and change the period to 2 s */ timer_del(t); timer_reinit(t); /* without init or reinit timer_add will refuse to re-add it*/ timer_add(t, S_TO_TICKS(2)); /* .... */ /* remove it at the end (optional) */ timer_del(t); timer_free(t); 4. Notes --------- 4.1 alloc & init ---------------- To use a timer you need a timer_ln structure. This structure must be stored in shared memory. You can either use timer_alloc() which will return a pointer to a shared memory allocated timer_ln structure or you can "attach" timer_ln as a member to one of your structures which is already stored in the shared memory. The timer_ln structure must be always initialized. Use the timer_init(...) macro for this. To the timer_init macro takes as parameters a pointer to the timer_ln structure, a pointer to a timer_handler_f function, a void* parameter for this function and some timer flags. Example: struct foo{ int bar; struct timer_ln timer; }; struct foo* f; f=shm_malloc(sizeof(struct foo)); time_init(&f->timer, timer_handle, &f->bar, 0); The timer flags can be either 0 (if it's a "slow" timer) or F_TIMER_FAST if this is a "fast" timer. A "fast" timer is a timer that does very little work in its timer handler (it always exits fast). You should use a "slow" timer if you don't care so much if your timer call is a little bit delayed or if you do dns lookups, query databases, blocking sends/writes. If you don't know which one to choose, choose "slow". 4.2 timer handlers ------------------ The timer handler can be periodic, one shot or it can change from call to call. It all depends on what value you return from it. If you always return (ticks_t)(-1) or timer_ln->initial_timeout you have a periodic timer. If you return 0 you have an one shot timer (the timer will be removed when your timer handler function exits). For any other value v, your timer will be automatically re-added with the next expire set to v (expressed in ticks). 4.3 timer_add ------------- The timer becomes active after you add it with timer_add. timer_add takes as parameters a pointer to the corresponding timer_ln structure and an expire interval (in ticks, use the macros from timer_ticks.h to convert from s/ms). The timer must be intialized (with timer_init()) before adding it the first time. timer_add returns 0 on success and -1 on error (timer already active or timer not intialized). If you want to re-add a deleted timer (timer_del was called on it) or an expired one shot timer (the timer handlers returned 0 on the last run), you have to re-init it first, either by calling timer_reinit(t) or by calling again timer_init(...). If you don't re-initialize the timer, timer_add will refuse to add it and it will return -1. So if timer_add returns error (-1) it means that either you're trying to re-add a running timer or a deleted/expired timer that was not re-initialized. WARNING: do not initialize/re-initialize a running timer! 4.4 timer_del ------------- To remove a timer from the active timer list call timer_del(struct timer_ln*). timer_del is the slowest of all the timer functions. If you are trying to delete a timer whose timer is executing. timer_del will wait until it finishes. timer_del returns 0 on success and a negative number on error (for now -1 if an attempt to delete and already removed or expired timer is made and -2 if timer_del is called from the timer handle it is supposed to delete). WARNING: - avoid deleting your own timer from its timer handle (if you try it, you'll get a BUG message in the log). If you _must_ have this, you have a broken design. If you still want it, look at timer_allow_del(). - if you have an one shot timer that frees its memory before exiting, make sure you don't call timer_del afterwards (e.g. use some reference counters and free the memory only if the counter is 0). Race example (using the struct foo defined above): /* simple one shot timer handler */ static ticks_t timer_h(ticks_t ticks, struct timer_ln* tl, void* data) { /* free the mem. */ shm_free(data); return 0; } struct foo* f; f=shm_malloc(sizeof(struct foo)); time_init(&f->timer, timer_handle, f, 0); timer_add(&f->timer, rand()); /* ... */ /* random amount of time spent doing other things */ timer_del(&f->timer); /* if the timer is already expired => f is already deleted => problems */ The above timer_del/free_in_one_shot_timer race example is very simple, but consider that you can have much more complex scenarios, when timer_del can be called from different processes on some asynchronous events. If this looks like your intended usage, make sure you use some reference counters or some other protection mechanism to avoid the above race. 4.5 timer_allow_del ------------------- Marks a timer as "to be deleted when the handler ends", usefull when the timer handler knows it won't prolong the timer anymore (it will return 0) and will do some time consuming work. Calling this function will cause simultaneous timer_dels to return immediately (they won't wait anymore for the timer handle to finish). It will also allow self-deleting from the timer handle without logging a bug. WARNING: - if you rely on timer_del to know when the timer handle execution finishes (e.g. to free resources used in the timer handle), don't use this function. - call this function only if this is your last timer handle run (the timer handle will return 0) - this function can be called only from a timer handle (in timer context), all other calls will have no effect and will log a bug message 4.6 Getting the ticks value ---------------------------- If you need the current ticks value you can get with get_ticks_raw(). WARNING: don't use get_ticks(). get_ticks() returns the number of ticks converted to seconds and it was kept only for compatibility reasons with the existing code. 4.7 Conversion --------------- To convert between ticks & time and viceversa, include timer_ticks.h and use one of the following macros: MS_TO_TICKS(ms) /* converts from milliseconds to ticks, rounds up */ S_TO_TICKS(s) /* convert from seconds to ticks */ TICKS_TO_MS(t) /* converts from ticks to milliseconds, can overflow for very large values (use long long and TICKS_TO_MS((long long)t) to try to avoid it if you know that you'll deal with such large values */ TICKS_TO_S(t) /* converts from ticks to s, rounded down */ 4.8 Ticks value comparison --------------------------- The ticks value can (and will) overflow so ticks values should never be compared directly (e.g. ticks1 t2 */ TICKS_LE(t1, t2) /* t1 <= t2 */ TICKS_GE(t1, t2) /* t1 >= t2 */ These macros work as long as the difference between t1 and t2 is less then 2^(sizeof(ticks_t)*8-1). For the default TIMER_TICKS_HZ values, this means 4.25 years. 4.9 Backward compatibility -------------------------- The old register_timer and get_ticks() are still supported for backward compatibility. This means that you don't have to change your existing working code. 5.0 Debugging ------------- The timers have built-in debugging information. To activate it you only need to define TIMER_DEBUG (recompile ser with make CC_EXTRA_OPTS=-DTIMER_DEBUG all). The timer debug log level is by default L_WARN. [ FIXME: add it as script option] TIMER_DEBUG enables extra sanity checks and it will log a lot of information (like the caller of timer_* functions, where a timer was added a.s.o). [Todo]: - SLOW, DRIFT, RESYNC, FREQUENCY kamailio-4.0.4/doc/cvs-commit-rules.txt0000644000000000000000000000246512223032457016531 0ustar rootroot# $Id$ # # SIP-router git commit rules 1. Changing other people's code: -------------------------------- - send a patch to the code/module mantainer and/or sr-dev (don't commit changes to code you don't own if you don't have the mantainer's approval) Exceptions: a. compilation (this includes warning) fixes b. bug fixes c. api changes (some external functions definitions change) d. small changes due to a new release in the very near future (allowed only for the release manager) 2. Code requirements -------------------- 2.1 Unstable branch: - the code must compile (at least on one architecture). If the code does not compile, but you still want to commit it, comment it out (#if 0 ... #endif) - the code should compile without warnings (with -Wall) (exceptions: very difficult to avoid warnings) - follow SIP-router coding style 2.2. Stable branch (everything for unstable branch +) ------------------------------------------------------ - the code should compile on all the architectures (this currently includes linux, freebsd, netbsd, openbsd, solaris >= 8; x86, ultrasparc, strongarm; gcc 4.x, icc, sun cc >=5.3). It should also compile on gcc 2.95 and 3.x. - the code must be tested or the change trivial enough - the code should compile without warnings on all the arhitectures (with some exceptions) kamailio-4.0.4/doc/counter_list/0000755000000000000000000000000012223032461015255 5ustar rootrootkamailio-4.0.4/doc/counter_list/counters_dns.txt0000644000000000000000000000030612223032457020530 0ustar rootrootCounters for dns =============================== [ this file is autogenerated, do not edit ] 1. dns.failed_dns_request incremented each time a DNS request has failed. kamailio-4.0.4/doc/counter_list/counters_sctp.txt0000644000000000000000000000224712223032457020723 0ustar rootrootCounters for sctp ================================ [ this file is autogenerated, do not edit ] 1. sctp.established incremented each time a new association is established. 2. sctp.connect_failed incremented each time a new outgoing connection fails. 3. sctp.local_reject number of rejected incoming connections. 4. sctp.remote_shutdown incremented each time an association is closed by the peer. 5. sctp.assoc_shutdown incremented each time an association is shutdown. 6. sctp.comm_lost incremented each time an established connection is close due tosome error. 7. sctp.sendq_full number of failed send attempt due to exceeded buffering capacity (full kernel buffers). 8. sctp.send_failed number of failed send attempt for any reason except full buffers. 9. sctp.send_force_retry incremented each time a failed send is force-retried(possible only if sctp_send_retries ! = 0. 10. sctp.current_opened_connections number of currently opened associations. 11. sctp.current_tracked_connections number of currently tracked associations. kamailio-4.0.4/doc/counter_list/docbook/0000755000000000000000000000000012223032457016702 5ustar rootrootkamailio-4.0.4/doc/counter_list/docbook/counters_sctp.xml0000644000000000000000000000476612223032457022334 0ustar rootroot Counters for sctp
sctp.established incremented each time a new association is established.
sctp.connect_failed incremented each time a new outgoing connection fails.
sctp.local_reject number of rejected incoming connections.
sctp.remote_shutdown incremented each time an association is closed by the peer.
sctp.assoc_shutdown incremented each time an association is shutdown.
sctp.comm_lost incremented each time an established connection is close due tosome error.
sctp.sendq_full number of failed send attempt due to exceeded buffering capacity (full kernel buffers).
sctp.send_failed number of failed send attempt for any reason except full buffers.
sctp.send_force_retry incremented each time a failed send is force-retried(possible only if sctp_send_retries ! = 0.
sctp.current_opened_connections number of currently opened associations.
sctp.current_tracked_connections number of currently tracked associations.
kamailio-4.0.4/doc/counter_list/docbook/counters_tcp.xml0000644000000000000000000000476612223032457022151 0ustar rootroot Counters for tcp
tcp.established incremented each time a tcp connection is established.
tcp.passive_open total number of accepted connections (so far).
tcp.connect_success total number of successfully active opened connections (successful connect()s).
tcp.connect_failed number of failed active connection attempts.
tcp.local_reject number of rejected incoming connections.
tcp.con_timeout total number of connections that did timeout (idle for too long).
tcp.con_reset total number of TCP_RSTs received on established connections.
tcp.send_timeout number of send attempts that failed due to a timeout(note: works only in tcp async mode).
tcp.sendq_full number of send attempts that failed because of exceeded bufferingcapacity (send queue full, works only in tcp async mode).
tcp.current_opened_connections number of currently opened connections.
tcp.current_write_queue_size current sum of all the connections write queue sizes.
kamailio-4.0.4/doc/counter_list/docbook/counter_list.xml0000644000000000000000000000147512223032457022145 0ustar rootroot ] > Counter List sip-router git-bf9d26 Wed, 01 Sep 2010 19:01:58 +0200 Automatically generated by: make -C doc/counter_list all kamailio-4.0.4/doc/counter_list/docbook/counters_dns.xml0000644000000000000000000000073712223032457022141 0ustar rootroot Counters for dns
dns.failed_dns_request incremented each time a DNS request has failed.
kamailio-4.0.4/doc/counter_list/docbook/counters_mysql.xml0000644000000000000000000000101412223032457022507 0ustar rootroot Counters for mysql
mysql.driver_errors incremented each time a Mysql error happened because the server/connection has failed.
kamailio-4.0.4/doc/counter_list/docbook/Makefile0000644000000000000000000000026312223032457020343 0ustar rootroot docs = counter_list.xml docbook_dir = ../../../docbook # no subsection listed in the main TOC xsltproc_flags+=--stringparam toc.section.depth 0 include $(docbook_dir)/Makefile kamailio-4.0.4/doc/counter_list/counters_mysql.txt0000644000000000000000000000036612223032457021117 0ustar rootrootCounters for mysql ================================= [ this file is autogenerated, do not edit ] 1. mysql.driver_errors incremented each time a Mysql error happened because the server/connection has failed. kamailio-4.0.4/doc/counter_list/Makefile0000644000000000000000000002512112223032461016716 0ustar rootroot COREPATH=../.. #include $(COREPATH)/Makefile.defs CFG2TXT=../scripts/cdefs2doc/dump_counters.pl CFG2DOCBOOK=../scripts/cdefs2doc/dump_counters.pl # output directory for generated txt files txt_output_dir=. # output directory for generated docbook xml files docbook_output_dir=docbook # list of files containing counter defs in the following format: # : # can be easily updated by adding the output of: # make diff-list (which obeys grp_exclude and file_exclude) # or completely regenerated by replacing files_list with the output of: # make gen-files-list files_list= \ $(COREPATH)/resolve.c:resolve \ $(COREPATH)/tcp_stats.c:tcp \ $(COREPATH)/sctp_stats.c:sctp \ $(COREPATH)/modules/db_mysql/mysql_mod.c:db_mysql # list of excluded groups grp_exclude= # list of file prefixes to exclude (full path needed) file_exclude=$(COREPATH)/modules_s/tls/ \ $(COREPATH)/obsolete # special per file group overrides # format= grp_filename=... ,where filename does not contain the extension # e.g.: # grp_f_tcp_stats=tcp # grp_f_sctp_stats=sctp # special per group group name overrides # e.g.: # grp_g_maxfwd=mf grp_g_resolve=dns grp_g_db_mysql=mysql # override auto-detected group if set to 1 (else the group is used inside the # file only if it cannot be autodetected) ifeq ($(group_override),1) override force_grp=force- else override force_grp= endif # command used for gcc (contains extra includes) gcc=gcc #-I$(COREPATH)/lib -I$(COREPATH) -I/usr/include/libxml2 # defines used by gcc c_defs=-D__CPU_i386 -D__OS_linux -DSER_VER=2099099 -DPKG_MALLOC -DSHM_MEM \ -DSHM_MMAP -DDNS_IP_HACK -DUSE_IPV6 -DUSE_MCAST -DUSE_TCP \ -DUSE_DNS_CACHE -DUSE_DNS_FAILOVER -DUSE_DST_BLACKLIST -DUSE_NAPTR \ -DUSE_TLS -DTLS_HOOKS -DFAST_LOCK -DCC_GCC_LIKE_ASM \ -DHAVE_GETHOSTBYNAME2 -DHAVE_UNION_SEMUN -DHAVE_SCHED_YIELD \ -DHAVE_MSG_NOSIGNAL -DHAVE_MSGHDR_MSG_CONTROL -DHAVE_ALLOCA_H \ -DHAVE_SCHED_SETSCHEDULER -DHAVE_EPOLL -DUSE_SCTP -DNAME='\"ser\"' \ -DVERSION='\"2.99.99-pre3\"' -DARCH='\"i386\"' -DOS_QUOTED='\"linux\"' # common makefile vars used in defs LOCALBASE=/usr/local SYSBASE=/usr filter_files=$(filter-out $(addsuffix %,$(file_exclude)),\ $(filter-out $(addprefix %:,$(grp_exclude)),$(1))) #filtered files list flist=$(call filter_files,$(files_list)) # throws an error if input is not in the format filename:grp check_fname_grp=$(if $(filter-out 2,$(words $(subst :, ,$(1)))),\ $(error bad format "$(1)", it should be filename:grp)) # get prereq from file:grp (get_prereq(file:grp) => file) get_prereq=$(firstword $(subst :, ,$(1))) # get grp from file:grp (get_grp(file:grp) => grp) get_listed_grp=$(word 2, $(subst :, ,$(1))) # get base file name from file:grp: get_bname(file:grp) # => basename(file) without extension (e.g. get_bname(foo/bar.c:x) => bar) # get_bname=$(basename $(notdir $(call get_prereq,$(1)))) #get grp from file:grp, using the overrides get_grp=$(strip $(if $(grp_f_$(call get_bname,$(1))), \ $(grp_f_$(call get_bname,$(1))),\ $(if $(grp_g_$(call get_listed_grp,$(1))),\ $(grp_g_$(call get_listed_grp,$(1))),\ $(call get_listed_grp,$(1))) ) ) # get target from file:grp (get_target(file:grp) => counter_grp.txt) get_target=counters_$(call get_grp,$(1)) # $(LF) definition (do not remove) define LF endef # get all the lines containing DEFS or INCLUDES definitions from the Makefile. # WARNING: does not work with all sed implementation (tested with GNU sed). # It uses a hack to restore the LFs (LFs are removed by $(shell)): LFs are # replaced with '^LF^' and then ^LF^ is subst'ed back to a real LF. get_make_idefs=$(subst ^LF^,$(LF),$(shell sed \ -ne '/^[\t ]*\(DEFS\|INCLUDES\)[\t ]*[+:]\?=.*[^\]$$/H'\ -ne '/^[\t ]*\(DEFS\|INCLUDES\)[\t ]*[+:]\?=.*\\$$/,/\(^$$\)\|\([^\]$$\)/H'\ -ne '$${g;s/\n/^LF^/g;p}'\ < $(1)/Makefile )) # get all the lines from the makefile containing variable definitions. # It will also return conditionals and try to filter out possible rules. # WARNING: does not work with all sed implementation (tested with GNU sed). # It uses a hack to restore the LFs (LFs are removed by $(shell)): LFs are # replaced with '^LF^' and then ^LF^ is subst'ed back to a real LF. get_make_vars=$(subst ^LF^,$(LF),$(shell sed -n \ -e ': start' \ -e '/^\(ifeq\|ifneq\|else\|endif\)[\t ]*\($$\|.*[^\]$$\)/{H;b end}' \ -e '/^\(ifeq\|ifneq\|else\|endif\)[\t ]\+.*[\]$$/,/[^\]$$/{H;b end}' \ -e '/^[a-zA-Z._/$$][a-zA-Z0-9._()/$$ \t-]*:\([^=]\|$$\)/b eat_rule' \ -e '/^[\t ]*[A-Za-z._][A-Za-z0-9._-]*[\t ]*[+:]\?=.*[^\]$$/{H;b end}' \ -e '/^[\t ]*[A-Za-z._][A-Za-z0-9._-]*[\t ]*[+:]\?=.*\\$$/,/\(^$$\)\|\([^\]$$\)/{H;b end}' \ -e ': end' \ -e '$${g;s/\n/^LF^/g;p}'\ -e 'b' \ -e ': eat_rule' \ -e '$$b end' \ -e 'n' \ -e '/^[a-zA-Z._/$$][a-zA-Z0-9._()/$$ \t-]*:\([^=]\|$$\)/b eat_rule' \ -e '/^[\t]/b eat_rule' \ -e 'b start' \ < $(1)/Makefile )) define mk_rules $(call check_fname_grp, $(1)) #$$(info generating counter_$$(call get_grp,$(1)).txt: $$(call get_prereq,$(1))) DEFS:= INCLUDES:= # extract all the includes and defs from the module makefile and # evaluate them $$(eval $$(call get_make_vars,$$(dir $$(call get_prereq,$(1))))) # override COREPATH (we know better) COREPATH=../.. # save the result in a per group e_idefs_ var $$(eval e_idefs_$$(call get_grp,$(1)):=$$(DEFS) $$(INCLUDES)) # debugging: #$$(info eval: $$(call get_make_vars,$$(dir $$(call get_prereq,$(1))))) #$$(info e_idefs_$$(call get_grp,$(1))=$$(e_idefs_$$(call get_grp,$(1)))) $(txt_output_dir)/$$(call get_target,$(1)).txt: \ $$(call get_prereq,$(1)) Makefile $(CFG2TXT) $(CFG2TXT) --file $$< --$(force_grp)grp=$$(call get_grp,$(1)) \ --gcc="$(gcc)" --txt \ --defs="$(c_defs) $$(e_idefs_$$(call get_grp,$(1)))" \ > "$$@" || (rm -f "$$@"; exit 1) $(docbook_output_dir)/$$(call get_target,$(1)).xml: \ $$(call get_prereq,$(1)) Makefile $(CFG2TXT) $(CFG2DOCBOOK) --file $$< --$(force_grp)grp=$$(call get_grp,$(1)) \ --gcc="$(gcc)" --docbook \ --defs="$(c_defs) $$(e_idefs_$$(call get_grp,$(1)))" \ > "$$@" || (rm -f "$$@"; exit 1) clean_$$(call get_target,$(1)).txt: rm -f "$(txt_output_dir)/$$(call get_target,$(1)).txt" clean_$$(call get_target,$(1)).xml: rm -f "$(docbook_output_dir)/$$(call get_target,$(1)).xml" txt: $(txt_output_dir)/$$(call get_target,$(1)).txt docbook: $(docbook_output_dir)/$$(call get_target,$(1)).xml clean_txt: clean_$$(call get_target,$(1)).txt clean_docbook: clean_$$(call get_target,$(1)).xml endef find_counter_files_cmd= find $(COREPATH) -type f -name "*.c" \ -exec grep "counter_def_t[ ][ ]*[a-zA-Z0-9_][a-zA-Z0-9_]*\[\][ ]*=" /dev/null {} \; \ | cut -d: -f1 # shell command to generate a file:grp list from a list of files # grp will be the modulename if the file is in a module directory or # the file name with the extension and _cnt, cnt_, _stats, stats_, # _cnts, or cnts_ stripped out of # it. # output: list of " "filename":"grpname gen_file_grp_cmd=\ sed -e "s!\(.*/modules[^/]*/\([^/][^/]*\)/.*\)! \1:\2!" \ -e "s!^\([^ ].*/\([^/.]*\)[^/]*$$\)!\1:\2!" \ -e "s!^\([^ :]*\):\(.*\)_cnts\?[_]*!\1:\2!" \ -e "s!^\([^ :]*\):\(.*\)cnts\?[_]*!\1:\2!" \ -e "s!^\([^ :]*\):\(.*\)_stats[_]*!\1:\2!" \ -e "s!^\([^ :]*\):\(.*\)stats[_]*!\1:\2!" # special vars for generating the list of files or updates found_lst=$(shell $(find_counter_files_cmd) | $(gen_file_grp_cmd)) # filtered found lst f_found_lst=$(call filter_files,$(found_lst)) diff_lst=$(filter-out $(flist),$(f_found_lst)) get_core_files=$(filter-out $(COREPATH)/modules% $(COREPATH)/lib%,$(1)) sort_files=$(sort $(call get_core_files,$(1)))\ $(sort $(filter-out $(call get_core_files,$(1)),$(1))) # replace $(COREPATH) with the text "$(COREPATH)" subst_corepath=$(patsubst $(patsubst %/,%,$(COREPATH))/%,\$$(COREPATH)/%,$(1)) # help will be the default rule (on-purpose since without having a patched # GCC:TranslationUnit module, make all won't work) .PHONY: help help: @echo "To regenerate $(foreach f,$(flist),$(call get_target,$f).{txt,xml})" @echo "type: $(MAKE) all ." @echo "or to regenerate all the counter lists by searching all" @echo " the source files for definitions, type: $(MAKE) autogen ." @echo "NOTE: you need the GCC:TranslationUnit perl module with an " @echo "extra patch applied (see $(CFG2TXT) --patch)." .PHONY: txt txt: .PHONY: docbook docbook: .PHONY: clean_txt clean_txt: .PHONY: clean_docbook clean_docbook: .PHONY: all all: txt $(docbook_output_dir)/counter_list.xml .PHONY: clean clean: clean_txt clean_docbook @rm -f $(docbook_output_dir)/counter_list.xml .PHONY: proper proper: @rm -f $(txt_output_dir)/counters_*.txt @rm -f $(docbook_output_dir)/counters_*.xml repo_ver="sip-router"\ "git-$(shell git rev-parse --verify --short=6 HEAD 2>/dev/null)" ifeq ($(repo_ver),git-) repo_ver="sip-router unknown" endif $(docbook_output_dir)/counter_list.xml: Makefile \ $(foreach f,$(flist),$(docbook_output_dir)/$(call get_target,$f).xml) @echo '' >$@ @echo '' >>$@ @echo '>$@ @echo ' "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"' >>$@ @echo ' [ >$@ @echo " \"xmlns:xi CDATA #FIXED 'http://www.w3.org/2001/XInclude'\">]">>$@ @echo '>' >>$@ @echo '' >>$@ @echo ' Counter List' >>$@ @echo ' ' >>$@ @echo ' '$(repo_ver)'' >>$@ @echo ' '`date -R`'' >>$@ @echo ' ' >>$@ @echo " Automatically generated by:">>$@ @echo " $(MAKE) -C doc/counter_list $(MAKECMDGOALS)" >>$@ @echo ' ' >>$@ @echo ' ' >>$@ @$(foreach f,$(flist),\ echo ' ' \ >>$@ ; ) @echo '' >>$@ # finds all the files containing cfg defs .PHONY: find find: @$(find_counter_files_cmd) # print the list of the autogenerated files .PHONY: print-lst print-lst: @$(find_counter_files_cmd) | $(gen_file_grp_cmd) # .PHONY: gen-file-list .PHONY: gen-files_list .PHONY: gen_files_list gen-file-list gen-files-list gen_files_list: @$(foreach f,$(call subst_corepath,$(call sort_files,$(f_found_lst))),\ echo "$f \\";) .PHONY: check-list .PHONY: update-list .PHONY: diff-list check-list update-list diff-list: @$(foreach f,$(call subst_corepath,$(call sort_files,$(diff_lst))),\ echo "$f \\";) # try to generate the docs from all the sources .PHONY: autogen autogen: @$(MAKE) all files_list="$(call sort_files,$(f_found_lst))" $(foreach f,$(flist),$(eval $(call mk_rules,$(f)))) kamailio-4.0.4/doc/counter_list/counters_tcp.txt0000644000000000000000000000227312223032457020537 0ustar rootrootCounters for tcp =============================== [ this file is autogenerated, do not edit ] 1. tcp.established incremented each time a tcp connection is established. 2. tcp.passive_open total number of accepted connections (so far). 3. tcp.connect_success total number of successfully active opened connections (successful connect()s). 4. tcp.connect_failed number of failed active connection attempts. 5. tcp.local_reject number of rejected incoming connections. 6. tcp.con_timeout total number of connections that did timeout (idle for too long). 7. tcp.con_reset total number of TCP_RSTs received on established connections. 8. tcp.send_timeout number of send attempts that failed due to a timeout(note: works only in tcp async mode). 9. tcp.sendq_full number of send attempts that failed because of exceeded bufferingcapacity (send queue full, works only in tcp async mode). 10. tcp.current_opened_connections number of currently opened connections. 11. tcp.current_write_queue_size current sum of all the connections write queue sizes. kamailio-4.0.4/doc/cfg_list/0000755000000000000000000000000012223032461014335 5ustar rootrootkamailio-4.0.4/doc/cfg_list/cfg_tcp.txt0000644000000000000000000000670512223032461016513 0ustar rootrootConfiguration Variables for tcp =============================== [ this file is autogenerated, do not edit ] 1. tcp.connect_timeout used only in non-async mode, in seconds. Range: -1 - 134217727. Type: integer. 2. tcp.send_timeout in seconds. Range: -1 - 2147483647. Type: integer. 3. tcp.connection_lifetime connection lifetime (in seconds). Range: -1 - 2147483647. Type: integer. 4. tcp.max_connections maximum connection number, soft limit. Range: 0 - 2147483647. Type: integer. 5. tcp.no_connect if set only accept new connections, never actively open new ones. Range: 0 - 1. Type: integer. 6. tcp.fd_cache file descriptor cache for tcp_send. Range: 0 - 1. Type: integer. Read-only. 7. tcp.async async mode for writes and connects. Range: 0 - 1. Type: integer. Read-only. 8. tcp.connect_wait parallel simultaneous connects to the same dst. (0) or one connect. Range: 0 - 1. Type: integer. Read-only. 9. tcp.conn_wq_max maximum bytes queued for write per connection (depends on async). Range: 0 - 1048576. Type: integer. 10. tcp.wq_max maximum bytes queued for write allowed globally (depends on async). Range: 0 - 1073741824. Type: integer. 11. tcp.defer_accept 0/1 on linux, seconds on freebsd (see docs). Range: 0 - 3600. Type: integer. Read-only. 12. tcp.delayed_ack initial ack will be delayed and sent with the first data segment. Range: 0 - 1. Type: integer. 13. tcp.syncnt number of syn retransmissions before aborting a connect (0=not set). Range: 0 - 1024. Type: integer. 14. tcp.linger2 lifetime of orphaned sockets in FIN_WAIT2 state in s (0=not set). Range: 0 - 3600. Type: integer. 15. tcp.keepalive enables/disables keepalives for tcp. Range: 0 - 1. Type: integer. 16. tcp.keepidle time before sending a keepalive if the connection is idle (linux). Range: 0 - 86400. Type: integer. 17. tcp.keepintvl time interval between keepalive probes on failure (linux). Range: 0 - 86400. Type: integer. 18. tcp.keepcnt number of failed keepalives before dropping the connection (linux). Range: 0 - 1024. Type: integer. 19. tcp.crlf_ping enable responding to CRLF SIP-level keepalives . Range: 0 - 1. Type: integer. 20. tcp.accept_aliases turn on/off tcp aliases (see tcp_accept_aliases) . Range: 0 - 1. Type: integer. 21. tcp.alias_flags flags used for adding new aliases (FORCE_ADD:1 , REPLACE:2) . Range: 0 - 2. Type: integer. 22. tcp.new_conn_alias_flags flags for the def. aliases for a new conn. (FORCE_ADD:1, REPLACE:2 . Range: 0 - 2. Type: integer. 23. tcp.accept_no_cl accept TCP messges without Content-Lenght . Range: 0 - 1. Type: integer. 24. tcp.rd_buf_size internal read buffer size (should be > max. expected datagram). Range: 512 - 65536. Type: integer. 25. tcp.wq_blk_size internal async write block size (debugging use only for now). Range: 1 - 65535. Type: integer. kamailio-4.0.4/doc/cfg_list/cfg_tm.txt0000644000000000000000000001501612223032461016340 0ustar rootrootConfiguration Variables for tm ============================== [ this file is autogenerated, do not edit ] 1. tm.ruri_matching perform Request URI check in transaction matching. Default: 1. Range: 0 - 1. Type: integer. 2. tm.via1_matching perform first Via header check in transaction matching. Default: 1. Range: 0 - 1. Type: integer. 3. tm.fr_timer timer which hits if no final reply for a request or ACK for a negative INVITE reply arrives (in milliseconds). Default: 30000. Type: integer. 4. tm.fr_inv_timer timer which hits if no final reply for an INVITE arrives after a provisional message was received (in milliseconds). Default: 120000. Type: integer. 5. tm.fr_inv_timer_next The value [ms] of fr_inv_timer for subsequent branches during serial forking.. Default: 30000. Type: integer. 6. tm.wt_timer time for which a transaction stays in memory to absorb delayed messages after it completed. Default: 5000. Type: integer. 7. tm.delete_timer time after which a to-be-deleted transaction currently ref-ed by a process will be tried to be deleted again.. Default: 200. Type: integer. 8. tm.retr_timer1 initial retransmission period (in milliseconds). Default: 500. Type: integer. 9. tm.retr_timer2 maximum retransmission period (in milliseconds). Default: 4000. Type: integer. 10. tm.max_inv_lifetime maximum time an invite transaction can live from the moment of creation. Default: 180000. Type: integer. 11. tm.max_noninv_lifetime maximum time a non-invite transaction can live from the moment of creation. Default: 32000. Type: integer. 12. tm.noisy_ctimer if set, INVITE transactions that time-out (FR INV timer) will be always replied. Default: 1. Range: 0 - 1. Type: integer. 13. tm.auto_inv_100 automatically send 100 to an INVITE. Default: 1. Range: 0 - 1. Type: integer. 14. tm.auto_inv_100_reason reason text of the automatically send 100 to an INVITE. Default: trying -- your call is important to us. Type: string. 15. tm.unix_tx_timeout Unix socket transmission timeout, in milliseconds. Default: 500. Type: integer. 16. tm.restart_fr_on_each_reply restart final response timer on each provisional reply. Default: 1. Range: 0 - 1. Type: integer. 17. tm.pass_provisional_replies enable/disable passing of provisional replies to TMCB_LOCAL_RESPONSE_OUT callbacks. Default: 0. Range: 0 - 1. Type: integer. 18. tm.aggregate_challenges if the final response is a 401 or a 407, aggregate all the authorization headers (challenges) (rfc3261 requires this to be on). Default: 1. Range: 0 - 1. Type: integer. 19. tm.unmatched_cancel determines how CANCELs with no matching transaction are handled (0: statefull forwarding, 1: stateless forwarding, 2: drop). Default: 0. Range: 0 - 2. Type: integer. 20. tm.default_code default SIP response code sent by t_reply(), if the function cannot retrieve its parameters. Default: 500. Range: 400 - 699. Type: integer. 21. tm.default_reason default SIP reason phrase sent by t_reply(), if the function cannot retrieve its parameters. Default: Server Internal Error. Type: string. 22. tm.reparse_invite if set to 1, the CANCEL and negative ACK requests are constructed from the INVITE message which was sent out instead of building them from the received request. Default: 1. Range: 0 - 1. Type: integer. 23. tm.ac_extra_hdrs header fields prefixed by this parameter value are included in the CANCEL and negative ACK messages if they were present in the outgoing INVITE (depends on reparse_invite). Default: . Type: string. 24. tm.blst_503 if set to 1, blacklist 503 SIP response sources. Default: 0. Range: 0 - 1. Type: integer. 25. tm.blst_503_def_timeout default 503 blacklist time (in s), when no Retry-After header is present. Default: 0. Type: integer. 26. tm.blst_503_min_timeout minimum 503 blacklist time (in s). Default: 0. Type: integer. 27. tm.blst_503_max_timeout maximum 503 blacklist time (in s). Default: 3600. Type: integer. 28. tm.blst_methods_add bitmap of method types that trigger blacklisting on transaction timeouts. Default: 1. Type: integer. 29. tm.blst_methods_lookup Bitmap of method types that are looked-up in the blacklist before statefull forwarding. Default: -9. Type: integer. 30. tm.cancel_b_method How to cancel branches on which no replies were received: 0 - fake reply, 1 - retransmitting the request, 2 - send cancel. Default: 1. Range: 0 - 2. Type: integer. 31. tm.reparse_on_dns_failover if set to 1, the SIP message after a DNS failover is constructed from the outgoing message buffer of the failed branch instead of from the received request. Default: 1. Range: 0 - 1. Type: integer. 32. tm.disable_6xx_block if set to 1, 6xx is treated like a normal reply (breaks rfc). Default: 0. Range: 0 - 1. Type: integer. 33. tm.local_ack_mode if set to 1 or 2, local 200 ACKs are sent to the same address as the corresponding INVITE (1) or the source of the 200 reply (2) instead of using the contact and the route set (it breaks the rfc, if it is not set to 0 but allows dealing with NATed contacts in some simple cases). Default: 0. Range: 0 - 2. Type: integer. 34. tm.local_cancel_reason if set to 1, a Reason header is added to locally generated CANCELs (see RFC3326). Default: 1. Range: 0 - 1. Type: integer. 35. tm.e2e_cancel_reason if set to 1, Reason headers from received CANCELs are copied into the corresponding generated hop-by-hop CANCELs. Default: 1. Range: 0 - 1. Type: integer. kamailio-4.0.4/doc/cfg_list/cfg_sctp.txt0000644000000000000000000000525612223032457016703 0ustar rootrootConfiguration Variables for sctp ================================ [ this file is autogenerated, do not edit ] 1. sctp.socket_rcvbuf socket receive buffer size (read-only). Range: 512 - 102400. Type: integer. Read-only. 2. sctp.socket_sndbuf socket send buffer size (read-only). Range: 512 - 102400. Type: integer. Read-only. 3. sctp.autoclose seconds before closing and idle connection (must be non-zero). Range: 1 - 1073741824. Type: integer. 4. sctp.send_ttl milliseconds before aborting a send. Range: 0 - 1073741824. Type: integer. 5. sctp.send_retries re-send attempts on failure. Range: 0 - 9. Type: integer. 6. sctp.assoc_tracking connection/association tracking (see also assoc_reuse). Range: 0 - 1. Type: integer. 7. sctp.assoc_reuse connection/association reuse (for now used only for replies), depends on assoc_tracking being set. Range: 0 - 1. Type: integer. 8. sctp.max_assocs maximum allowed open associations (-1 = disable, as many as allowed by the OS). Type: integer. 9. sctp.srto_initial initial value of the retr. timeout, used in RTO calculations, in msecs. Range: 0 - 1073741824. Type: integer. 10. sctp.srto_max maximum value of the retransmission timeout (RTO), in msecs. Range: 0 - 1073741824. Type: integer. 11. sctp.srto_min minimum value of the retransmission timeout (RTO), in msecs. Range: 0 - 1073741824. Type: integer. 12. sctp.asocmaxrxt maximum retransmission attempts per association. Range: 0 - 1024. Type: integer. 13. sctp.init_max_attempts max INIT retransmission attempts. Range: 0 - 1024. Type: integer. 14. sctp.init_max_timeo max INIT retransmission timeout (RTO max for INIT), in msecs. Range: 0 - 1073741824. Type: integer. 15. sctp.hbinterval heartbeat interval in msecs. Range: 0 - 1073741824. Type: integer. 16. sctp.pathmaxrxt maximum retransmission attempts per path. Range: 0 - 1024. Type: integer. 17. sctp.sack_delay time since the last received packet before sending a SACK, in msecs. Range: 0 - 1073741824. Type: integer. 18. sctp.sack_freq number of received packets that trigger the sending of a SACK. Range: 0 - 1024. Type: integer. 19. sctp.max_burst maximum burst of packets that can be emitted by an association. Range: 0 - 1024. Type: integer. kamailio-4.0.4/doc/cfg_list/cfg_carrierroute.txt0000644000000000000000000000143312223032457020431 0ustar rootrootConfiguration Variables for carrierroute ======================================== [ this file is autogenerated, do not edit ] 1. carrierroute.use_domain When using tree lookup per user, this parameter specifies whether to use the domain part for user matching or not.. Default: 0. Range: 0 - 1. Type: integer. 2. carrierroute.fallback_default If the user has a non-existing tree set and fallback_default is set to 1, the default tree is used. Else error is returned. Default: 1. Range: 0 - 1. Type: integer. 3. carrierroute.fetch_rows The number of the rows to be fetched at once from database when loading the routing data.. Default: 2000. Type: integer. kamailio-4.0.4/doc/cfg_list/cfg_malloc_test.txt0000644000000000000000000000136412223032457020234 0ustar rootrootConfiguration Variables for malloc_test ======================================= [ this file is autogenerated, do not edit ] 1. malloc_test.check_content check if allocated memory was overwritten by filling it with a special pattern and checking it on free.. Default: 0. Range: 0 - 1. Type: integer. 2. malloc_test.realloc_p realloc probability in percents. During tests and mem_rnd_alloc realloc_p percents of the allocations will be made by realloc'ing and existing chunk. The maximum value is limited to 90, to avoid very long mem_rnd_alloc runs (a realloc might also free memory).. Default: 0. Range: 0 - 90. Type: integer. kamailio-4.0.4/doc/cfg_list/cfg_dispatcher.txt0000644000000000000000000000073512223032457020055 0ustar rootrootConfiguration Variables for dispatcher ====================================== [ this file is autogenerated, do not edit ] 1. dispatcher.probing_threshhold Number of failed requests, before a destination is set to probing.. Default: 3. Type: integer. 2. dispatcher.ping_reply_codes Additional, valid reply codes for the OPTIONS Pinger. Default is "". Default: . Type: string. kamailio-4.0.4/doc/cfg_list/cfg_registrar.txt0000644000000000000000000000344512223032461017725 0ustar rootrootConfiguration Variables for registrar ===================================== [ this file is autogenerated, do not edit ] 1. registrar.default_expires Contains number of second to expire if no expire hf or contact expire present. Default: 3600. Type: integer. 2. registrar.default_expires_range Percent from default_expires that will be used in generating the range for the expire interval. Default: 0. Range: 0 - 100. Type: integer. 3. registrar.min_expires The minimum expires value of a Contact. Value 0 disables the checking. . Default: 60. Type: integer. 4. registrar.max_expires The maximum expires value of a Contact. Value 0 disables the checking. . Default: 0. Type: integer. 5. registrar.max_contacts The maximum number of Contacts for an AOR. Value 0 disables the checking. . Default: 0. Type: integer. 6. registrar.retry_after If you want to add the Retry-After header field in 5xx replies, set this parameter to a value grater than zero. Default: 0. Type: integer. 7. registrar.case_sensitive If set to 1 then AOR comparison will be case sensitive. Recommended and default is 0, case insensitive. Default: 0. Type: integer. 8. registrar.default_q The parameter represents default q value for new contacts.. Default: -1. Range: -1 - 1000. Type: integer. 9. registrar.append_branches If set to 1(default), lookup will put all contacts found in msg structure. Default: 1. Type: integer. 10. registrar.realm_pref Realm prefix to be removed. Default is "". Type: string. kamailio-4.0.4/doc/cfg_list/cfg_core.txt0000644000000000000000000001535412223032461016655 0ustar rootrootConfiguration Variables for core ================================ [ this file is autogenerated, do not edit ] 1. core.debug debug level. Default: 0. Type: integer. 2. core.log_facility syslog facility, see "man 3 syslog". Default: 24. Type: string. 3. core.memdbg log level for memory debugging messages. Default: 3. Type: integer. 4. core.use_dst_blacklist enable/disable destination blacklisting. Default: 0. Range: 0 - 1. Type: integer. 5. core.dst_blacklist_expire how much time (in s) a blacklisted destination is kept in the list. Default: 60. Type: integer. 6. core.dst_blacklist_mem maximum shared memory amount (in KB) used for keeping the blacklisted destinations. Default: 250. Type: integer. 7. core.dst_blacklist_udp_imask blacklist event ignore mask for UDP. Default: 0. Type: integer. 8. core.dst_blacklist_tcp_imask blacklist event ignore mask for TCP. Default: 0. Type: integer. 9. core.dst_blacklist_tls_imask blacklist event ignore mask for TLS. Default: 0. Type: integer. 10. core.dst_blacklist_sctp_imask blacklist event ignore mask for SCTP. Default: 0. Type: integer. 11. core.dns_try_ipv6 enable/disable IPv6 DNS lookups. Default: 1. Range: 0 - 1. Type: integer. 12. core.dns_try_naptr enable/disable NAPTR DNS lookups. Default: 0. Range: 0 - 1. Type: integer. 13. core.dns_udp_pref udp protocol preference when doing NAPTR lookups. Default: 30. Type: integer. 14. core.dns_tcp_pref tcp protocol preference when doing NAPTR lookups. Default: 20. Type: integer. 15. core.dns_tls_pref tls protocol preference when doing NAPTR lookups. Default: 10. Type: integer. 16. core.dns_sctp_pref sctp protocol preference when doing NAPTR lookups. Default: 20. Type: integer. 17. core.dns_retr_time time in s before retrying a dns request. Default: -1. Type: integer. 18. core.dns_retr_no number of dns retransmissions before giving up. Default: -1. Type: integer. 19. core.dns_servers_no how many dns servers from the ones defined in /etc/resolv.conf will be used. Default: -1. Type: integer. 20. core.dns_use_search_list if set to 0, the search list in /etc/resolv.conf is ignored. Default: 1. Range: 0 - 1. Type: integer. 21. core.dns_search_full_match enable/disable domain name checks against the search list in DNS answers. Default: 1. Range: 0 - 1. Type: integer. 22. core.dns_reinit set to 1 in order to reinitialize the DNS resolver. Default: 0. Range: 1 - 1. Type: integer. 23. core.use_dns_cache enable/disable the dns cache. Default: 1. Range: 0 - 1. Type: integer. 24. core.dns_cache_flags dns cache specific resolver flags (1=ipv4 only, 2=ipv6 only, 4=prefer ipv6. Default: 0. Range: 0 - 4. Type: integer. 25. core.use_dns_failover enable/disable dns failover in case the destination resolves to multiple ip addresses and/or multiple SRV records (depends on use_dns_cache). Default: 0. Range: 0 - 1. Type: integer. 26. core.dns_srv_lb enable/disable load balancing to different srv records of the same priority based on the srv records weights (depends on dns_failover). Default: 0. Range: 0 - 1. Type: integer. 27. core.dns_cache_negative_ttl time to live for negative results ("not found") in seconds. Use 0 to disable. Default: 60. Type: integer. 28. core.dns_cache_min_ttl minimum accepted time to live for a record, in seconds. Default: 0. Type: integer. 29. core.dns_cache_max_ttl maximum accepted time to live for a record, in seconds. Default: -1. Type: integer. 30. core.dns_cache_mem maximum memory used for the dns cache in Kb. Default: 500. Type: integer. 31. core.dns_cache_del_nonexp allow deletion of non-expired records from the cache when there is no more space left for new ones. Default: 0. Range: 0 - 1. Type: integer. 32. core.dns_cache_rec_pref DNS cache record preference: 0 - do not check duplicates 1 - prefer old records 2 - prefer new records 3 - prefer records with longer lifetime. Default: 0. Range: 0 - 3. Type: integer. 33. core.mem_dump_pkg dump process memory status, parameter: pid_number. Default: 0. Type: integer. 34. core.mem_dump_shm dump shared memory status. Default: 0. Type: integer. 35. core.max_while_loops maximum iterations allowed for a while loop. Default: 100. Type: integer. 36. core.udp_mtu fallback to a congestion controlled protocol if send size exceeds udp_mtu. Default: 0. Range: 0 - 65535. Type: integer. 37. core.udp_mtu_try_proto if send size > udp_mtu use proto (1 udp, 2 tcp, 3 tls, 4 sctp). Default: 0. Range: 1 - 4. Type: integer. 38. core.udp4_raw enable/disable using a raw socket for sending UDP IPV4 packets. Should be faster on multi-CPU linux running machines.. Default: 0. Range: -1 - 1. Type: integer. 39. core.udp4_raw_mtu set the MTU used when using raw sockets for udp sending. This value will be used when deciding whether or not to fragment the packets.. Default: 1500. Range: 28 - 65535. Type: integer. 40. core.udp4_raw_ttl set the IP TTL used when using raw sockets for udp sending. -1 will use the same value as for normal udp sockets.. Default: -1. Range: -1 - 255. Type: integer. 41. core.force_rport force rport for all the received messages. Default: 0. Range: 0 - 1. Type: integer. 42. core.memlog log level for memory status/summary information. Default: 3. Type: integer. 43. core.mem_summary memory debugging information displayed on exit (flags): 0 - off, 1 - dump all the pkg used blocks (status), 2 - dump all the shm used blocks (status), 4 - summary of pkg used blocks, 8 - summary of shm used blocks. Default: 3. Range: 0 - 15. Type: integer. kamailio-4.0.4/doc/cfg_list/docbook/0000755000000000000000000000000012223032457015762 5ustar rootrootkamailio-4.0.4/doc/cfg_list/docbook/cfg_malloc_test.xml0000644000000000000000000000230712223032457021633 0ustar rootroot Configuration Variables for malloc_test
malloc_test.check_content check if allocated memory was overwritten by filling it with a special pattern and checking it on free.. Default value: 0. Range: 0 - 1. Type: integer.
malloc_test.realloc_p realloc probability in percents. During tests and mem_rnd_alloc realloc_p percents of the allocations will be made by realloc'ing and existing chunk. The maximum value is limited to 90, to avoid very long mem_rnd_alloc runs (a realloc might also free memory).. Default value: 0. Range: 0 - 90. Type: integer.
kamailio-4.0.4/doc/cfg_list/docbook/cfg_carrierroute.xml0000644000000000000000000000256612223032457022042 0ustar rootroot Configuration Variables for carrierroute
carrierroute.use_domain When using tree lookup per user, this parameter specifies whether to use the domain part for user matching or not.. Default value: 0. Range: 0 - 1. Type: integer.
carrierroute.fallback_default If the user has a non-existing tree set and fallback_default is set to 1, the default tree is used. Else error is returned. Default value: 1. Range: 0 - 1. Type: integer.
carrierroute.fetch_rows The number of the rows to be fetched at once from database when loading the routing data.. Default value: 2000. Type: integer.
kamailio-4.0.4/doc/cfg_list/docbook/cfg_sctp.xml0000644000000000000000000001215712223032457020302 0ustar rootroot Configuration Variables for sctp
sctp.socket_rcvbuf socket receive buffer size (read-only). Range: 512 - 102400. Type: integer. Read-only.
sctp.socket_sndbuf socket send buffer size (read-only). Range: 512 - 102400. Type: integer. Read-only.
sctp.autoclose seconds before closing and idle connection (must be non-zero). Range: 1 - 1073741824. Type: integer.
sctp.send_ttl milliseconds before aborting a send. Range: 0 - 1073741824. Type: integer.
sctp.send_retries re-send attempts on failure. Range: 0 - 9. Type: integer.
sctp.assoc_tracking connection/association tracking (see also assoc_reuse). Range: 0 - 1. Type: integer.
sctp.assoc_reuse connection/association reuse (for now used only for replies), depends on assoc_tracking being set. Range: 0 - 1. Type: integer.
sctp.max_assocs maximum allowed open associations (-1 = disable, as many as allowed by the OS). Type: integer.
sctp.srto_initial initial value of the retr. timeout, used in RTO calculations, in msecs. Range: 0 - 1073741824. Type: integer.
sctp.srto_max maximum value of the retransmission timeout (RTO), in msecs. Range: 0 - 1073741824. Type: integer.
sctp.srto_min minimum value of the retransmission timeout (RTO), in msecs. Range: 0 - 1073741824. Type: integer.
sctp.asocmaxrxt maximum retransmission attempts per association. Range: 0 - 1024. Type: integer.
sctp.init_max_attempts max INIT retransmission attempts. Range: 0 - 1024. Type: integer.
sctp.init_max_timeo max INIT retransmission timeout (RTO max for INIT), in msecs. Range: 0 - 1073741824. Type: integer.
sctp.hbinterval heartbeat interval in msecs. Range: 0 - 1073741824. Type: integer.
sctp.pathmaxrxt maximum retransmission attempts per path. Range: 0 - 1024. Type: integer.
sctp.sack_delay time since the last received packet before sending a SACK, in msecs. Range: 0 - 1073741824. Type: integer.
sctp.sack_freq number of received packets that trigger the sending of a SACK. Range: 0 - 1024. Type: integer.
sctp.max_burst maximum burst of packets that can be emitted by an association. Range: 0 - 1024. Type: integer.
kamailio-4.0.4/doc/cfg_list/docbook/cfg_tcp.xml0000644000000000000000000001515212223032457020115 0ustar rootroot Configuration Variables for tcp
tcp.connect_timeout used only in non-async mode, in seconds. Range: -1 - 134217727. Type: integer.
tcp.send_timeout in seconds. Range: -1 - 2147483647. Type: integer.
tcp.connection_lifetime connection lifetime (in seconds). Range: -1 - 2147483647. Type: integer.
tcp.max_connections maximum connection number, soft limit. Range: 0 - 2147483647. Type: integer.
tcp.no_connect if set only accept new connections, never actively open new ones. Range: 0 - 1. Type: integer.
tcp.fd_cache file descriptor cache for tcp_send. Range: 0 - 1. Type: integer. Read-only.
tcp.async async mode for writes and connects. Range: 0 - 1. Type: integer. Read-only.
tcp.connect_wait parallel simultaneous connects to the same dst. (0) or one connect. Range: 0 - 1. Type: integer. Read-only.
tcp.conn_wq_max maximum bytes queued for write per connection (depends on async). Range: 0 - 1048576. Type: integer.
tcp.wq_max maximum bytes queued for write allowed globally (depends on async). Range: 0 - 1073741824. Type: integer.
tcp.defer_accept 0/1 on linux, seconds on freebsd (see docs). Range: 0 - 3600. Type: integer. Read-only.
tcp.delayed_ack initial ack will be delayed and sent with the first data segment. Range: 0 - 1. Type: integer.
tcp.syncnt number of syn retransmissions before aborting a connect (0=not set). Range: 0 - 1024. Type: integer.
tcp.linger2 lifetime of orphaned sockets in FIN_WAIT2 state in s (0=not set). Range: 0 - 3600. Type: integer.
tcp.keepalive enables/disables keepalives for tcp. Range: 0 - 1. Type: integer.
tcp.keepidle time before sending a keepalive if the connection is idle (linux). Range: 0 - 86400. Type: integer.
tcp.keepintvl time interval between keepalive probes on failure (linux). Range: 0 - 86400. Type: integer.
tcp.keepcnt number of failed keepalives before dropping the connection (linux). Range: 0 - 1024. Type: integer.
tcp.crlf_ping enable responding to CRLF SIP-level keepalives . Range: 0 - 1. Type: integer.
tcp.accept_aliases turn on/off tcp aliases (see tcp_accept_aliases) . Range: 0 - 1. Type: integer.
tcp.alias_flags flags used for adding new aliases (FORCE_ADD:1 , REPLACE:2) . Range: 0 - 2. Type: integer.
tcp.new_conn_alias_flags flags for the def. aliases for a new conn. (FORCE_ADD:1, REPLACE:2 . Range: 0 - 2. Type: integer.
tcp.accept_no_cl accept TCP messges without Content-Lenght . Range: 0 - 1. Type: integer.
tcp.rd_buf_size internal read buffer size (should be > max. expected datagram). Range: 512 - 65536. Type: integer.
tcp.wq_blk_size internal async write block size (debugging use only for now). Range: 1 - 65535. Type: integer.
kamailio-4.0.4/doc/cfg_list/docbook/cfg_maxfwd.xml0000644000000000000000000000106312223032457020611 0ustar rootroot Configuration Variables for maxfwd
maxfwd.max_limit Max. maxfwd limit. Default value: 16. Range: 0 - 255. Type: integer.
kamailio-4.0.4/doc/cfg_list/docbook/cfg_dispatcher.xml0000644000000000000000000000166312223032457021457 0ustar rootroot Configuration Variables for dispatcher
dispatcher.probing_threshhold Number of failed requests, before a destination is set to probing.. Default value: 3. Type: integer.
dispatcher.ping_reply_codes Additional, valid reply codes for the OPTIONS Pinger. Default is "". Default value: <unknown:str>. Type: string.
kamailio-4.0.4/doc/cfg_list/docbook/cfg_registrar.xml0000644000000000000000000000644512223032457021336 0ustar rootroot Configuration Variables for registrar
registrar.default_expires Contains number of second to expire if no expire hf or contact expire present. Default value: 3600. Type: integer.
registrar.default_expires_range Percent from default_expires that will be used in generating the range for the expire interval. Default value: 0. Range: 0 - 100. Type: integer.
registrar.min_expires The minimum expires value of a Contact. Value 0 disables the checking. . Default value: 60. Type: integer.
registrar.max_expires The maximum expires value of a Contact. Value 0 disables the checking. . Default value: 0. Type: integer.
registrar.max_contacts The maximum number of Contacts for an AOR. Value 0 disables the checking. . Default value: 0. Type: integer.
registrar.retry_after If you want to add the Retry-After header field in 5xx replies, set this parameter to a value grater than zero. Default value: 0. Type: integer.
registrar.case_sensitive If set to 1 then AOR comparison will be case sensitive. Recommended and default is 0, case insensitive. Default value: 0. Type: integer.
registrar.default_q The parameter represents default q value for new contacts.. Default value: -1. Range: -1 - 1000. Type: integer.
registrar.append_branches If set to 1(default), lookup will put all contacts found in msg structure. Default value: 1. Type: integer.
registrar.realm_pref Realm prefix to be removed. Default is "". Type: string.
kamailio-4.0.4/doc/cfg_list/docbook/cfg_tm.xml0000644000000000000000000002646312223032457017756 0ustar rootroot Configuration Variables for tm
tm.ruri_matching perform Request URI check in transaction matching. Default value: 1. Range: 0 - 1. Type: integer.
tm.via1_matching perform first Via header check in transaction matching. Default value: 1. Range: 0 - 1. Type: integer.
tm.fr_timer timer which hits if no final reply for a request or ACK for a negative INVITE reply arrives (in milliseconds). Default value: 30000. Type: integer.
tm.fr_inv_timer timer which hits if no final reply for an INVITE arrives after a provisional message was received (in milliseconds). Default value: 120000. Type: integer.
tm.fr_inv_timer_next The value [ms] of fr_inv_timer for subsequent branches during serial forking.. Default value: 30000. Type: integer.
tm.wt_timer time for which a transaction stays in memory to absorb delayed messages after it completed. Default value: 5000. Type: integer.
tm.delete_timer time after which a to-be-deleted transaction currently ref-ed by a process will be tried to be deleted again.. Default value: 200. Type: integer.
tm.retr_timer1 initial retransmission period (in milliseconds). Default value: 500. Type: integer.
tm.retr_timer2 maximum retransmission period (in milliseconds). Default value: 4000. Type: integer.
tm.max_inv_lifetime maximum time an invite transaction can live from the moment of creation. Default value: 180000. Type: integer.
tm.max_noninv_lifetime maximum time a non-invite transaction can live from the moment of creation. Default value: 32000. Type: integer.
tm.noisy_ctimer if set, INVITE transactions that time-out (FR INV timer) will be always replied. Default value: 1. Range: 0 - 1. Type: integer.
tm.auto_inv_100 automatically send 100 to an INVITE. Default value: 1. Range: 0 - 1. Type: integer.
tm.auto_inv_100_reason reason text of the automatically send 100 to an INVITE. Default value: trying -- your call is important to us. Type: string.
tm.unix_tx_timeout Unix socket transmission timeout, in milliseconds. Default value: 500. Type: integer.
tm.restart_fr_on_each_reply restart final response timer on each provisional reply. Default value: 1. Range: 0 - 1. Type: integer.
tm.pass_provisional_replies enable/disable passing of provisional replies to TMCB_LOCAL_RESPONSE_OUT callbacks. Default value: 0. Range: 0 - 1. Type: integer.
tm.aggregate_challenges if the final response is a 401 or a 407, aggregate all the authorization headers (challenges) (rfc3261 requires this to be on). Default value: 1. Range: 0 - 1. Type: integer.
tm.unmatched_cancel determines how CANCELs with no matching transaction are handled (0: statefull forwarding, 1: stateless forwarding, 2: drop). Default value: 0. Range: 0 - 2. Type: integer.
tm.default_code default SIP response code sent by t_reply(), if the function cannot retrieve its parameters. Default value: 500. Range: 400 - 699. Type: integer.
tm.default_reason default SIP reason phrase sent by t_reply(), if the function cannot retrieve its parameters. Default value: Server Internal Error. Type: string.
tm.reparse_invite if set to 1, the CANCEL and negative ACK requests are constructed from the INVITE message which was sent out instead of building them from the received request. Default value: 1. Range: 0 - 1. Type: integer.
tm.ac_extra_hdrs header fields prefixed by this parameter value are included in the CANCEL and negative ACK messages if they were present in the outgoing INVITE (depends on reparse_invite). Default value: <unknown:str>. Type: string.
tm.blst_503 if set to 1, blacklist 503 SIP response sources. Default value: 0. Range: 0 - 1. Type: integer.
tm.blst_503_def_timeout default 503 blacklist time (in s), when no Retry-After header is present. Default value: 0. Type: integer.
tm.blst_503_min_timeout minimum 503 blacklist time (in s). Default value: 0. Type: integer.
tm.blst_503_max_timeout maximum 503 blacklist time (in s). Default value: 3600. Type: integer.
tm.blst_methods_add bitmap of method types that trigger blacklisting on transaction timeouts. Default value: 1. Type: integer.
tm.blst_methods_lookup Bitmap of method types that are looked-up in the blacklist before statefull forwarding. Default value: -9. Type: integer.
tm.cancel_b_method How to cancel branches on which no replies were received: 0 - fake reply, 1 - retransmitting the request, 2 - send cancel. Default value: 1. Range: 0 - 2. Type: integer.
tm.reparse_on_dns_failover if set to 1, the SIP message after a DNS failover is constructed from the outgoing message buffer of the failed branch instead of from the received request. Default value: 1. Range: 0 - 1. Type: integer.
tm.disable_6xx_block if set to 1, 6xx is treated like a normal reply (breaks rfc). Default value: 0. Range: 0 - 1. Type: integer.
tm.local_ack_mode if set to 1 or 2, local 200 ACKs are sent to the same address as the corresponding INVITE (1) or the source of the 200 reply (2) instead of using the contact and the route set (it breaks the rfc, if it is not set to 0 but allows dealing with NATed contacts in some simple cases). Default value: 0. Range: 0 - 2. Type: integer.
tm.local_cancel_reason if set to 1, a Reason header is added to locally generated CANCELs (see RFC3326). Default value: 1. Range: 0 - 1. Type: integer.
tm.e2e_cancel_reason if set to 1, Reason headers from received CANCELs are copied into the corresponding generated hop-by-hop CANCELs. Default value: 1. Range: 0 - 1. Type: integer.
kamailio-4.0.4/doc/cfg_list/docbook/cfg_core.xml0000644000000000000000000003133312223032457020256 0ustar rootroot Configuration Variables for core
core.debug debug level. Default value: 0. Type: integer.
core.log_facility syslog facility, see "man 3 syslog". Default value: 24. Type: string.
core.memdbg log level for memory debugging messages. Default value: 3. Type: integer.
core.use_dst_blacklist enable/disable destination blacklisting. Default value: 0. Range: 0 - 1. Type: integer.
core.dst_blacklist_expire how much time (in s) a blacklisted destination is kept in the list. Default value: 60. Type: integer.
core.dst_blacklist_mem maximum shared memory amount (in KB) used for keeping the blacklisted destinations. Default value: 250. Type: integer.
core.dst_blacklist_udp_imask blacklist event ignore mask for UDP. Default value: 0. Type: integer.
core.dst_blacklist_tcp_imask blacklist event ignore mask for TCP. Default value: 0. Type: integer.
core.dst_blacklist_tls_imask blacklist event ignore mask for TLS. Default value: 0. Type: integer.
core.dst_blacklist_sctp_imask blacklist event ignore mask for SCTP. Default value: 0. Type: integer.
core.dns_try_ipv6 enable/disable IPv6 DNS lookups. Default value: 1. Range: 0 - 1. Type: integer.
core.dns_try_naptr enable/disable NAPTR DNS lookups. Default value: 0. Range: 0 - 1. Type: integer.
core.dns_udp_pref udp protocol preference when doing NAPTR lookups. Default value: 30. Type: integer.
core.dns_tcp_pref tcp protocol preference when doing NAPTR lookups. Default value: 20. Type: integer.
core.dns_tls_pref tls protocol preference when doing NAPTR lookups. Default value: 10. Type: integer.
core.dns_sctp_pref sctp protocol preference when doing NAPTR lookups. Default value: 20. Type: integer.
core.dns_retr_time time in s before retrying a dns request. Default value: -1. Type: integer.
core.dns_retr_no number of dns retransmissions before giving up. Default value: -1. Type: integer.
core.dns_servers_no how many dns servers from the ones defined in /etc/resolv.conf will be used. Default value: -1. Type: integer.
core.dns_use_search_list if set to 0, the search list in /etc/resolv.conf is ignored. Default value: 1. Range: 0 - 1. Type: integer.
core.dns_search_full_match enable/disable domain name checks against the search list in DNS answers. Default value: 1. Range: 0 - 1. Type: integer.
core.dns_reinit set to 1 in order to reinitialize the DNS resolver. Default value: 0. Range: 1 - 1. Type: integer.
core.use_dns_cache enable/disable the dns cache. Default value: 1. Range: 0 - 1. Type: integer.
core.dns_cache_flags dns cache specific resolver flags (1=ipv4 only, 2=ipv6 only, 4=prefer ipv6. Default value: 0. Range: 0 - 4. Type: integer.
core.use_dns_failover enable/disable dns failover in case the destination resolves to multiple ip addresses and/or multiple SRV records (depends on use_dns_cache). Default value: 0. Range: 0 - 1. Type: integer.
core.dns_srv_lb enable/disable load balancing to different srv records of the same priority based on the srv records weights (depends on dns_failover). Default value: 0. Range: 0 - 1. Type: integer.
core.dns_cache_negative_ttl time to live for negative results ("not found") in seconds. Use 0 to disable. Default value: 60. Type: integer.
core.dns_cache_min_ttl minimum accepted time to live for a record, in seconds. Default value: 0. Type: integer.
core.dns_cache_max_ttl maximum accepted time to live for a record, in seconds. Default value: -1. Type: integer.
core.dns_cache_mem maximum memory used for the dns cache in Kb. Default value: 500. Type: integer.
core.dns_cache_del_nonexp allow deletion of non-expired records from the cache when there is no more space left for new ones. Default value: 0. Range: 0 - 1. Type: integer.
core.dns_cache_rec_pref DNS cache record preference: 0 - do not check duplicates 1 - prefer old records 2 - prefer new records 3 - prefer records with longer lifetime. Default value: 0. Range: 0 - 3. Type: integer.
core.mem_dump_pkg dump process memory status, parameter: pid_number. Default value: 0. Type: integer.
core.mem_dump_shm dump shared memory status. Default value: 0. Type: integer.
core.max_while_loops maximum iterations allowed for a while loop. Default value: 100. Type: integer.
core.udp_mtu fallback to a congestion controlled protocol if send size exceeds udp_mtu. Default value: 0. Range: 0 - 65535. Type: integer.
core.udp_mtu_try_proto if send size > udp_mtu use proto (1 udp, 2 tcp, 3 tls, 4 sctp). Default value: 0. Range: 1 - 4. Type: integer.
core.udp4_raw enable/disable using a raw socket for sending UDP IPV4 packets. Should be faster on multi-CPU linux running machines.. Default value: 0. Range: -1 - 1. Type: integer.
core.udp4_raw_mtu set the MTU used when using raw sockets for udp sending. This value will be used when deciding whether or not to fragment the packets.. Default value: 1500. Range: 28 - 65535. Type: integer.
core.udp4_raw_ttl set the IP TTL used when using raw sockets for udp sending. -1 will use the same value as for normal udp sockets.. Default value: -1. Range: -1 - 255. Type: integer.
core.force_rport force rport for all the received messages. Default value: 0. Range: 0 - 1. Type: integer.
core.memlog log level for memory status/summary information. Default value: 3. Type: integer.
core.mem_summary memory debugging information displayed on exit (flags): 0 - off, 1 - dump all the pkg used blocks (status), 2 - dump all the shm used blocks (status), 4 - summary of pkg used blocks, 8 - summary of shm used blocks, 16 - show only summary of used blocks instead of full dump (to use in conjuntion with flags 1 and 2). Default value: 3. Range: 0 - 31. Type: integer.
kamailio-4.0.4/doc/cfg_list/docbook/cfg_siputils.xml0000644000000000000000000000110612223032457021175 0ustar rootroot Configuration Variables for siputils
siputils.ring_timeout define how long the Call-id is kept in the internal list. Default value: 0. Type: integer.
kamailio-4.0.4/doc/cfg_list/docbook/cfg_var_list.xml0000644000000000000000000000212612223032457021147 0ustar rootroot ] > Runtime Configuration Variables List sip-router git-a1b23f Mon, 04 Oct 2010 20:24:11 +0200 Automatically generated by: make -C doc/cfg_list all kamailio-4.0.4/doc/cfg_list/docbook/cfg_tls.xml0000644000000000000000000002255112223032457020132 0ustar rootroot Configuration Variables for tls
tls.force_run force loading the tls module even when initial sanity checks fail. Default value: 0. Range: 0 - 1. Type: integer. Read-only.
tls.method TLS method used (TLSv1, SSLv3, SSLv2, SSLv23). Default value: <unknown:str>. Type: string. Read-only.
tls.verify_certificate if enabled the certificates will be verified. Default value: 0. Range: 0 - 1. Type: integer. Read-only.
tls.verify_depth sets how far up the certificate chain will the certificate verification go in the search for a trusted CA. Default value: 9. Range: 0 - 100. Type: integer. Read-only.
tls.require_certificate if enabled a certificate will be required from clients. Default value: 0. Range: 0 - 1. Type: integer. Read-only.
tls.private_key name of the file containing the private key (pem format), if not contained in the certificate file. Default value: <unknown:str>. Type: string. Read-only.
tls.ca_list name of the file containing the trusted CA list (pem format). Default value: <unknown:str>. Type: string. Read-only.
tls.crl name of the file containing the CRL (certificare revocation list in pem format). Default value: <unknown:str>. Type: string. Read-only.
tls.certificate name of the file containing the certificate (pem format). Default value: <unknown:str>. Type: string. Read-only.
tls.cipher_list list of the accepted ciphers (strings separated by colons). Default value: <unknown:str>. Type: string. Read-only.
tls.session_cache enables or disables the session cache. Default value: 0. Range: 0 - 1. Type: integer. Read-only.
tls.session_id string used for the session id. Default value: <unknown:str>. Type: string. Read-only.
tls.config tls config file name (used for the per domain options). Default value: <unknown:str>. Type: string.
tls.log tls info messages log level. Default value: 3. Range: 0 - 1000. Type: integer.
tls.debug tls debug messages log level. Default value: 3. Range: 0 - 1000. Type: integer.
tls.connection_timeout initial connection lifetime (in s) (obsolete). Default value: 600. Range: -1 - -2147483648. Type: integer.
tls.disable_compression if set disable the built-in OpenSSL compression. Default value: 1. Range: 0 - 1. Type: integer. Read-only.
tls.ssl_release_buffers quickly release internal OpenSSL read or write buffers. Works only for OpenSSL >= 1.0.. Default value: -1. Range: -1 - 1. Type: integer. Read-only.
tls.ssl_free_list_max maximum number of free/cached memory chunks that OpenSSL will keep per connection. Works only for OpenSSL >= 1.0.. Default value: -1. Range: -1 - 1073741824. Type: integer. Read-only.
tls.ssl_max_send_fragment sets the maximum number of bytes (clear text) send into one TLS record. Valid values are between 512 and 16384. Works only for OpenSSL >= 0.9.9. Default value: -1. Range: -1 - 65536. Type: integer. Read-only.
tls.ssl_read_ahead Enables read ahead, reducing the number of BIO read calls done internally by the OpenSSL library. Note that in newer tls module versions it is better to have read ahead disabled, since everything it is buffered in memory anyway. Default value: 0. Range: -1 - 1. Type: integer. Read-only.
tls.low_mem_threshold1 sets the minimum amount of free memory for accepting new TLS connections (KB). Default value: -1. Range: -1 - 1073741824. Type: integer.
tls.low_mem_threshold2 sets the minimum amount of free memory after which no more TLS operations will be attempted (even on existing connections). Default value: -1. Range: -1 - 1073741824. Type: integer.
tls.ct_wq_max maximum bytes queued globally for write when write has to wait due to TLS-level renegotiation (SSL_ERROR_WANT_READ) or initial TLS connection establishment (it is different from tcp.wq_max, which works at the TCP connection level). Default value: 10485760. Range: 0 - 1073741824. Type: integer.
tls.con_ct_wq_max maximum bytes queued for write per connection when write has to wait due to TLS-level renegotiation (SSL_ERROR_WANT_READ) or initial TLS connection establishment (it is different from tcp.conn_wq_max, which works at the TCP connection level). Default value: 65536. Range: 0 - 4194304. Type: integer.
tls.ct_wq_blk_size internal TLS pre-write (clear-text) queue minimum block size (advanced tunning or debugging for now). Default value: 4096. Range: 1 - 65536. Type: integer.
tls.send_close_notify enable/disable sending a close notify TLS shutdown alert before closing the corresponding TCP connection.Note that having it enabled has a performance impact.. Default value: 0. Range: 0 - 1. Type: integer.
kamailio-4.0.4/doc/cfg_list/docbook/Makefile0000644000000000000000000000026312223032457017423 0ustar rootroot docs = cfg_var_list.xml docbook_dir = ../../../docbook # no subsection listed in the main TOC xsltproc_flags+=--stringparam toc.section.depth 0 include $(docbook_dir)/Makefile kamailio-4.0.4/doc/cfg_list/cfg_maxfwd.txt0000644000000000000000000000037412223032457017214 0ustar rootrootConfiguration Variables for maxfwd ================================== [ this file is autogenerated, do not edit ] 1. maxfwd.max_limit Max. maxfwd limit. Default: 16. Range: 0 - 255. Type: integer. kamailio-4.0.4/doc/cfg_list/cfg_tls.txt0000644000000000000000000001267612223032457016540 0ustar rootrootConfiguration Variables for tls =============================== [ this file is autogenerated, do not edit ] 1. tls.force_run force loading the tls module even when initial sanity checks fail. Default: 0. Range: 0 - 1. Type: integer. Read-only. 2. tls.method TLS method used (TLSv1, SSLv3, SSLv2, SSLv23). Default: . Type: string. Read-only. 3. tls.verify_certificate if enabled the certificates will be verified. Default: 0. Range: 0 - 1. Type: integer. Read-only. 4. tls.verify_depth sets how far up the certificate chain will the certificate verification go in the search for a trusted CA. Default: 9. Range: 0 - 100. Type: integer. Read-only. 5. tls.require_certificate if enabled a certificate will be required from clients. Default: 0. Range: 0 - 1. Type: integer. Read-only. 6. tls.private_key name of the file containing the private key (pem format), if not contained in the certificate file. Default: . Type: string. Read-only. 7. tls.ca_list name of the file containing the trusted CA list (pem format). Default: . Type: string. Read-only. 8. tls.crl name of the file containing the CRL (certificare revocation list in pem format). Default: . Type: string. Read-only. 9. tls.certificate name of the file containing the certificate (pem format). Default: . Type: string. Read-only. 10. tls.cipher_list list of the accepted ciphers (strings separated by colons). Default: . Type: string. Read-only. 11. tls.session_cache enables or disables the session cache. Default: 0. Range: 0 - 1. Type: integer. Read-only. 12. tls.session_id string used for the session id. Default: . Type: string. Read-only. 13. tls.config tls config file name (used for the per domain options). Default: . Type: string. 14. tls.log tls info messages log level. Default: 3. Range: 0 - 1000. Type: integer. 15. tls.debug tls debug messages log level. Default: 3. Range: 0 - 1000. Type: integer. 16. tls.connection_timeout initial connection lifetime (in s) (obsolete). Default: 600. Range: -1 - -2147483648. Type: integer. 17. tls.disable_compression if set disable the built-in OpenSSL compression. Default: 1. Range: 0 - 1. Type: integer. Read-only. 18. tls.ssl_release_buffers quickly release internal OpenSSL read or write buffers. Works only for OpenSSL >= 1.0.. Default: -1. Range: -1 - 1. Type: integer. Read-only. 19. tls.ssl_free_list_max maximum number of free/cached memory chunks that OpenSSL will keep per connection. Works only for OpenSSL >= 1.0.. Default: -1. Range: -1 - 1073741824. Type: integer. Read-only. 20. tls.ssl_max_send_fragment sets the maximum number of bytes (clear text) send into one TLS record. Valid values are between 512 and 16384. Works only for OpenSSL >= 0.9.9. Default: -1. Range: -1 - 65536. Type: integer. Read-only. 21. tls.ssl_read_ahead Enables read ahead, reducing the number of BIO read calls done internally by the OpenSSL library. Note that in newer tls module versions it is better to have read ahead disabled, since everything it is buffered in memory anyway. Default: 0. Range: -1 - 1. Type: integer. Read-only. 22. tls.low_mem_threshold1 sets the minimum amount of free memory for accepting new TLS connections (KB). Default: -1. Range: -1 - 1073741824. Type: integer. 23. tls.low_mem_threshold2 sets the minimum amount of free memory after which no more TLS operations will be attempted (even on existing connections). Default: -1. Range: -1 - 1073741824. Type: integer. 24. tls.ct_wq_max maximum bytes queued globally for write when write has to wait due to TLS-level renegotiation (SSL_ERROR_WANT_READ) or initial TLS connection establishment (it is different from tcp.wq_max, which works at the TCP connection level). Default: 10485760. Range: 0 - 1073741824. Type: integer. 25. tls.con_ct_wq_max maximum bytes queued for write per connection when write has to wait due to TLS-level renegotiation (SSL_ERROR_WANT_READ) or initial TLS connection establishment (it is different from tcp.conn_wq_max, which works at the TCP connection level). Default: 65536. Range: 0 - 4194304. Type: integer. 26. tls.ct_wq_blk_size internal TLS pre-write (clear-text) queue minimum block size (advanced tunning or debugging for now). Default: 4096. Range: 1 - 65536. Type: integer. 27. tls.send_close_notify enable/disable sending a close notify TLS shutdown alert before closing the corresponding TCP connection.Note that having it enabled has a performance impact.. Default: 0. Range: 0 - 1. Type: integer. kamailio-4.0.4/doc/cfg_list/Makefile0000644000000000000000000002541112223032461016000 0ustar rootroot COREPATH=../.. #include $(COREPATH)/Makefile.defs CFG2TXT=../scripts/cdefs2doc/dump_cfg_defs.pl CFG2DOCBOOK=../scripts/cdefs2doc/dump_cfg_defs.pl # output directory for generated txt files txt_output_dir=. # output directory for generated docbook xml files docbook_output_dir=docbook # list of files containing cfg defs in the following format: # : # can be easily updated by adding the output of: # make diff-list (which obeys grp_exclude and file_exclude) # or completely regenerated by replacing files_list with the output of: # make gen-files-list files_list= \ $(COREPATH)/cfg_core.c:core \ $(COREPATH)/sctp_options.c:sctp \ $(COREPATH)/tcp_options.c:tcp \ $(COREPATH)/modules/tm/config.c:tm \ $(COREPATH)/modules_k/registrar/config.c:registrar \ $(COREPATH)/modules_k/siputils/config.c:siputils \ $(COREPATH)/modules_s/maxfwd/maxfwd.c:maxfwd \ $(COREPATH)/modules/carrierroute/config.c:carrierroute \ $(COREPATH)/modules/malloc_test/malloc_test.c:malloc_test \ $(COREPATH)/modules/tls/tls_cfg.c:tls \ $(COREPATH)/modules_k/dispatcher/config.c:dispatcher # list of excluded groups grp_exclude=pa # list of file prefixes to exclude (full path needed) file_exclude= $(COREPATH)/modules_s/tls/ # special per file group overrides # format= grp_filename=... ,where filename does not contain the extension # e.g.: # grp_f_tcp_options=tcp # grp_f_sctp_options=sctp # special per group group name overrides # e.g.: # grp_g_maxfwd=mf # override auto-detected group if set to 1 (else the group is used inside the # file only if it cannot be aut-odetected) ifeq ($(group_override),1) override force_grp=force- else override force_grp= endif # command used for gcc (contains extra includes) gcc=gcc #-I$(COREPATH)/lib -I$(COREPATH) -I/usr/include/libxml2 # defines used by gcc c_defs=-D__CPU_i386 -D__OS_linux -DSER_VER=2099099 -DPKG_MALLOC -DSHM_MEM \ -DSHM_MMAP -DDNS_IP_HACK -DUSE_IPV6 -DUSE_MCAST -DUSE_TCP \ -DUSE_DNS_CACHE -DUSE_DNS_FAILOVER -DUSE_DST_BLACKLIST -DUSE_NAPTR \ -DUSE_TLS -DTLS_HOOKS -DFAST_LOCK -DCC_GCC_LIKE_ASM \ -DHAVE_GETHOSTBYNAME2 -DHAVE_UNION_SEMUN -DHAVE_SCHED_YIELD \ -DHAVE_MSG_NOSIGNAL -DHAVE_MSGHDR_MSG_CONTROL -DHAVE_ALLOCA_H \ -DHAVE_SCHED_SETSCHEDULER -DHAVE_EPOLL -DUSE_SCTP -DNAME='\"ser\"' \ -DVERSION='\"2.99.99-pre3\"' -DARCH='\"i386\"' -DOS_QUOTED='\"linux\"' # common makefile vars used in defs LOCALBASE=/usr/local SYSBASE=/usr filter_files=$(filter-out $(addsuffix %,$(file_exclude)),\ $(filter-out $(addprefix %:,$(grp_exclude)),$(1))) #filtered files list flist=$(call filter_files,$(files_list)) # throws an error if input is not in the format filename:grp check_fname_grp=$(if $(filter-out 2,$(words $(subst :, ,$(1)))),\ $(error bad format "$(1)", it should be filename:grp)) # get prereq from file:grp (get_prereq(file:grp) => file) get_prereq=$(firstword $(subst :, ,$(1))) # get grp from file:grp (get_grp(file:grp) => grp) get_listed_grp=$(word 2, $(subst :, ,$(1))) # get base file name from file:grp: get_bname(file:grp) # => basename(file) without extension (e.g. get_bname(foo/bar.c:x) => bar) # get_bname=$(basename $(notdir $(call get_prereq,$(1)))) #get grp from file:grp, using the overrides get_grp=$(strip $(if $(grp_f_$(call get_bname,$(1))), \ $(grp_f_$(call get_bname,$(1))),\ $(if $(grp_g_$(call get_listed_grp,$(1))),\ $(grp_g_$(call get_listed_grp,$(1))),\ $(call get_listed_grp,$(1))) ) ) # get target from file:grp (get_target(file:grp) => cfg_grp.txt) get_target=cfg_$(call get_grp,$(1)) # $(LF) definition (do not remove) define LF endef # get all the lines containing DEFS or INCLUDES definitions from the Makefile. # WARNING: does not work with all sed implementation (tested with GNU sed). # It uses a hack to restore the LFs (LFs are removed by $(shell)): LFs are # replaced with '^LF^' and then ^LF^ is subst'ed back to a real LF. get_make_idefs=$(subst ^LF^,$(LF),$(shell sed \ -ne '/^[\t ]*\(DEFS\|INCLUDES\)[\t ]*[+:]\?=.*[^\]$$/H'\ -ne '/^[\t ]*\(DEFS\|INCLUDES\)[\t ]*[+:]\?=.*\\$$/,/\(^$$\)\|\([^\]$$\)/H'\ -ne '$${g;s/\n/^LF^/g;p}'\ < $(1)/Makefile )) # get all the lines from the makefile containing variable definitions. # It will also return conditionals and try to filter out possible rules. # WARNING: does not work with all sed implementation (tested with GNU sed). # It uses a hack to restore the LFs (LFs are removed by $(shell)): LFs are # replaced with '^LF^' and then ^LF^ is subst'ed back to a real LF. get_make_vars=$(subst ^LF^,$(LF),$(shell sed -n \ -e ': start' \ -e '/^\(ifeq\|ifneq\|else\|endif\)[\t ]*\($$\|.*[^\]$$\)/{H;b end}' \ -e '/^\(ifeq\|ifneq\|else\|endif\)[\t ]\+.*[\]$$/,/[^\]$$/{H;b end}' \ -e '/^[a-zA-Z._/$$][a-zA-Z0-9._()/$$ \t-]*:\([^=]\|$$\)/b eat_rule' \ -e '/^[\t ]*[A-Za-z._][A-Za-z0-9._-]*[\t ]*[+:]\?=.*[^\]$$/{H;b end}' \ -e '/^[\t ]*[A-Za-z._][A-Za-z0-9._-]*[\t ]*[+:]\?=.*\\$$/,/\(^$$\)\|\([^\]$$\)/{H;b end}' \ -e ': end' \ -e '$${g;s/\n/^LF^/g;p}'\ -e 'b' \ -e ': eat_rule' \ -e '$$b end' \ -e 'n' \ -e '/^[a-zA-Z._/$$][a-zA-Z0-9._()/$$ \t-]*:\([^=]\|$$\)/b eat_rule' \ -e '/^[\t]/b eat_rule' \ -e 'b start' \ < $(1)/Makefile )) define mk_rules $(call check_fname_grp, $(1)) #$$(info generating cfg_$$(call get_grp,$(1)).txt: $$(call get_prereq,$(1))) DEFS:= INCLUDES:= # extract all the includes and defs from the module makefile and # evaluate them $$(eval $$(call get_make_vars,$$(dir $$(call get_prereq,$(1))))) # override COREPATH (we know better) COREPATH=../.. # save the result in a per group e_idefs_ var $$(eval e_idefs_$$(call get_grp,$(1)):=$$(DEFS) $$(INCLUDES)) # debugging: #$$(info eval: $$(call get_make_vars,$$(dir $$(call get_prereq,$(1))))) #$$(info e_idefs_$$(call get_grp,$(1))=$$(e_idefs_$$(call get_grp,$(1)))) $(txt_output_dir)/$$(call get_target,$(1)).txt: \ $$(call get_prereq,$(1)) Makefile $(CFG2TXT) $(CFG2TXT) --file $$< --$(force_grp)grp=$$(call get_grp,$(1)) \ --gcc="$(gcc)" --txt \ --defs="$(c_defs) $$(e_idefs_$$(call get_grp,$(1)))" \ > "$$@" || (rm -f "$$@"; exit 1) $(docbook_output_dir)/$$(call get_target,$(1)).xml: \ $$(call get_prereq,$(1)) Makefile $(CFG2TXT) $(CFG2DOCBOOK) --file $$< --$(force_grp)grp=$$(call get_grp,$(1)) \ --gcc="$(gcc)" --docbook \ --defs="$(c_defs) $$(e_idefs_$$(call get_grp,$(1)))" \ > "$$@" || (rm -f "$$@"; exit 1) clean_$$(call get_target,$(1)).txt: rm -f "$(txt_output_dir)/$$(call get_target,$(1)).txt" clean_$$(call get_target,$(1)).xml: rm -f "$(docbook_output_dir)/$$(call get_target,$(1)).xml" txt: $(txt_output_dir)/$$(call get_target,$(1)).txt docbook: $(docbook_output_dir)/$$(call get_target,$(1)).xml clean_txt: clean_$$(call get_target,$(1)).txt clean_docbook: clean_$$(call get_target,$(1)).xml endef find_cfg_files_cmd= find $(COREPATH) -type f -name "*.c" \ -exec grep "cfg_def_t[ ][a-zA-Z0-9_]*\[\][ ]*=" /dev/null {} \; \ | cut -d: -f1 # shell command to generate a file:grp list from a list of files # grp will be the modulename if the file is in a module directory or # the file name with the extension and _cfg, cfg_ or _options stripped out of # it. # output: list of " "filename":"grpname gen_file_grp_cmd=\ sed -e "s!\(.*/modules[^/]*/\([^/][^/]*\)/.*\)! \1:\2!" \ -e "s!^\([^ ].*/\([^/.]*\)[^/]*$$\)!\1:\2!" \ -e "s!^\([^ :]*\):\(.*\)_cfg[_]*!\1:\2!" \ -e "s!^\([^ :]*\):\(.*\)cfg[_]*!\1:\2!" \ -e "s!^\([^ :]*\):\(.*\)_options[_]*!\1:\2!" # special vars for generating the list of files or updates found_lst=$(shell $(find_cfg_files_cmd) | $(gen_file_grp_cmd)) # filtered found lst f_found_lst=$(call filter_files,$(found_lst)) diff_lst=$(filter-out $(flist),$(f_found_lst)) get_core_files=$(filter-out $(COREPATH)/modules% $(COREPATH)/lib%,$(1)) sort_files=$(sort $(call get_core_files,$(1)))\ $(sort $(filter-out $(call get_core_files,$(1)),$(1))) # replace $(COREPATH) with the text "$(COREPATH)" subst_corepath=$(patsubst $(patsubst %/,%,$(COREPATH))/%,\$$(COREPATH)/%,$(1)) # help will be the default rule (on-purpose since without having a patched # GCC:TranslationUnit module, make all won't work) .PHONY: help help: @echo "To regenerate $(foreach f,$(flist),$(call get_target,$f).{txt,xml})" @echo "type: $(MAKE) all ." @echo "or to regenerate all the cfg documentation by searching all" @echo " the source files for definitions, type: $(MAKE) autogen ." @echo "NOTE: you need the GCC:TranslationUnit perl module with an " @echo "extra patch applied (see $(CFG2TXT) --patch)." .PHONY: txt txt: .PHONY: docbook docbook: .PHONY: clean_txt clean_txt: .PHONY: clean_docbook clean_docbook: .PHONY: all all: txt $(docbook_output_dir)/cfg_var_list.xml .PHONY: clean clean: clean_txt clean_docbook @rm -f $(docbook_output_dir)/cfg_var_list.xml .PHONY: proper proper: @rm -f $(txt_output_dir)/cfg_*.txt @rm -f $(docbook_output_dir)/cfg_*.xml repo_ver="sip-router"\ "git-$(shell git rev-parse --verify --short=6 HEAD 2>/dev/null)" ifeq ($(repo_ver),git-) repo_ver="sip-router unknown" endif $(docbook_output_dir)/cfg_var_list.xml: Makefile \ $(foreach f,$(flist),$(docbook_output_dir)/$(call get_target,$f).xml) @echo '' >$@ @echo '' >>$@ @echo '>$@ @echo ' "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"' >>$@ @echo ' [ >$@ @echo " \"xmlns:xi CDATA #FIXED 'http://www.w3.org/2001/XInclude'\">]">>$@ @echo '>' >>$@ @echo '' >>$@ @echo ' Runtime Configuration Variables List' >>$@ @echo ' ' >>$@ @echo ' '$(repo_ver)'' >>$@ @echo ' '`date -R`'' >>$@ @echo ' ' >>$@ @echo " Automatically generated by:">>$@ @echo " $(MAKE) -C doc/cfg_list $(MAKECMDGOALS)" >>$@ @echo ' ' >>$@ @echo ' ' >>$@ @$(foreach f,$(flist),\ echo ' ' \ >>$@ ; ) @echo '' >>$@ # finds all the files containing cfg defs .PHONY: find find: @$(find_cfg_files_cmd) # print the list of the autogenerated files .PHONY: print-lst print-lst: @$(find_cfg_files_cmd) | $(gen_file_grp_cmd) # .PHONY: gen-file-list .PHONY: gen-files_list .PHONY: gen_files_list gen-file-list gen-files-list gen_files_list: @$(foreach f,$(call subst_corepath,$(call sort_files,$(f_found_lst))),\ echo "$f \\";) .PHONY: check-list .PHONY: update-list .PHONY: diff-list check-list update-list diff-list: @$(foreach f,$(call subst_corepath,$(call sort_files,$(diff_lst))),\ echo "$f \\";) # try to generate the docs from all the sources .PHONY: autogen autogen: @$(MAKE) all files_list="$(call sort_files,$(f_found_lst))" $(foreach f,$(flist),$(eval $(call mk_rules,$(f)))) kamailio-4.0.4/doc/cfg_list/cfg_siputils.txt0000644000000000000000000000042312223032457017575 0ustar rootrootConfiguration Variables for siputils ==================================== [ this file is autogenerated, do not edit ] 1. siputils.ring_timeout define how long the Call-id is kept in the internal list. Default: 0. Type: integer. kamailio-4.0.4/doc/serfaq/0000755000000000000000000000000012223032460014023 5ustar rootrootkamailio-4.0.4/doc/serfaq/serfaq.xml0000644000000000000000000012500712223032460016033 0ustar rootroot
Jan Janak jan@iptel.org $Revision$ $Date$ A compilation of questions and answers from serusers@iptel.org, and serdev@iptel.org mailing lists. 2003 FhG FOKUS Frequently Asked Questions Is it possible to use SER as a SIP user agent (both of User Agent Client (UAC) and User Agent Server (UASS)? Not easily. SER has built-in some functions that allow to use it as user agent, but our primary goal is to develop a server so this is without guarantee and can even disappear in future versions. Can SER work well together with some of the location server (e. g.LDAP or DNS) ? SER's built-in location server uses in-RAM database for high performance and optionally MySQL for persistence. More database protocols may be supplied on contractual basis. As far as I know, DNS is not used in the industry for user location. What is a proxy server ? A proxy server is an entity that routes SIP messages. See SIP introduction which is part of the distribution. What is the difference between proxy server and back to back user agent (B2BUA) ? The main difference is that proxy servers are transaction-stateful, while B2BUAs are call stateful. That means proxy servers keep state only during SIP transactions (that is at the beginning and and of a call) and do not keep any state during the whole call. A B2BUA acts merely as connection of two or more user agents which are connected through some means. B2BUAs keep some state (usually some structures in the memory) during the whole call. This property gives B2BUA some interesting features that proxies don't have. For example B2BUA can tear down and existing call--proxies can't do that. On the other hand B2BUAs can easily become a bottleneck in terms of scalability. So is SER proxy or B2BUA ? Can it terminate an existing call ? SER is a proxy. I can't terminate existing call. I'd like to know if SER supports CPL and servlets. Where can I find any documentation about this, or any link ? Yes, CPL is supported through cpl module, the module needs an external CPL interpreter written in Java. A C version is under development. There is no support for Java servlets. The documentation can be found at http://iptel.org/ser/doc. I wanted to know whether <PUT_YOUR_FAVOURITE_METHOD_HERE> is supported by SER. Proxy server are indifferent of non-INVITEs methods. <METHOD> works as good as BYE, INFO, and FOOBAR. I was wondering if SER has been tested and is supported on FreeBSD ? Yes. Does SER support TCP ? Yes. I think I found a bug that should be fixed, what information should I send and where should I send it? Please send us as much info as possible. We would like see the following: SER version (ser -V). Configuration file. SIP message dumps. Coredump (if any, if not please generate it). Anything else you think might help us. The whole compiled source tree. Please send it to serusers@lists.iptel.org. How does SER scale ? SER is able to handle thousands calls per second on a regular PC. I read Throughput thousands of calls per second (CPS) on a dual-CPU PC on your webpage. How fast is it really ? Last time we have checked ~4900cps on a dual Athlon MP2000. This was ser 0.8.9 running statefully (stateless is should be much faster), with 4 processes and 256 Mb shared mem. It was compiled with: STATS:Off, USE_IPV6, NO_DEBUG, SHM_MEM, SHM_MMAP, PKG_MALLOC, F_MALLOC, FAST_LOCK-ADAPTIVE_WAIT ADAPTIVE_WAIT_LOOPS=1024, MAX_RECV_BUFFER_SIZE 262144, MAX_LISTEN 16, MAX_URI_SIZE 1024, BUF_SIZE 3040 Do you think that SER is suitable for a commercial environment in your opinion ? Sure. Do you have any recommendations on additional open source or developer community resources to round out my platform? I'm a SER believer and think that other servers simply don't compare :) If you wish more detailed propaganda, check our website and if that is not enough I will send you some more. Do you have any thoughts on how it compares in deployability and features to the Vovida stuff or other options ? Also, are there any issues I should be thinking of that would make a commercial venture with SER difficult (licensing, scaling, etc..) SER is licensed under the GNU GPL. I would be surprised if any SIP server available today would scale a bit better--we spent lot of work on performance, achieved thousands of CPS on a PC--I guess it will take lot of time until your demand hits this capacity. Does SER have the capability to automatically send an INVITE from one number to another? Yes if the phones support REFER. There is an application called Click-To-Dial which can connect two phones. Is it necessary to have a DNS SVR Resource Record, as mentioned in the ser-Howto guide for connecting to SER ? No. It's nice to have it, but you can work around it setting a normal A record for your domain. The SRV lookup will fall back to normal A lookup. What would you charge to help get us started? See http://iptel.org/support. Are you going to provide integration support from SIP to H.323 and vice versa ? No, we are not going to provide the integration. I've gone thru the SER Admin's Guide and the module documentation (sip_router/modules/). Is there additional documentation on how to use the modules? Each module has a doc subdirectory which contains complete documentation of the module in docbook format. PS, PDF and HTML renderings can be obtained through our web page, see http://iptel.org/ser/doc. What functions would I use in my ser.cfg to log missed calls? FIXME. Are there some additional requirements for using the acc.so module? Radius? I want to be able to view dialed calls from serweb. FIXME. Do you have any example configs that use nathelper? FIXME. Who are the people behind SER ? FIXME. How successful has nathelper been with NAT/firewall transversal? I only know that users reported success with nathelper and ATAs. Does ser support LDAP ? FIXME. Is it possible to get access to iptel's working copy of ser.cfg? We no longer disclose our operational policy to the public audience. The configuration file is only available to our customers under NDA. Can you send me detail for me to sign up for the commercial program. I would also like detail on your levels of support you offer (ie...paid support)? See http://iptel.org/support/. Don't hesitate to approach us if you have any further questions. I need a method to determine if a called URI has an account on the system. I found a function in the groups module, is_user_in. but haven't yet figured out how to use it. The uri module's does_uri_exist is what you are seeking. We have a question regarding usrloc: where are the active sessions being stored? We were not able to find any entries in MySQL. SER has no notion of active session. SER is a transaction-stateful proxy, that means it knows about transactions (INVITE transaction, BYE transaction), but it keeps no state if there is no transaction active. That means, it keeps state when an INVITE comes and until a final response to the INVITE is sent. The same for BYE. Has the timeout for re-registering at the UA to be the same as in the tm module mentioned in ser.cfg ? No. How do we deal with aliases? If for example uid=mic is authenticated, he is available with sip address sip:mic@comnect.at. If I would want to be addressable with michael.dosser@comnect.at how is this accomplished with ser ? Aliases are tied to user location. It uses a special table which has the same structure as user location table and includes an entry for each alias. You do not have to do something special for that. You have to create this table and then you can use serctl to add aliases. I would like to know if SER supports IPv6. If not, do you have any plans for it ? When ? Yes, SER supports IPv6. Is there any way to change the default log file to a special SER.log file ? Try logging to stderr and redirecting it to a file: ser -E 2>/tmp/ser.log (by default ser logs to syslog) Does anybody know anything about the P-Hint Header Field added from SER? http://www.iptel.org/ser/doc/seruser-html/c638.html#AEN729 When using the Jabber Gateway, some users get the following error from time to time: ERROR: Connection to Jabber server lost. You have to login to Jabber server again (join again the conferences that you were participating, too). sip_to_jabber_gateway says: INFO: Your are now offline in Jabber network. Thank you for using SIP-Jabber gateway. Do you know the reason why these messages appear and if it is possible to avoid them? that usually occurs because Jabber server crashes or, for some unknown reasons, the TCP connection with jabber server is down. The second message is to inform the users that they are no more connected to Jabber network. There is no way to disable sending these messages. I may introduce a new parameter to enable/disable them. But I am not sure it is a good idea (users must be informed about the changes of the status). I would like know if SER support also the transmission protocol TCP, or TLS. Yes, SER supports TCP. TLS is work in progress. I'm trying to rewrite the to domain, as in: To: <sip:19723236598@augustvoice.net> ;user=phone. I can't find any rewrite* function to rewrite the to domain. The proxy is not supposed to touch To or From URIs. Only Request-URI can be changed. When the messenger sends a message for the jabber gateway the following error occurs: ERROR: Your message was not sent. You do not have permissions to use the gateway. What could be the problem here? You have to create the database for SIMPLE2Jabber gateway and after that you have to associate SIP users with Jabber IDs. http://www.iptel.org/ser/doc/jabgw/xjab-manual.html#5._Admins_guide Authentication doesn't work!!! Where am I wrong? Windows Messenger needs same string both realm and SIP_DOMAIN, and it wants to reach this address oh your network. I was wondering whether there is a ser module for SIP-CGI. Or whether there are attempts at creating one? There is no SIP-CGI module for SER. The most similar, though much simpler, functionality is provided by the exec module. How could I integrate SIP and H323 together? You will need a SIP to H.323 gateway. SER is a SIP proxy only, it cannot act as H.323 gateway. How can I contribute code ? FIXME. What, if anything, should the SER server do with a OPTIONS method? FIXME. What should I do to see the detailed debugs? Set debug=9 in your config script. Is it possible for SER to forward a call to another phone if the recipient does not answer? This could be achieved with sequential forking (by trying contacts in decreasing q order), but sequential forking is not yet supported in SER. Is it possible to configure SER so that it only has the latest registration? FIXME. mkdir: cannot create directory `/usr/local/etc/ser': Permission denied make: *** [/usr/local/etc/ser] Error 1 what may be the error ? You have no write permissions to the directory, try it again as root. Can a proxy terminate a call ? No, proxy cannot do that because it is transaction stateful only. i want to know what accounting support is available using MySql. what settings need to be done? are any records generated and placed in the database? how do i access these records? FIXME. It seems that SER supports only strict routing. Please tell me if it supports loose routing. if yes, do i have to enable it somehow? Yes, it does support loose routing as of 0.8.11, you don't have to enable it. I need to know if currently generated call records can be put into MySql database. if yes, is there any tool available to view these records? Serweb can do it. If I want to use SER commercially do I need to purchase any license for the same, as long as I'm not going to charge the customer for the SIP service but only for the termination of calls. SER is distributed under the GPL so you don't need to purchase any license, you can use it freely. Is there a support for accounting ? Yes, see acc module. What tool can I use to capture SIP traffic ? ngrep, ethereal, tcpdump. Is there ENUM support in SER ? Yes, see enum module. Do you have any experience (or know) where I can get some info on setting up an ENUM testbed with DNS and Linux? if by testbed you mean running your own e164.arpa root, then you simply make your dns server a root for e164.arpa domain by adding line primary e164.arpa e164.arpa.db into your named.boot file and then by populating file e164.arpa.db with your enum entries. Below is an example. $ORIGIN . $TTL 0 ; 0 seconds e164.arpa IN SOA foo.fi. hostmaster.foo.fi. ( 200204681 ; serial 28800 ; refresh (8 hours) 7200 ; retry (2 hours) 604800 ; expire (1 week) 86400 ; minimum (1 day) ) NS foo.fi. $ORIGIN e164.arpa. $ORIGIN 8.5.3.e164.arpa. $ORIGIN 9.3.8.1.5.6.2.8.5.3.e164.arpa. 3.1 NAPTR 1 1 "u" "E2U+sip" "!(^.*$)!sip:abc@bar.fi!i" . Will SER support STUN in the future? STUN is a protocol operated separately from SER Is there any way to show active calls (dialed number, duration, originating IP...) with SER? No, ser is not call stateful. If I'm working with RFC2543 compliant phones (such as ATA) may I use loose routing? Yes, loose routing is backwards compatible. How can I configure radiator for digest authentication? In case of radiator, you don't need to do anything special in the configuration. Just install the latest radiator and then make sure your config can handle the service-types you have configured ser to use. For example, you can have <Handler Service-Type=SIP> ... </AuthBy> Or whatever your strategy is. How can I configure radiator for digest authentication? see freeradius-0.8.1/doc/rlm_digest Trying to create alias, I am getting the following message: 400 Table 'aliases' Not Found You must have lookup(aliases) somewhere in your script. Also how much shared memory does SER allocate by default? 32 Mb. How can I identify what ser doesn't like about the config file? 0(612) parse error (81,1-10): syntax error 0(612) parse error (91,1-6): ^^^^^^ These are the line number and the characters. So look in line 81 and 91 of your config file for errors. Warning: 392 216.87.144.203:5060 "Noisy feedback tells: pid=19604 req_src_ip=216.87.144.205 req_src_port=5065 in_uri=sip:addaline.com out_uri=sip:addaline.com via_cnt==1". I want to get rid of these? Use sip_warning=no Is SER a gatekeeper ? No, SER is a SIP proxy. Is there an example of the session timer with SER somewhere? That's a theoretical SIP option today, SER is not supporting session-timer. We gave it a try more than one year ago and gave up due to interop problems. The specification was developing at that time so quickly that there was not any UA which would work with another one correctly. Once the interoperability gets better, it could be worth implementing. It is in general a nice mechanism for avoiding session state silo in network, which is good for scalability. Could someone point me where I can read about this www_authorize() The documentation is in sip_router/modules/auth_db/doc The function tries to verify user's credentials. It returns 1 if they are correct and -1 if not. I'm new to SER and would like to set up a test lab with a couple different ip phones / adapters to learn from. Could someone recommend a few devices that we could use for that purpose? Hardphones: Grandstream, Cisco, ATA, Mitel, Pingtel, Snom Softphones: kphone, Xten, Windows Messenger, Hotsip client. how can i configure my ser for multiple domains? For example andrea@foo.bar and andrea@foo2.bar on the same server? If you are using 0.8.10, it is a manual process--copy and paste SQL tables, have a table set for each served domain, and refer to the table names from your scripts. E.g., if (uri=~"domain1.com") { lookup("domain1"); ... With 0.8.11 you can automate the process quite a lot. Authorization functions with realm set to empty value take domain name from SIP requests. User location database keeps track of domains as well. The "domain" modules allows you to keep track of maintained domains in a way which does not take changing scripts. It is possible there are some magic options in domain/usrloc/auth_db/ registrar/auth modules you need to turn on to enable multidomain operation--I don't remember these by heart, hopefuly some people on the mailing list do. I am testing SER version 0.8.11pre29 with two MSN Messenger(v4.6) clients. I tried to send the following MESSAGE through SER with record-route header added by SER. However, the receiving MSN client responded with a 400 Bad Request for that message. It's MSN Messenger's bug--lr parameter is not recognized as specified in RFC3261. Use modparam("rr", "enable_full_lr", 1) in ser config file to make it working. I have got an H.323 Mediatrix FXO gateway. It is not SIP based. May I use it with SER ? No. It is possible to process the voicemail request on the same instance I use to forward/register users ? Yes. How can I checkout the sources from the CVS ? FIXME. Are there any binary packages available ? FIXME. What is symmetric signaling ? FIXME. What is SIP, SIMPLE, SDP, Proxy, Registrar, Redirect server ? FIXME. SER starts normally with the default ser.cfg. When I uncommented the line load module "/usr/local/lib/ser/modules/mysql.so" to load mysql.so, everything seems normally. But actually, ser stops abnormally. mysql module is excluded from compilation/installation by default (dependencies issue). You can compile it separately with: make modules modules=modules/mysql. Also, you can add it in the list of the default compiled modules with: make all include_modules="mysql". Then use: make install include_modules="mysql". To install only the modules: make install-modules include_modules="mysql" or only the mysql module: make install-modules modules=modules/mysql. Is ser compatible with MySQL 4.x ? Yes. Is the SIP Express Router (SER) free ? Or do we need to pay ? Because my boss worry about that, please help me. Yes, it is free. See http://cvs.berlios.de/cgi-bin/viewcvs.cgi/*checkout*/ser/sip_router/COPYING?rev=HEAD&content-type=text/plain Mar 10 16:46:33 ttalksvr /usr/sbin/ser[6592]: connect_db(): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2) Make sure your MySQL server is running. I get the following error: 0(5164) db_init(): Error while trying to connect database 0(5164) mod_init(): Error while connecting database 0(5164) init_modules(): Error while initializing module usrloc Modules using database have variable called db_url which contains username, password, hostname, and name of the database. Make sure the settings are correct. See module documentation for more information. Does MySQL/Postgres/Whatever have to reside on localhost? No. Why do I need a database ? Many modules need the database. Authentication modules retrieve users' credentials from the database, user location module can be configured to store registered bindings into the database, accounting module can use database to store CDR and so on. Which database engines are supported ? Currently we support MySQL, Postgres and plaintext files. Which database engine should I use ? That depends on your requirements. Performance aspects are probably not very important. The most stable and proven is MySQL module. Also serctl utility works well with this database. Another option is postgres support which was donated by Greg Fausak. The module still new and not that mature (read proven) yet. People using the module reported that it worked well. Last option is dbtext module which uses plaintext files to store the data. This module is somewhat experimental and shouldn't be used for any serious deployment. The module is quite slow, but it can fit well into small installations containing only a couple of users or demos where you need to change the content of the database often by hand. Is there send IM page for sending instant messages only or does it accept replies as well (i.e. does it act like a full UA) ? It can send messages only. I have serweb running. I'm able to connect to MySQL fine. But it doesn't seem like the form params are getting assigned to variables. Is there something obvious I'm missing? Please check if register_globals is set to On in your php.ini file. The option is set off by default due to security reasons. Can somebody tell me how to configure the serweb? Which directory should I put those files and which file and parameters should I modify. Or maybe this question was asked previously by somebody already, then please provide me a like to those answers. Dan Austin's HOWTO (see SER webpage) is probably the currently most extensive documentation available. Trying to load the serweb page I get the following error message: Fatal error: Call to undefined function: mysql_pconnect() in /var/www/html/phplib/db_mysql.inc on line 73 Configure PHP to load the MySQL extension. I can open the page and even make changes and save them, but I'm receiving the following warning message: Warning: fopen("/tmp/ser_fifo", "w") ? Permission denied in /var/www/html/serweb/functions.php on line 172 Sorry - cannot open fifo. Make sure that the user under which the web server is running has read and write access to the FIFO. Is there a way to add/edit/view aliases from serweb? When new user confirm registration, new numeric alias is created. There is no another way to add or edit aliases from serweb. I am getting a "Forbidden" reply from the Apache. The Apache is pointing to the index.php for the serweb. I've only changed the IP address of the local host to 127.0.0.1 in the config.php. Does anyone have any suggestions ? Check if the file/folder has read permission for everybody. I get following warnings and error on NetBSD when I call user_interface/index.php: [Wed Jul 16 09:56:53 2003] [error] PHP Warning: Call-time pass-by-reference has been deprecated - argument passed by value; If you would like to pass it by reference, modify the declaration of [runtime function name](). If you would like to enable call-time pass-by-reference, you can set allow_call_time_pass_reference to true in your INI file. However, future versions may not support this any longer. in /usr/pkg/share/httpd/htdocs/iptel/phplib/user.inc on line 72 [Wed Jul 16 09:56:53 2003] [error] PHP Fatal error: Call to undefined function: mysql_pconnect() in /usr/pkg/share/httpd/htdocs/iptel/phplib/db_mysql.inc on line 73 Simply do what the text says: enable allow_call_time_pass_reference in /usr/pkg/etc/php.ini and install PHP MySQL support. I started the webserver and got the following message: Starting up of httpd: [Thu Aug 14 15:16:51 2003] alert ] httpd: Could not determine the of server fully qualified domain name, using 127.0.0.1 for ServerName Does anyone know where is the problem ? Set ServerName variable in the configuration file of your webserver to hostname of your computer. What version of ser should I use with serweb from the CVS ? CVS version of serweb is aligned to the stable branch of ser. It will not work with ser 0.8.10 because the database tables have been changed recently.
kamailio-4.0.4/doc/serfaq/Makefile0000644000000000000000000000011712223032460015462 0ustar rootrootdocs = serfaq.xml docbook_dir = ../../docbook include $(docbook_dir)/Makefile kamailio-4.0.4/doc/rpc/0000755000000000000000000000000012223032457013334 5ustar rootrootkamailio-4.0.4/doc/rpc/ser_rpc.txt0000644000000000000000000006262612223032457015546 0ustar rootroot1. RPC Control Interface __________________________________________________________________ 1.1. Overview of Operation 1.2. Module API 1.2.1. RPC Functions 1.2.2. Data Types 1.2.3. Getting Parameters 1.2.3.1. scan 1.2.3.2. struct_scan 1.2.3.3. Retrieving Parameters Example 1.2.4. Building Reply 1.2.4.1. fault 1.2.4.2. send 1.2.4.3. add 1.2.4.4. printf 1.2.4.5. struct_add 1.2.5. Real World Example 1.3. Client Examples 1.4. Implementing New Transports 1.5. Examples using xmlrpc 1.1. Overview of Operation The RPC (Remote Procedure Call) interface is an interface for communicating with external applications. Using it an external application can call a function or procedure that will be executed inside SIP Server (SER or Kamailio). Function parameters are supported as well as returning multiple values as results. By itself RPC consists of two APIs, one for defining RPC functions in a transport independent way (called the rpc module api) and one for implementing RPC transports. The RPC transports are implemented by writting a RPC transport module. The most used transport modules are ctl and xmlrpc . The first one implements a ser-proprietary fast and space efficient RPC encoding over different protocols (unix sockets, UDP, TCP, fifo). The second one uses the de-facto XML-RPC standard encoding (over HTTP TCP or TLS). For more information about the existing transport modules, please refer to their documentation. When writing a RPC procedure or function, one needs only use the RPC API and it will work automatically with all the transports and encodings. One needs only to load the desired RPC transport module (e.g. xmlrpc). The RPC interface (or API) was created in such a way that would allow supporting XML-RPC (because XML-RPC is a de-facto standard), while in the same time being very easy to use. 1.2. Module API Each module can export RPC functions just like it can export parameters and functions to be called from the script. Whenever SIP server receives an RPC request, it will search through the list of exported RPC functions and the function with matching name will be executed. A couple of essential RPC functions are also embedded into the SIP server core. This section gives a detailed overview of the whole RPC API. Section 1.2.1, "RPC Functions" describes the prototype and conventions used in RPC functions. Section 1.2.2, "Data Types" gives a detailed overview of available data types that can be used in function parameters and return value. Section 1.2.3, "Getting Parameters" describes functions of the RPC API that can be used to retrieve parameters of the function, and finally Section 1.2.4, "Building Reply" describes functions of the API that can be used to build the result value that will be sent in the reply to the caller. The whole RPC API is described in header file sip_router/rpc.h. This file defines the set of functions that must be implemented by RPC transport modules, as described in Section 1.4, "Implementing New Transports", prototypes of RPC functions and structures used for the communication between RPC transport modules and ordinary modules exporting RPC functions. 1.2.1. RPC Functions RPC functions are standard C functions with the following prototype: typedef void (*rpc_function_t)(rpc_t* rpc, void* ctx); RPC functions take two parameters, first parameter is a pointer to rpc_t structure and the context. The rpc_t structure contains references to all API functions available to the RPC function as well as all data necessary to create the response. RPC functions do not return any value, instead the return value is created using functions from the context. The motivation for this decision is the fact that RPC functions should always return a response and even the API functions called from RPC functions should have the possibility to indicate an error (and should not rely on RPC functions doing so). If no reply is sent explicitely, the RPC transport module will automatically send a "success" reply (e.g. 200 OK for XML-RPC) when the RPC function finishes. If no values are added to the response, the reponse will be an empty "success" reply (e.g. a 200 OK with empty body for XML-RPC). RPC API functions will automatically send an error reply upon a failure. Each RPC function has associated an array of documentation strings. The purpose of the documentation strings is to give a short overview of the function, accepted parameters, and format of the reply. By convention the name of the documentation string array is same as the name of the function with "_doc" suffix. Each module containing RPC functions has to export all the RPC functions to SIP server core in order to make them visible to the RPC transport modules. The export process involves a rpc_export_t structure (either by itself or in an array): typedef struct rpc_export { const char* name; /* Name of the RPC function (null terminated) */ rpc_function_t function; /* Pointer to the function */ const char** doc_str; /* Documentation strings, method signature and desc ription */ unsigned int flags; /* Various flags, reserved for future use */ } rpc_export_t; The flags attribute of the rpc_export structure is reserved for future use and is currently unused. There are several ways of exporting the RPC functions to the SIP server core: * register a null terminated array of rpc_export_t structures using the rpc_register_array() function (defined in rpc_lookup.h), from the module init function (mod_init()). This is the recommended method for all the new modules. Example 1. usrloc RPC Exports Declaration The rpc_export_t array for the modules_s/usrloc module looks like: rpc_export_t ul_rpc[] = { {"usrloc.statistics", rpc_stats, rpc_stats_doc, 0}, {"usrloc.delete_aor", rpc_delete_aor, rpc_delete_aor_doc, 0}, {"usrloc.delete_contact", rpc_delete_contact, rpc_delete_contact_doc, 0}, {"usrloc.dump", rpc_dump, rpc_dump_doc, 0}, {"usrloc.flush", rpc_flush, rpc_flush_doc, 0}, {"usrloc.add_contact", rpc_add_contact, rpc_add_contact_doc, 0}, {"usrloc.show_contacts", rpc_show_contacts, rpc_show_contacts_doc, 0}, {0, 0, 0, 0} }; To register it from the module init function one would use something similar to: if (rpc_register_array(ul_rpc) != 0) { ERR("failed to register RPC commands\n"); return -1; } * register RPCs one by one using the rpc_register_function() (defined in rpc_lookup.h), from the module init function. * register a null terminated array of rpc_export_t structures using the SIP server module interface SER_MOD_INTERFACE (specific for SER flavour). For this purpose, the module_exports structure of SIP server module API contains a new attribute called rpc_methods: struct module_exports { char* name; /* null terminated module name */ cmd_export_t* cmds; /* null terminated array of the exported command s */ rpc_export_t* rpc_methods; /* null terminated array of exported rpc methods */ param_export_t* params; /* null terminated array of the exported module parameters */ init_function init_f; /* Initialization function */ response_function response_f; /* function used for responses */ destroy_function destroy_f; /* function called upon shutdown */ onbreak_function onbreak_f; child_init_function init_child_f; /* function called by all processes after the fork */ }; rpc_methods is a pointer to an array of rpc_export_t structures. The last element of the array is a bumper containing zeroes in all the attributes of the structure. The following program listing shows the exported RPC functions of the modules_s/usrloc module, using the rpc_export_t array ul_rpc defined above, in the rpc_register_array() example: Example 2. usrloc Module Exports Declaration struct module_exports exports = { "usrloc", cmds, /* Exported functions */ ul_rpc, /* RPC methods */ params, /* Export parameters */ mod_init, /* Module initialization function */ 0, /* Response function */ destroy, /* Destroy function */ 0, /* OnCancel function */ child_init /* Child initialization function */ }; Note This mode works only with modules using the SER flavour module interface. It does not work for kamailio modules and it will probably not work for future sip-router modules. It is safer and recommended to use instead the rpc_register_array() function. By convention the name of every exported function consists of two parts delimited by a dot. The first part is the name of the module or SIP server subsystem this function belongs to. The second part is the name of the function. 1.2.2. Data Types The RPC API defines several basic and 1 compound data type that can be used in communication with the caller of RPC functions. The RPC API uses formating strings to describe data types. Each data type is described by exactly one character in the formating string. For example, if an RPC function calls function add of the RPC API and it passes two parameters to it, the first one of type string and the second one of type integer, the function parameters will look like: add("sd", string_param, int_param); Character "s" in the formating string tells to the function that the 2nd parameter should be interpreted as string, character "d" in the formating string tells to the function that the 3rd parameter should be interpreted as signed integer. Integer. Integer type represents a signed 32-bit integer. Corresponding character in the formating string is "d". This parameter can be stored in C-style variable with type int. Float. Float type represents a signed floating point number. Corresponding character in the formating string is "f". Data of this type can be stored in C-style variables of type double. String. String type represents a string of characters. The string may contain zeroes. This data type is represented by two characters in the formatting string, either "s" or "S". "s" indicates to the conversion function that the result should be stored in a variable of type char* and it should be zero terminated. "S" indicates to the conversion function that the result will be stored in a variable of type str which contains both the pointer to the beginning of the string and its length. Structure. Structure is the only compound data type currently defined in the API. A structure is a collection of attributes. Each attribute is identified using name (string) and each attribute can be one of the basic data types, that is integer, float, or string. Nesting of structures is not allowed (in other words, structure attributes cannot be of type struct again). Corresponding character in the formatting string is "{". Optional parameters. Optional parameters can be used, but only in the scan function. For optional parameters the scan function will not automatically generate a rpc fault if the input ends. Note that in this case the scan will still return a negative value (minus the number of parameters successfully read). Optional parameters can be marked in the format string by preceding the first optional parameter type with a "*". All the parameters following a "*" are considered to be optional. For example for the format string "ds*dds", the last 3 parameters (2 ints and a string) are optional. Table 1. Data Type Overview Name Formating String Char C-Style Variable Integer d int Float f double String s char* String S str Optional modifier * marks all further parameters as optional Autoconvert modifier . requires auto-conversion for the next parameter 1.2.3. Getting Parameters Each RPC function call can contain parameters. Parameters have no name, their meaning is determined by their position in the parameter set. Note You can pass all parameters to a function within a structure if you want to make them position independent. Then each parameter can be retrieved by its name regardless of its position. There are two functions in the RPC API that can be used to obtain function call parameters: scan and struct_scan. 1.2.3.1. scan Function scan can be used to retrieve parameters from the parameter set. The function accepts variable number of parameters. The first parameter is the formatting string that determines the type of the parameters to be retrieved. Each parameter is represented by exactly one parameter type character in the string. The variable part of parameters must contain as many pointers to C variables as there are formatting non-modifiers characters in the formatting string. Warning The function will crash if you fail to provide enough parameters. Besides characters representing parameter types, the formatting string can contain two special modifiers: "*" and ".". The modifiers do not have a correspondent in the variable part of the parameters. The meaning of "*" modifier is that any further parameters (defined by other type characters in the formatting string) are optional (they can be missing in the input and no rpc fault will automatically be generated). The '.' modifiers turns on type autoconversion for the next parameter. This means that if the type of the next parameter differs from the type specified in the formatting string, the parameter will be automatically converted to the formatting string type (if possible) and if the automatic conversion succeeds, no fault will be generated. The function returns the number of parameters read on success (a number greater or equal 0) and - (minus) the number of parameters read on error (for example for an error after reading 2 parameters it will return -2). When a failure occurs (incorrect parameter type or no more parameters in the parameter set) the function will return a negative number (- number of parameters read so far) and it will also automatically change the reply that will be sent to the caller to indicate that a failure has occurred on the server (unless the "*" is used and the error is lack of more parameters). The prototype of the function is: int scan((void* ctx, char* fmt, ...) It is possible to either call the function once to scan all the parameters: rpc->scan(ctx, "sdf", &string_val, &int_val, &double_val); Or you can call the same function several times and it will continue where it left off previously: rpc->scan(ctx, "s", &string_val); rpc->scan(ctx, "d", &int_val); rpc->scan(ctx, "f", &double_val); 1.2.3.2. struct_scan Function struct_scan can be used to retrieve named attributes from a parameter of type structure. Note This function is obsolete and not implemented by all the rpc transports (e.g.: ctl / binrpc). Consider using the normal scan instead. When retrieving a structure parameter from the parameter set: rpc->scan(ctx, "{", &handle); The corresponding variable (named handle in the example above) will contain the index of the structure parameter within the parameter set, but the index cannot be used to retrieve the contents of the structure. To retrieve the contents of the structure you can use function struct_scan. The function gets the handle as the first parameter: rpc->struct_scan(handle, "sd", "str_attr", &str_val, "int_attr", &int_val); The second parameter is the formatting string followed by pairs of parameters. First parameter in each pair is the name of the attribute to retrieve (string) and the second parameter in each pair is the pointer to the variable to store the value of the parameter. The function returns the number of parameters (name value pairs) read on success and - number of parameters read so far on an error (just like the scan function). The function also indicates an error if a requested attribute is missing in the structure. 1.2.3.3. Retrieving Parameters Example Example 3. Retrieving Parameters static void rpc_delete_contact(rpc_t* rpc, void* ctx) { str aor, contact; char* table; void *handle; int expires; double q; if (rpc->scan(ctx, "sS{", &table, &aor, &handle) < 0) { /* Reply is set automatically by scan upon failure, * no need to do anything here */ return; } if (rpc->struct_scan(handle, "Sdf", "Contact", &contact, "Expires", &expires, "Q", &q ) < 0) { /* Reply is set automatically by struct_scan upon failure, * no need to do anything here */ return; } /* Process retrieved parameters here */ } /* variable number of parameters: echo back all the parameters, string type required */ static void core_prints(rpc_t* rpc, void* c) { char* string = 0; while((rpc->scan(c, "*s", &string)>0)) rpc->add(c, "s", string); } /* variable number of parameters and auto conversion: echo back all the parameters, works with any type (everything is internally converted to string, notice the '.' modifier) */ static void core_echo(rpc_t* rpc, void* c) { char* string = 0; while((rpc->scan(c, "*.s", &string)>0)) rpc->add(c, "s", string); } 1.2.4. Building Reply The RPC API contains several functions that can be used to modify and/or send a reply. The functions use formatting strings and parameter lists just like functions described in Section 1.2.3, "Getting Parameters". Each RPC function call must return a reply. The reply can be either a failure reply or success reply. Failure replies contain only the status code and reason phrase. Success replies can have arbitrary amount of data attached to them. Status codes 3xx, 4xx, 5xx, and 6xx indicate failures. Status code 2xx indicates success. The default reply is 200 OK with no data attached to it. This is what will be returned by the RPC transport module if you do not call any of the reply-related functions described in this section. Example 4. Sending default reply static void rpc_dummy(rpc_t* rpc, void *ctx) { /* 200 OK with no data will be returned */ } 1.2.4.1. fault You can use fault function to indicate that an error has occurred on the server to the caller. The function accepts two parameters. The first parameter is the status code and the second parameter is the reason phrase. static void rpc_my_function(rpc_t* rpc, void *ctx) { rpc->fault(ctx, 600, "Not Yet Implemented"); } If your function first creates some result using add, or printf functions then all the data will be lost once you call fault function. Failure replies must not contain any data: static void rpc_my_function(rpc_t* rpc, void *ctx) { rpc->add(ctx, "s", "result1"); rpc->add(ctx, "d", variable); /* Reply created by previous functions will be * deleted and a failure reply 600 Not Yet Implemented * will be created instead */ rpc->fault(ctx, 600, "Not Yet Implemented"); /* You can also add data here, but that will have no * effect */ rpc->add(ctx, "s", "result2"); } Similarly you can also call add or printf functions after calling fault, in this case they will have no effect: static void rpc_my_function(rpc_t* rpc, void *ctx) { rpc->fault(ctx, 600, "Not Yet Implemented"); /* You can also add data here, but that will have no * effect and only 600 Not Yet Implemented will be returned */ rpc->add(ctx, "s", "result2"); } 1.2.4.2. send RPC functions can use function send to explicitly send the reply. Each RPC function call generates exactly one reply. No reply will be sent after the function finishes if it already sent the reply using send function explicitly. This function is especially useful if the RPC function needs to perform some (potentially destructive) actions after the reply has been sent. Example 5. Kill the server static void core_kill(rpc_t* rpc, void *ctx) { int sig_no; if (rpc->scan(ctx, "d", &sig_no) < 0) return; rpc->send(ctx, ); /* First send a reply */ kill(0, sig_no); /* Then kill the server */ } 1.2.4.3. add Function add can be used to add arbitrary data to the result set. Its parameters and use are analogical to scan function described in Section 1.2.3.1, "scan". The first parameter of the function is the formatting string that determines the types of additional parameters: static void rpc_func(rpc_t* rpc, void *ctx) { str str_result; int int_result; void *handle; double float_result; if (rpc->add(ctx, "Sdf{", &str_result, int_result, float_result, &handle) < 0) return; } Naturally you can call this function several times, adding only one piece of data at a time. The function returns 0 on success and -1 on an error. In case of an error the reply is set automatically with corresponding error code and reason phrase. The last character in the formatting string of the function above indicates that the last data to be added will be a structure. This deserves some clarification. In this case, the function will create an empty structure and the handle to the newly created structure will be stored in handle variable (hence the last parameter is pointer to an integer). In this particular example parameters str_result, int_result, and float_result will be used for reading while parameter handle will be used for writing by the function. You can set the attributes of the newly created structure using struct_add function described in Section 1.2.4.5, "struct_add". 1.2.4.4. printf printf is a convenience function. The function adds data of type string to the result set. The first parameter of the function is again a formatting string, but this time it is printf-like formatting string: if (rpc->printf(ctx, "Unable to delete %d entries from table %s", num_entries, t able_name) < 0) return; The return value of the function is the same as of add function. 1.2.4.5. struct_add Function struct_add can be used to add attributes to a structure (created previously by add function). The first parameter of the function is handle obtained through add function, the second parameters is formatting string that determines the types of attributes to be added. There must be two parameters per each character in the formatting string, the first one is the name of the attribute, the second parameter is the value of the attribute. If a parameter with such a name already exist in the structure then it will be overwritten with the new value. static void rpc_func(rpc_t* rpc, void *ctx) { void *handle; /* Create empty structure and obtain its handle */ if (rpc->add(ctx, "{", &handle) < 0) return; /* Fill-in the structure */ if (rpc->struct_add(handle, "sd", "attr1", str_val, "attr2", int_val ) < 0) return; } The function returns -1 on an error (and sets the status code and reason phrase of the reply accordingly) and 0 on success. 1.2.5. Real World Example The following example illustrates the use of most of the functions from the API together: Example 6. Real World Example RPC Function static void rpc_register(rpc_t* rpc, void *ctx) { char* domain; str aor; contact_t contact, new_contact; void *handle; /* Extract the domain, address of record from the request */ if (rpc->scan(ctx, "sS{", &domain, &aor, &handle) < 0) return; /* Extract the structure describing the contact to be processed */ if (rpc->struct_scan(handle, "Sdf", "Contact", &contact.c, "Expires", &contact.expires, "Q", &contact.q ) < 0) return; /* Process the contact, new_contact will contain updated value after pro cessing */ if (process_contact(domain, &aor, &new_contact, &contact) < 0) { /* Processing failed, indicate the failure to the caller */ rpc->fault(ctx, 500, "Error While Processing Contact"); return; } /* Return the domain and the address of record */ rpc->add(ctx, "sS{", &domain, &aor, &handle) < 0) return; /* And also add the new values for contact, q, and expires parameters */ rpc->struct_add(handle, "Sdf", "Contact", &new_contact.c, "Expires", &new_contact.expires, "Q", &new_contact.q ); } 1.3. Client Examples * sercmd (C application that uses the binrpc interface implemented by the ctl module). * ser_ctl (python application that uses the XML-RPC interface implemented by the xmlrpc module). * serweb (php application that can use the XML-RPC interface to call ser functions). 1.4. Implementing New Transports To be done. Examples: * ctl * xmlrpc 1.5. Examples using xmlrpc See the xmlrpc module documentation: modules/xmlrpc/README. kamailio-4.0.4/doc/rpc/ser_rpc.xml0000644000000000000000000010064312223032457015517 0ustar rootroot ] >
RPC Control Interface
Overview of Operation The RPC (Remote Procedure Call) interface is an interface for communicating with external applications. Using it an external application can call a function or procedure that will be executed inside SIP Server (SER or Kamailio). Function parameters are supported as well as returning multiple values as results. By itself RPC consists of two APIs, one for defining RPC functions in a transport independent way (called the rpc module api) and one for implementing RPC transports. The RPC transports are implemented by writting a RPC transport module. The most used transport modules are ctl and xmlrpc . The first one implements a ser-proprietary fast and space efficient RPC encoding over different protocols (unix sockets, UDP, TCP, fifo). The second one uses the de-facto XML-RPC standard encoding (over HTTP TCP or TLS). For more information about the existing transport modules, please refer to their documentation. When writing a RPC procedure or function, one needs only use the RPC API and it will work automatically with all the transports and encodings. One needs only to load the desired RPC transport module (e.g. xmlrpc). The RPC interface (or API) was created in such a way that would allow supporting XML-RPC (because XML-RPC is a de-facto standard), while in the same time being very easy to use.
Module API Each module can export RPC functions just like it can export parameters and functions to be called from the script. Whenever SIP server receives an RPC request, it will search through the list of exported RPC functions and the function with matching name will be executed. A couple of essential RPC functions are also embedded into the SIP server core. This section gives a detailed overview of the whole RPC API. describes the prototype and conventions used in RPC functions. gives a detailed overview of available data types that can be used in function parameters and return value. describes functions of the RPC API that can be used to retrieve parameters of the function, and finally describes functions of the API that can be used to build the result value that will be sent in the reply to the caller. The whole RPC API is described in header file sip_router/rpc.h. This file defines the set of functions that must be implemented by RPC transport modules, as described in , prototypes of RPC functions and structures used for the communication between RPC transport modules and ordinary modules exporting RPC functions.
RPC Functions RPC functions are standard C functions with the following prototype: typedef void (*rpc_function_t)(rpc_t* rpc, void* ctx); RPC functions take two parameters, first parameter is a pointer to rpc_t structure and the context. The rpc_t structure contains references to all API functions available to the RPC function as well as all data necessary to create the response. RPC functions do not return any value, instead the return value is created using functions from the context. The motivation for this decision is the fact that RPC functions should always return a response and even the API functions called from RPC functions should have the possibility to indicate an error (and should not rely on RPC functions doing so). If no reply is sent explicitely, the RPC transport module will automatically send a "success" reply (e.g. 200 OK for XML-RPC) when the RPC function finishes. If no values are added to the response, the reponse will be an empty "success" reply (e.g. a 200 OK with empty body for XML-RPC). RPC API functions will automatically send an error reply upon a failure. Each RPC function has associated an array of documentation strings. The purpose of the documentation strings is to give a short overview of the function, accepted parameters, and format of the reply. By convention the name of the documentation string array is same as the name of the function with "_doc" suffix. Each module containing RPC functions has to export all the RPC functions to SIP server core in order to make them visible to the RPC transport modules. The export process involves a rpc_export_t structure (either by itself or in an array): typedef struct rpc_export { const char* name; /* Name of the RPC function (null terminated) */ rpc_function_t function; /* Pointer to the function */ const char** doc_str; /* Documentation strings, method signature and description */ unsigned int flags; /* Various flags, reserved for future use */ } rpc_export_t; The flags attribute of the rpc_export structure is reserved for future use and is currently unused. There are several ways of exporting the RPC functions to the SIP server core: register a null terminated array of rpc_export_t structures using the rpc_register_array() function (defined in rpc_lookup.h), from the module init function (mod_init()). This is the recommended method for all the new modules. usrloc RPC Exports Declaration The rpc_export_t array for the modules_s/usrloc module looks like: rpc_export_t ul_rpc[] = { {"usrloc.statistics", rpc_stats, rpc_stats_doc, 0}, {"usrloc.delete_aor", rpc_delete_aor, rpc_delete_aor_doc, 0}, {"usrloc.delete_contact", rpc_delete_contact, rpc_delete_contact_doc, 0}, {"usrloc.dump", rpc_dump, rpc_dump_doc, 0}, {"usrloc.flush", rpc_flush, rpc_flush_doc, 0}, {"usrloc.add_contact", rpc_add_contact, rpc_add_contact_doc, 0}, {"usrloc.show_contacts", rpc_show_contacts, rpc_show_contacts_doc, 0}, {0, 0, 0, 0} }; To register it from the module init function one would use something similar to: if (rpc_register_array(ul_rpc) != 0) { ERR("failed to register RPC commands\n"); return -1; } register RPCs one by one using the rpc_register_function() (defined in rpc_lookup.h), from the module init function. register a null terminated array of rpc_export_t structures using the SIP server module interface SER_MOD_INTERFACE (specific for SER flavour). For this purpose, the module_exports structure of SIP server module API contains a new attribute called rpc_methods: struct module_exports { char* name; /* null terminated module name */ cmd_export_t* cmds; /* null terminated array of the exported commands */ rpc_export_t* rpc_methods; /* null terminated array of exported rpc methods */ param_export_t* params; /* null terminated array of the exported module parameters */ init_function init_f; /* Initialization function */ response_function response_f; /* function used for responses */ destroy_function destroy_f; /* function called upon shutdown */ onbreak_function onbreak_f; child_init_function init_child_f; /* function called by all processes after the fork */ }; rpc_methods is a pointer to an array of rpc_export_t structures. The last element of the array is a bumper containing zeroes in all the attributes of the structure. The following program listing shows the exported RPC functions of the modules_s/usrloc module, using the rpc_export_t array ul_rpc defined above, in the rpc_register_array() example: usrloc Module Exports Declaration struct module_exports exports = { "usrloc", cmds, /* Exported functions */ ul_rpc, /* RPC methods */ params, /* Export parameters */ mod_init, /* Module initialization function */ 0, /* Response function */ destroy, /* Destroy function */ 0, /* OnCancel function */ child_init /* Child initialization function */ }; This mode works only with modules using the SER flavour module interface. It does not work for kamailio modules and it will probably not work for future sip-router modules. It is safer and recommended to use instead the rpc_register_array() function. By convention the name of every exported function consists of two parts delimited by a dot. The first part is the name of the module or SIP server subsystem this function belongs to. The second part is the name of the function.
Data Types The RPC API defines several basic and 1 compound data type that can be used in communication with the caller of RPC functions. The RPC API uses formating strings to describe data types. Each data type is described by exactly one character in the formating string. For example, if an RPC function calls function add of the RPC API and it passes two parameters to it, the first one of type string and the second one of type integer, the function parameters will look like: add("sd", string_param, int_param); Character "s" in the formating string tells to the function that the 2nd parameter should be interpreted as string, character "d" in the formating string tells to the function that the 3rd parameter should be interpreted as signed integer. Integer Integer type represents a signed 32-bit integer. Corresponding character in the formating string is "d". This parameter can be stored in C-style variable with type int. Float Float type represents a signed floating point number. Corresponding character in the formating string is "f". Data of this type can be stored in C-style variables of type double. String String type represents a string of characters. The string may contain zeroes. This data type is represented by two characters in the formatting string, either "s" or "S". "s" indicates to the conversion function that the result should be stored in a variable of type char* and it should be zero terminated. "S" indicates to the conversion function that the result will be stored in a variable of type str which contains both the pointer to the beginning of the string and its length. Structure Structure is the only compound data type currently defined in the API. A structure is a collection of attributes. Each attribute is identified using name (string) and each attribute can be one of the basic data types, that is integer, float, or string. Nesting of structures is not allowed (in other words, structure attributes cannot be of type struct again). Corresponding character in the formatting string is "{". Optional parameters Optional parameters can be used, but only in the scan function. For optional parameters the scan function will not automatically generate a rpc fault if the input ends. Note that in this case the scan will still return a negative value (minus the number of parameters successfully read). Optional parameters can be marked in the format string by preceding the first optional parameter type with a "*". All the parameters following a "*" are considered to be optional. For example for the format string "ds*dds", the last 3 parameters (2 ints and a string) are optional. Data Type Overview Name Formating String Char C-Style Variable Integer d int Float f double String s char* String S str Optional modifier * marks all further parameters as optional Autoconvert modifier . requires auto-conversion for the next parameter
Getting Parameters Each RPC function call can contain parameters. Parameters have no name, their meaning is determined by their position in the parameter set. You can pass all parameters to a function within a structure if you want to make them position independent. Then each parameter can be retrieved by its name regardless of its position. There are two functions in the RPC API that can be used to obtain function call parameters: scan and struct_scan.
<function>scan</function> Function scan can be used to retrieve parameters from the parameter set. The function accepts variable number of parameters. The first parameter is the formatting string that determines the type of the parameters to be retrieved. Each parameter is represented by exactly one parameter type character in the string. The variable part of parameters must contain as many pointers to C variables as there are formatting non-modifiers characters in the formatting string. The function will crash if you fail to provide enough parameters. Besides characters representing parameter types, the formatting string can contain two special modifiers: "*" and ".". The modifiers do not have a correspondent in the variable part of the parameters. The meaning of "*" modifier is that any further parameters (defined by other type characters in the formatting string) are optional (they can be missing in the input and no rpc fault will automatically be generated). The '.' modifiers turns on type autoconversion for the next parameter. This means that if the type of the next parameter differs from the type specified in the formatting string, the parameter will be automatically converted to the formatting string type (if possible) and if the automatic conversion succeeds, no fault will be generated. The function returns the number of parameters read on success (a number greater or equal 0) and - (minus) the number of parameters read on error (for example for an error after reading 2 parameters it will return -2). When a failure occurs (incorrect parameter type or no more parameters in the parameter set) the function will return a negative number (- number of parameters read so far) and it will also automatically change the reply that will be sent to the caller to indicate that a failure has occurred on the server (unless the "*" is used and the error is lack of more parameters). The prototype of the function is: int scan((void* ctx, char* fmt, ...) It is possible to either call the function once to scan all the parameters: rpc->scan(ctx, "sdf", &string_val, &int_val, &double_val); Or you can call the same function several times and it will continue where it left off previously: rpc->scan(ctx, "s", &string_val); rpc->scan(ctx, "d", &int_val); rpc->scan(ctx, "f", &double_val);
<function>struct_scan</function> Function struct_scan can be used to retrieve named attributes from a parameter of type structure. This function is obsolete and not implemented by all the rpc transports (e.g.: ctl / binrpc). Consider using the normal scan instead. When retrieving a structure parameter from the parameter set: rpc->scan(ctx, "{", &handle); The corresponding variable (named handle in the example above) will contain the index of the structure parameter within the parameter set, but the index cannot be used to retrieve the contents of the structure. To retrieve the contents of the structure you can use function struct_scan. The function gets the handle as the first parameter: rpc->struct_scan(handle, "sd", "str_attr", &str_val, "int_attr", &int_val); The second parameter is the formatting string followed by pairs of parameters. First parameter in each pair is the name of the attribute to retrieve (string) and the second parameter in each pair is the pointer to the variable to store the value of the parameter. The function returns the number of parameters (name value pairs) read on success and - number of parameters read so far on an error (just like the scan function). The function also indicates an error if a requested attribute is missing in the structure.
Retrieving Parameters Example Retrieving Parameters scan(ctx, "sS{", &table, &aor, &handle) < 0) { /* Reply is set automatically by scan upon failure, * no need to do anything here */ return; } if (rpc->struct_scan(handle, "Sdf", "Contact", &contact, "Expires", &expires, "Q", &q ) < 0) { /* Reply is set automatically by struct_scan upon failure, * no need to do anything here */ return; } /* Process retrieved parameters here */ } /* variable number of parameters: echo back all the parameters, string type required */ static void core_prints(rpc_t* rpc, void* c) { char* string = 0; while((rpc->scan(c, "*s", &string)>0)) rpc->add(c, "s", string); } /* variable number of parameters and auto conversion: echo back all the parameters, works with any type (everything is internally converted to string, notice the '.' modifier) */ static void core_echo(rpc_t* rpc, void* c) { char* string = 0; while((rpc->scan(c, "*.s", &string)>0)) rpc->add(c, "s", string); } ]]>
Building Reply The RPC API contains several functions that can be used to modify and/or send a reply. The functions use formatting strings and parameter lists just like functions described in . Each RPC function call must return a reply. The reply can be either a failure reply or success reply. Failure replies contain only the status code and reason phrase. Success replies can have arbitrary amount of data attached to them. Status codes 3xx, 4xx, 5xx, and 6xx indicate failures. Status code 2xx indicates success. The default reply is 200 OK with no data attached to it. This is what will be returned by the RPC transport module if you do not call any of the reply-related functions described in this section. Sending default reply
fault You can use fault function to indicate that an error has occurred on the server to the caller. The function accepts two parameters. The first parameter is the status code and the second parameter is the reason phrase. fault(ctx, 600, "Not Yet Implemented"); } ]]> If your function first creates some result using add, or printf functions then all the data will be lost once you call fault function. Failure replies must not contain any data: add(ctx, "s", "result1"); rpc->add(ctx, "d", variable); /* Reply created by previous functions will be * deleted and a failure reply 600 Not Yet Implemented * will be created instead */ rpc->fault(ctx, 600, "Not Yet Implemented"); /* You can also add data here, but that will have no * effect */ rpc->add(ctx, "s", "result2"); } ]]> Similarly you can also call add or printf functions after calling fault, in this case they will have no effect: fault(ctx, 600, "Not Yet Implemented"); /* You can also add data here, but that will have no * effect and only 600 Not Yet Implemented will be returned */ rpc->add(ctx, "s", "result2"); } ]]>
send RPC functions can use function send to explicitly send the reply. Each RPC function call generates exactly one reply. No reply will be sent after the function finishes if it already sent the reply using send function explicitly. This function is especially useful if the RPC function needs to perform some (potentially destructive) actions after the reply has been sent. Kill the server scan(ctx, "d", &sig_no) < 0) return; rpc->send(ctx, ); /* First send a reply */ kill(0, sig_no); /* Then kill the server */ } ]]>
add Function add can be used to add arbitrary data to the result set. Its parameters and use are analogical to scan function described in . The first parameter of the function is the formatting string that determines the types of additional parameters: add(ctx, "Sdf{", &str_result, int_result, float_result, &handle) < 0) return; } ]]> Naturally you can call this function several times, adding only one piece of data at a time. The function returns 0 on success and -1 on an error. In case of an error the reply is set automatically with corresponding error code and reason phrase. The last character in the formatting string of the function above indicates that the last data to be added will be a structure. This deserves some clarification. In this case, the function will create an empty structure and the handle to the newly created structure will be stored in handle variable (hence the last parameter is pointer to an integer). In this particular example parameters str_result, int_result, and float_result will be used for reading while parameter handle will be used for writing by the function. You can set the attributes of the newly created structure using struct_add function described in .
printf printf is a convenience function. The function adds data of type string to the result set. The first parameter of the function is again a formatting string, but this time it is printf-like formatting string: printf(ctx, "Unable to delete %d entries from table %s", num_entries, table_name) < 0) return; ]]> The return value of the function is the same as of add function.
struct_add Function struct_add can be used to add attributes to a structure (created previously by add function). The first parameter of the function is handle obtained through add function, the second parameters is formatting string that determines the types of attributes to be added. There must be two parameters per each character in the formatting string, the first one is the name of the attribute, the second parameter is the value of the attribute. If a parameter with such a name already exist in the structure then it will be overwritten with the new value. add(ctx, "{", &handle) < 0) return; /* Fill-in the structure */ if (rpc->struct_add(handle, "sd", "attr1", str_val, "attr2", int_val ) < 0) return; } ]]> The function returns -1 on an error (and sets the status code and reason phrase of the reply accordingly) and 0 on success.
Real World Example The following example illustrates the use of most of the functions from the API together: Real World Example RPC Function scan(ctx, "sS{", &domain, &aor, &handle) < 0) return; /* Extract the structure describing the contact to be processed */ if (rpc->struct_scan(handle, "Sdf", "Contact", &contact.c, "Expires", &contact.expires, "Q", &contact.q ) < 0) return; /* Process the contact, new_contact will contain updated value after processing */ if (process_contact(domain, &aor, &new_contact, &contact) < 0) { /* Processing failed, indicate the failure to the caller */ rpc->fault(ctx, 500, "Error While Processing Contact"); return; } /* Return the domain and the address of record */ rpc->add(ctx, "sS{", &domain, &aor, &handle) < 0) return; /* And also add the new values for contact, q, and expires parameters */ rpc->struct_add(handle, "Sdf", "Contact", &new_contact.c, "Expires", &new_contact.expires, "Q", &new_contact.q ); } ]]>
Client Examples sercmd (C application that uses the binrpc interface implemented by the ctl module). ser_ctl (python application that uses the XML-RPC interface implemented by the xmlrpc module). serweb (php application that can use the XML-RPC interface to call ser functions).
Implementing New Transports To be done. Examples: ctl xmlrpc
Examples using xmlrpc See the xmlrpc module documentation: modules/xmlrpc/README.
kamailio-4.0.4/doc/rpc/Makefile0000644000000000000000000000012012223032457014765 0ustar rootrootdocs = ser_rpc.xml docbook_dir = ../../docbook include $(docbook_dir)/Makefile kamailio-4.0.4/doc/config_migration.txt0000644000000000000000000000611012223032457016625 0ustar rootroot# $Id$ # # History: # -------- # 2009-05-06 created by andrei ============================================================= = Config migration guide from ser or kamailio to sip-router = ============================================================= ser 2.1 config migration ======================== 1. Avps, selects and strings in if or other expressions The most important change is the different way in which avp and select are evaluated in boolean expressions (if ()). In ser "if ($v){}" or "if (@select){}" were true if the avp or select were "defined" and if their value was non-empty (!= ""). In sip-router this changed: the ifs will be true if the avp or select are non-empty and they evaluate to a non-zero number. The catch is that a non-numeric string evaluates to 0 (e.g. "abc" evaluates to 0, "123" evaluates to 123, "" to 0, "0" to 0). Something like "if($v)" should be used only if $v is supposed to have a numeric value. "if (@select)" should not be used in general (it's probably not what you want). The equivalent sip-router checks are: instead of if ($v) use if ($v != "") instead of if (!$v) use if ($v == "") or if (strempty($v)). instead of if (@select) use if (@select != "") instead of if (!@select) use if (@select == "") or if (strempty(@select)). If the test is for value existence, then if ($v) can be replaced with if (defined $v). E.g.: replace if (! $f.did) with if (strempty($f.did)) replace if (method=="INVITE" && !@to.tag) with if (method=="INVITE" && strempty(@to.tag)) replace if ($f.did && ! $t.did) with if ($f.did != "" && $t.did == "") replace if (@to.tag) with if (@to.tag != "") 2. Module path While in ser there was only a single directory holding the modules, now there are 3: modules (for common modules), modules_s (for ser modules) and modules_k (for kamailio modules). The easiest way to migrate a ser config is to add: loadpath "/usr/lib/ser/modules:/usr/lib/ser/modules_s" at the beginning (before any loadmodule command). This will set the module search path to first look into the common modules and if not found in the ser modules. Make sure that all the loadmodule commands do not include the path to the module or the .so extension (or else they won't make use of the loadpath). E.g.: replace loadmodule "/usr/lib/ser/modules/tm.so" with loadmodule "tm" 3. Module names Some of the modules changed their name (usually after being merged with the kamailio ones). The most common changes are mysql -> db_mysql and postgres -> db_postgres. E.g.: replace loadmodule "mysql" with loadmodule "db_mysql" 4. msg:len and max_len max_len was removed. All the comparisons of msg:len with max_len must be changed to use a number (e.g. 4096 or 16384) instead of max_len. Comparing with max_len didn't make sense anyway since max_len was the size of the internal receive buffer on UDP. You could never exceed it, unless you were using TCP configured in a non-standard way. E.g.: replace if (msg:len >= max_len) with if (msg:len >= 4096) kamailio config migration ========================= [TODO: probably most of the things from the ser section apply too] kamailio-4.0.4/doc/modules_init.txt0000644000000000000000000001400412223032457016003 0ustar rootroot#$Id$ # # # History #-------- # 2007-06-07 created by Andrei Pelinescu # SIP-router Module intialization description =========================================== This document is a very brief overview on what possibilities are for a module to run some initializations (or put in another way: What's safe and what's not safe to do in mod_init and mod_child_init). The interesting function are the mod_init (the init_f memeber of the module_exports structure) and the mod_child_init (init_child_f in module_exports) functions. mod_init -------- mod_init is called after parsing the config, loading all the modules and going into daemon mode. mod_init is called in the main process context, before changing the uid or gid (so if you want to do something requiring higher privileges this is the place to do it). This is not the right place to fork more SIP-router processes, but instead is the only place where one can register future forked processes (see register_procs()). mod_init is ideal for initializing per process variables, assuming that you don't need different values for each SIP-router child (in which case see mod_child_init below) and shared memory variables assuming that you don't need SIP-router's number of processes (which is not available at this point). mod_child_init --------------- mod_child_init is called for each forked SIP-router process with 2 exceptions: - it's called for the main process too and it's called twice - it's not called for SIP-router processes forked with PROC_NOCHLDINIT mod_child_init is always called after mod_init. It takes one parameter, the process rank. This parameter can be used to differentiate between normal new-forked-process calls and some special calls. There are two very special rank values: PROC_INIT (as of 2007-06-06) and PROC_MAIN: mod_child_init(PROC_INIT) is the first mod_child_init call made, and it's guaranteed to happen before any child process is forked. The process context is the "main" process (as for mod_init), but this time we have a little bit more information: we know the number of SIP-router processes. This is the right place to initialize things like shared arrays[get_max_procs()]. Note also that everything done here will be inherited in all the future children processes (since it's executed in the "main" process context before forking). mod_child_init(PROC_MAIN) is another call that is done in the same "main" process context. This call happens just before initializing the main tcp process. This is the only right place for forking more SIP-router processes (see fork_process()). WARNING: the context is the same "main" process as for mod_child_init(PROC_INIT) and mod_init() so care must be taken not to initialize things twice or three times for the "main" process. Except for the "main" process case mod_child_init(rank) will be called only once for each new child process, just after forking. A positive non-zero rank means the current process is a normal SIP-router process and a negative rank has some special meaning (grep PROC_ sr_module.h for more info). mod_child_init(rank) is the best place for initializing per process variables, opening per process database connections, new sockets a.s.o. Note however that several mod_child_init()s can execute in the same time (with the PROC_INIT exceptions) so this is not a good place for initializing shared memory variables. mod_child_init in the no-fork case ---------------------------------- If SIP-router is started in no-fork mode it will try to start as few processes as possible (as of this date it will start 3 processes the main process and the 2 timers). In this case mod_child_init() will be called 3 times in the same "main" process context: mod_child_init(PROC_INIT); mod_child_init(PROC_MAIN) and mod_child_init(1) (since the first process is also the "main" one). Forking new SIP-router processes from a module --------------------------------------- static int mod_init() { register_procs(2); /* we want to create 2 extra processes */ return 0; } static int mod_child(int rank) { int pid; if (rank==PROC_MAIN){ pid=fork_process(some_positive_number, "name", 1); if (pid<0) return -1; /* error */ else if (pid ==0){ /* child1_main_loop(); */ }else{ /* parent */ pid=fork_process(some_other_postivie_number, "name2", 1); if (pid<0) /* ... same as above */ ... } } return 0; } The forked child process shall also update its local configuration, please read the section "Refreshing the configuration" in doc/cfg.txt. Summary ------- mod_init(): - register the number of processes that will be forked from mod_child_init(PROC_MAIN) (if any) , see register_procs(). - initialize things requiring higher privileges - initialize per process variables with common value (they will be inherited by all the future children) - initialize shared memory variables if possible (no need for things that are available latter like the process count) mod_child_init(PROC_INIT) - same as mod_init except for registering processes & privileged stuff - the process number is now available so for example initializing shared per process statistics arrays (int stats[proc_no]) is possible - guaranteed to be run before any process is forked (like mod_init()). WARNING: same process context as mod_init() and mod_child_init(PROC_MAIN) so care must be taken not to initialize things 2 or 3 times for the "main" process. mod_child_init(PROC_MAIN) - the only place from where another SIP-router process can be forked (see fork_process ()), but remember first to register the number of to-be-forked processes in mod_init() mod_child_init(rank!=PROC_INIT && rank!=PROC_MAIN) - initialize other per process variables (e.g. different values), whose initial values will be visible only in the current process (they won't be inherited anymore). - open per process db connections, sockets a.s.o. - seed custom random generators WARNINGs: - several mod_child_inits() can run in parallel - the rank number is not necessarily unique - kamailio-4.0.4/doc/tcp_tunning.txt0000644000000000000000000000762112223032460015641 0ustar rootroot# $Id$ # # History: # -------- # 2006-01-26 created by andrei SIP-router TCP Tunning/monitoring for lots of open connections ============================================================== 0. Introduction ---------------- This document describes very briefly various settings that should improve sip-router+TCP performance for sites handling a lot of TCP traffic (> 1000 open connections or very high connection/disconnection rates). For now it deals only with Linux specific optimizations. 1. Useful Linux kernel settings ------------------------------- 1.1 Connection rate/pending connections: by default the connection rate is too small net.core.somaxconn - limit of the listen() backlog, default 128 net.ipv4.tcp_max_syn_backlog - default 1024 or 128 net.ipv4.tcp_timestamps - default on., should be on (along with tcp_tw_recycle and timestamp supporting peers allows for fast connections rates) 1.2 connection in close_wait Connection should stay as little as possible in close_wait to quickly free the fd/resources for new connections attempts WARNING: this could break normal TCP use, use it only if you know what you are doing net.ipv4.tcp_max_tw_buckets - maximum number of timewait sockets (the default seems to be ok) net.ipv4.tcp_tw_recycle - enables fast time wait sockets recycling (default off), should be enabled if you have lots of short lived connections WARNING: see the above warning net.ipv4.tcp_tw_reuse - allows reusing of time-wait sockets (default off) WARNING: see above net.ipv4.tcp_syncookies - default off, in this case it's probably better to keep it off 1.3 Port range net.ipv4.ip_local_port_range - should be increased (e.g. 4096-65534) 1.4 Open file descriptors fs.file-max - maximum number of fds that will be allocated (you probably need to increase it, default depends on installed memory) 1.5 other sysctl that might affect tcp connection rate or the maximum number of open connections fs.epoll.max_user_instances - maximum number of devices - per user (2.6.27.8+) fs.epoll.max_user_watches - maximum number of "watched" fds - per user (2.6.27.8+) net.ipv4.tcp_max_orphans - might be worth a look if things go wrong net.core.netdev_max_backlog - maximum device backlog Related applications -------------------- ifconfig txqueuelen - set device transmission queue len iptables - remove the ip_conntrack module (it limits the maximum tcp connections, adds extra overhead (slow)). It's probably better to remove all the iptables modules. 2. Monitoring (values to watch for) ----------------------------------- 2.1 File descriptors fs.dentry-state - format: nr. dentries, nr. unused, age_limit, want_pages fs.file-nr - format: allocated, unused, max (==fs.file-max) fs.inode-state - format: nr. allocated, nr. free, preshrink 2.2 TCP /proc/net/netstat - the TW, TWRecycled, TWKilled, PAWPassive, PAWActive, PASWEstab fields ( cat /proc/net/netstat |cut -d" " -f12-17 ; cat /proc/net/sockstat) /proc/net/sockstat 3. Sip-router settings ---------------------- - Don't forget to increase tcp_max_connections and the amount of shared memory - You should increase the number of ser "tcp_children" processes (-N no) As a rule of thumb, (maximum simultaneous connections)/2000 should be ok - You might have to decrease TCP_BUF_SIZE to a smaller value (e.g 8K) - You might want to increase PKG_MEM_POOL_SIZE (for large queues) - You might need to increase the maximum open fds limit before starting ser (e.g. ulimit -n 1000000) kamailio-4.0.4/doc/stylesheets/0000755000000000000000000000000012223032460015116 5ustar rootrootkamailio-4.0.4/doc/stylesheets/dbschema2docbook.xsl0000644000000000000000000001161012223032460021036 0ustar rootroot
<xsl:value-of select="name"/> database tables
Table "<xsl:value-of select="name"/>" name type size description
kamailio-4.0.4/doc/stylesheets/dbschema/0000755000000000000000000000000012223032460016664 5ustar rootrootkamailio-4.0.4/doc/stylesheets/dbschema/xsl/0000755000000000000000000000000012223032460017472 5ustar rootrootkamailio-4.0.4/doc/stylesheets/dbschema/xsl/common.xsl0000644000000000000000000001515712223032460021523 0ustar rootroot unsigned ERROR: Table: , column: - unsupported column type: . 0 1 1 1 0 ERROR: Column with id ' ' does not exist. ERROR: Column with id ' ' does not exist. kamailio-4.0.4/doc/stylesheets/dbschema/xsl/oracle.xsl0000644000000000000000000001203412223032460021467 0ustar rootroot ) ; TINYINT SMALLINT INT BIGINT DATETIME DOUBLE FLOAT VARCHAR BLOB UNSIGNED , UNIQUE KEY ( ) , ' ' @' % ' DELETE FROM WHERE ; INSERT IGNORE INTO ( ) VALUES ( ); kamailio-4.0.4/doc/stylesheets/dbschema/xsl/postgres.xsl0000644000000000000000000001507112223032460022074 0ustar rootroot SMALLINT SMALLINT INTEGER BIGINT TIMESTAMP DOUBLE PRECISION REAL VARCHAR BYTEA WITHOUT TIME ZONE CREATE TABLE ( , CONSTRAINT UNIQUE ( ) , CREATE UNIQUE INDEX ON ( ); CREATE USER PASSWORD ' ' PASSWORD ' ' ; DROP USER ; GRANT ON , TO ; kamailio-4.0.4/doc/stylesheets/dbschema/xsl/dbtext.xsl0000644000000000000000000001106612223032460021520 0ustar rootroot ( int double str ,null ) ERROR: Value for column in table was not provided and the column has no default value. : kamailio-4.0.4/doc/stylesheets/dbschema/xsl/sql.xsl0000644000000000000000000002005612223032460021024 0ustar rootroot CREATE TABLE ( ); NOT NULL DEFAULT NULL ' ' DEFAULT NULL ' ' , ( ) , UNIQUE ( ) , , DELETE FROM WHERE ; INSERT INTO ( ) VALUES ( ); ERROR: row-identificator does not exists. ERROR: row-identificator does not have any column. = ERROR: Value of column with id ' ' does not exist. ' ' AND NULL ' ' , , kamailio-4.0.4/doc/stylesheets/dbschema/xsl/mysql.xsl0000644000000000000000000001221012223032460021363 0ustar rootroot ) Type= ; TINYINT SMALLINT INT BIGINT DATETIME DOUBLE FLOAT VARCHAR BLOB UNSIGNED , UNIQUE KEY ( ) , ' ' @' % ' DELETE FROM WHERE ; INSERT IGNORE INTO ( ) VALUES ( ); kamailio-4.0.4/doc/stylesheets/dbschema/xsl/docbook.xsl0000644000000000000000000000136112223032460021643 0ustar rootroot kamailio-4.0.4/doc/stylesheets/dbschema/README0000644000000000000000000000122112223032460017540 0ustar rootrootSER supports multiple database servers, at the moment of writing this text MySQL, PostgreSQL, dbtext (aka plain text files), and Oracle databases are supported. Maintaining database schema for each database separately would be tedious job, therefore SER keeps the database schema in an XML-based format and schema definitions for the database servers are generated automatically using stylesheets. This directory contains DTD (Document Type Definition) files describing the structure of the XML-based database description language and XSL stylesheets used to generate database schema files for all the database servers. -- Jan Janak kamailio-4.0.4/doc/stylesheets/dbschema/dtd/0000755000000000000000000000000012223032460017437 5ustar rootrootkamailio-4.0.4/doc/stylesheets/dbschema/dtd/dbschema.dtd0000644000000000000000000000166212223032460021707 0ustar rootroot kamailio-4.0.4/doc/stylesheets/serdoc2man.xsl0000644000000000000000000000521012223032460017701 0ustar rootroot TODO () (7) (1) (8) (5)
Type:

Default:

Type:
[ ]
kamailio-4.0.4/doc/stylesheets/dbschema_k/0000755000000000000000000000000012223032460017176 5ustar rootrootkamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/0000755000000000000000000000000012223032461020005 5ustar rootrootkamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/dbschema2docbook.xsl0000644000000000000000000002216412223032460023732 0ustar rootroot Table "<xsl:value-of select="name"/>" name type size default null key extra attributes description not specified NULL ' ' yes no primary autoincrement
Table "<xsl:value-of select="name"/>" indexes name type links description unique primary default ,
kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/common.xsl0000644000000000000000000001551512223032460022033 0ustar rootroot unsigned ERROR: Table: , column: - unsupported column type: . 0 1 1 1 0 ERROR: Column with id ' ' does not exist. ERROR: Column with id ' ' does not exist. kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/oracle.xsl0000644000000000000000000001552112223032460022005 0ustar rootroot ) ; CREATE OR REPLACE TRIGGER before insert on FOR EACH ROW BEGIN auto_id(:NEW.id); END / BEGIN map2users(' '); END; / NUMBER(5) NUMBER(5) NUMBER(10) NUMBER(11) BIGINT DATE NUMBER NUMBER VARCHAR2 BLOB CLOB PRIMARY KEY DEFAULT NULL DEFAULT NULL ' ' NOT NULL , kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/pi_framework_table.xsl0000644000000000000000000000727112223032460024377 0ustar rootroot <!-- Declaration of table--> <db_table id=" "> <table_name> </table_name> <db_url_id>mysql</db_url_id> </db_table> <column><field> </field><type> </type></column> DB1_INT DB1_INT DB1_INT DB1_BIGINT DB1_DATETIME DB1_DOUBLE DB1_DOUBLE DB1_STR DB1_BLOB DB1_BLOB DB1_BLOB DB1_BLOB kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/modules_c.xsl0000644000000000000000000002001212223032460022501 0ustar rootroot /*! * \file * \ingroup db * \brief Database support for modules. * * Database support functions for modules. * * @cond * WARNING: * This file was autogenerated from the XML source file * It can be regenerated by running 'make modules' in the db/schema * directory of the source code. You need to have xsltproc and * docbook-xsl stylesheets installed. * ALL CHANGES DONE HERE WILL BE LOST IF THE FILE IS REGENERATED * @endcond */ /* database variables */ /* TODO assign read-write or read-only URI, introduce a parameter in XML */ /* * Closes the DB connection. */ /*! * Initialises the DB API, check the table version and closes the connection. * This should be called from the mod_init function. * * \return 0 means ok, -1 means an error occured. */ LM_ERR("you have to set the db_url module parameter.\n"); return -1; } LM_ERR("can't bind database module.\n"); return -1; } LM_ERR("can't connect to database.\n"); return -1; } if ( || ) { LM_ERR("during table version check.\n"); return -1; } return 0; } /*! * Initialize the DB connection without checking the table version and DB URL. * This should be called from child_init. An already existing database * connection will be closed, and a new one created. * * \return 0 means ok, -1 means an error occured. */ LM_ERR("can't connect to database.\n"); return -1; } return 0; } /*! * Update the variable length after eventual assignments from the config script. * This is necessary because we're using the 'str' type. */ } /* column names */ /* table version */ ; kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/postgres.xsl0000644000000000000000000001030412223032460022400 0ustar rootroot ) Type= ; SMALLINT SMALLINT INTEGER BIGINT TIMESTAMP DOUBLE PRECISION REAL VARCHAR BYTEA TEXT WITHOUT TIME ZONE SERIAL PRIMARY KEY kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/dbtext.xsl0000644000000000000000000000766712223032460022046 0ustar rootroot : ( int double string ,null ) kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/sql.xsl0000644000000000000000000001567212223032460021346 0ustar rootroot CREATE TABLE ( INSERT INTO version (table_name, table_version) values (' ',' '); , CONSTRAINT UNIQUE ( ) , PRIMARY KEY ( ) , CREATE UNIQUE INDEX ON ( ); DEFAULT NULL DEFAULT NULL ' ' NOT NULL , ( ) , kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/db_berkeley.xsl0000644000000000000000000001561212223032460023010 0ustar rootroot METADATA_COLUMNS METADATA_KEY METADATA_READONLY 0 METADATA_LOGFLAGS 0 METADATA_DEFAULTS NULL NULL ' ' NIL | | | ( int datetime double str ) kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/dbdoc.xsl0000644000000000000000000001056712223032460021620 0ustar rootroot WARNING: This file was autogenerated from the XML source file It can be regenerated by running 'make dbdoc' in the db/schema directory of the source code. You need to have xsltproc and docbook-xsl stylesheets installed. ALL CHANGES DONE HERE WILL BE LOST IF THE FILE IS REGENERATED Module parameter for database access.
<varname>db_url</varname> (String) URL to the database containing the data. Default value is mysql://openserro:openserro@localhost/openser. Set <varname>db_url</varname> parameter
<varname><xsl:value-of select="concat($name, '_table')"/></varname> (String) Default value is . Set <varname><xsl:value-of select="concat($name, '_table')"/></varname> parameter
<varname><xsl:value-of select="concat($table.name, '_', $column.name, '_col')"/></varname> (string) Set <varname><xsl:value-of select="concat($table.name, '_', $column.name, '_col')"/></varname> parameter
kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/mysql.xsl0000644000000000000000000001116212223032461021703 0ustar rootroot ) ENGINE= ; TINYINT SMALLINT INT BIGINT DATETIME DOUBLE FLOAT VARCHAR BLOB MEDIUMBLOB TEXT MEDIUMTEXT UNSIGNED AUTO_INCREMENT PRIMARY KEY kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/modules_h.xsl0000644000000000000000000001403612223032460022517 0ustar rootroot /*! * \file * \ingroup db * \brief Database support for modules. * * Database support functions for modules. * * @cond * WARNING: * This file was autogenerated from the XML source file * It can be regenerated by running 'make modules' in the db/schema * directory of the source code. You need to have xsltproc and * docbook-xsl stylesheets installed. * ALL CHANGES DONE HERE WILL BE LOST IF THE FILE IS REGENERATED * @endcond */ /* necessary includes */ #include "../../db/db.h" #include "../../str.h" #include "../../ut.h" #include <string.h> /* database variables */ /* * Closes the DB connection. */ /*! * Initialises the DB API, check the table version and closes the connection. * This should be called from the mod_init function. * * \return 0 means ok, -1 means an error occured. */ /*! * Initialize the DB connection without checking the table version and DB URL. * This should be called from child_init. An already existing database * connection will be closed, and a new one created. * * \return 0 means ok, -1 means an error occured. */ /*! * Update the variable length after eventual assignments from the config script. * This is necessary because we're using the 'str' type. */ #endif /* column names */ /* table version */ kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/docbook.xsl0000644000000000000000000000364712223032460022166 0ustar rootroot kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/db_sqlite.xsl0000644000000000000000000001015312223032460022502 0ustar rootroot ) Type= ; SMALLINT SMALLINT INTEGER BIGINT TIMESTAMP DOUBLE PRECISION REAL VARCHAR BLOB TEXT WITHOUT TIME ZONE INTEGER PRIMARY KEY kamailio-4.0.4/doc/stylesheets/dbschema_k/xsl/pi_framework_mod.xsl0000644000000000000000000001307312223032460024064 0ustar rootroot <!-- provisionning --> <mod><mod_name> </mod_name> <cmd><cmd_name>show</cmd_name> <db_table_id> </db_table_id> <cmd_type>DB1_QUERY</cmd_type> <query_cols> <col><field> </field></col> </query_cols> </cmd> <cmd><cmd_name>add</cmd_name> <db_table_id> </db_table_id> <cmd_type>DB1_INSERT</cmd_type> <query_cols> <col><field> </field></col> </query_cols> </cmd> <cmd><cmd_name>update</cmd_name> <db_table_id> </db_table_id> <cmd_type>DB1_UPDATE</cmd_type> <clause_cols> <col><field> </field><operator>=</operator></col> </clause_cols> <query_cols> <col><field> </field></col> </query_cols> </cmd> <cmd><cmd_name>delete</cmd_name> <db_table_id> </db_table_id> <cmd_type>DB1_DELETE</cmd_type> <clause_cols> <col><field> </field><operator>=</operator></col> </clause_cols> </cmd> </mod> kamailio-4.0.4/doc/stylesheets/dbschema_k/bookinfo.xml0000644000000000000000000000166212223032460021533 0ustar rootroot &kamailio; database tables &kamailio; &kamailio; Development Team &kamailiohomelink;
&kamailiodevmail;
Henning Westerholt 1und1 Internet AG
henning.westerholt@1und1.de
Norman Brandinger
2007 &kamailio; development Team $Revision: 4594 $ $Date: 2008-08-06 12:08:33 +0200 (Mi, 06 Aug 2008) $
kamailio-4.0.4/doc/stylesheets/dbschema_k/catalog.xml0000644000000000000000000000666012223032460021342 0ustar rootroot kamailio-4.0.4/doc/stylesheets/dbschema_k/dtd/0000755000000000000000000000000012223032460017751 5ustar rootrootkamailio-4.0.4/doc/stylesheets/dbschema_k/dtd/dbschema.dtd0000644000000000000000000000333412223032460022217 0ustar rootroot kamailio-4.0.4/doc/ser_radius/0000755000000000000000000000000012223032460014702 5ustar rootrootkamailio-4.0.4/doc/ser_radius/ser_radius.xml0000644000000000000000000005236512223032460017577 0ustar rootroot %docentities; ]>
Jan Janak jan@iptel.org 2003 FhG FOKUS $Revision$ $Date$ SIP-router RADIUS Howto
Introduction SIP-router can be configured to use RADIUS server for authentication, accounting, and group membership checking. Since configuration of RADIUS seems to be a common source of problems, we decided to put together this HOWTO. The HOWTO covers installation and configuration of FreeRADIUS server only. There are other RADIUS servers available and as long as they support digest authentication, they should work too. Any volunteers willing to describe setup of other RADIUS servers are encouraged to contact the author.
Prerequisites To setup RADIUS support in SIP-router you will need the following: FreeRADIUS server, you can get it from FreeRADIUS website. The HOWTO describes installation and setup of release 0.9.1. Radiusclient library. In version 0.8.14 we started to use the new version of radiusclient library developed by Maxim Sobolev called radiusclient-ng. The homepage of the library is http://developer.berlios.de/projects/radiusclient-ng/ SIP-router, get it from &serhome; You should also have some experience in configuring SIP-router. Before you enable RADIUS authentication or accounting make sure that the basic server is running and that you know how to customize it to your taste. If you want to use RADIUS accounting then you will have to compile SIP-router from sources so you should know how to do it. Various Unix/Linux distributions might include binary packages of the mentioned applications. In that case you can safely use the packages, there shouldn't be any problem. Location of some files may be different, though. We will describe how to install the software from sources only. Configuration of FreeRADIUS server described in the document is in no way exhaustive. This document is a sort of quick-start-guide, it shows how to get things running, but you should definitely read FreeRADIUS documentation and configure the server properly ! You have been warned.
Radiusclient Library Untar the source tarball. root@localhost:/usr/local/src# tar xvfz radiusclient-0.4.3.tar.gz Compile and install the library. root@localhost:/usr/local/src# cd radiusclient-0.3.2 root@localhost:/usr/local/src/radiusclient-0.3.2# ./configure root@localhost:/usr/local/src/radiusclient-0.3.2# make root@localhost:/usr/local/src/radiusclient-0.3.2# make install By default all the configuration files of the radiusclient library will be in /usr/local/etc/radiusclient directory. If you use binary packages then the configuration files will be probably in /etc/radiusclient.
File <filename>radiusclient.conf</filename> The main configuration file of the library is /usr/local/etc/radiusclient/radiusclient.conf, open the file in your favorite text editor and find lines containing the following: authserver localhost This is the hostname or IP address of the RADIUS server used for authentication. You will have to change this unless the server is running on the same host as your SIP proxy. acctserver localhost This is the hostname or IP address of the RADIUS server used for accounting. You will have to change this unless the server is running on the same host as your SIP proxy.
File <filename>servers</filename> RADIUS protocol uses simple access control mechanism based on shared secrets that allows RADIUS servers to limit access from RADIUS clients. A RADIUS server is configured with a secret string and only RADIUS clients that have the same secret will be accepted. You need to configure a shared secret for each server you have configured in radiusclient.conf file in the previous step. The shared secrets are stored in /usr/local/etc/radiusclient/servers file. Each line contains hostname of a RADIUS server and shared secret used in communication with that server. The two values are separated by whitespaces. Configure shared secrets for every RADIUS server you are going to use. RADIUS servers and clients must be configured with the same shared secret, otherwise they will not accept RADIUS messages from each other and neither authentication nor accounting will work !
File <filename>dictionary</filename> Radiusclient library contains file called dictionary.ser. That file includes all the attributes that are needed by SIP-router. Include the file in the main dictionary file. To include the file, put the following line at the end of dictionary file: $INCLUDE /usr/local/etc/radiuclient/dictionary.ser
FreeRADIUS Server Untar, configure, build, and install the server: root@localhost:/usr/local/src# tar xvfz freeradius-0.9.1.tar.gz root@localhost:/usr/local/src# cd freeradius-0.9.1 root@localhost"/usr/local/src/freeradius-0.9.1# ./configure root@localhost"/usr/local/src/freeradius-0.9.1# make root@localhost"/usr/local/src/freeradius-0.9.1# make install All the configuration files of FreeRADIUS server will be in /usr/local/etc/raddb directory. If you install a binary package then you will probably find them in /etc/raddb. The following sections describe how to configure freeradius server. First we describe the common configuration that must be done in any case. Configuration specific for authentication, accounting, and group membership checking will be described in separate sections.
Common configuration
File <filename>clients.conf</filename> File /usr/local/etc/raddb/clients.conf contains description of RADIUS clients that are allowed to use the server. For each of the clients you need to specify it's hostname or IP address and also a shared secret. The shared secret must be the same string you configured in radiusclient library. Suppose that your SIP server is running on host proxy.foo.bar and radiusclient library on that machine has been configure with "foobarsecret" as the shared secret. You need to put the following section into the file: client proxy.foo.bar { secret = foobarsecret shortname = foo } This fragment allows access from RADIUS clients on proxy.foo.bar if they use "foobarsecret" as the shared secret. The file already contains an entry for localhost (127.0.0.1), so if you are running the RADIUS server on the same host as your SIP server, then modify the existing entry instead. By default it contains shared secret "testing123".
File <filename>dictionary</filename> File /usr/local/etc/raddb/dictionary contains the dictionary of FreeRADIUS server. You have to add the same dictionary file (dictionary.ser), which you added to the dictionary of radiusclient library, also here. In this case you don't have to append the contents of the file, you can include it into the main file. Add the following line at the end of /usr/local/etc/raddb/dictionary: $INCLUDE /usr/local/etc/radiusclient/dictionary.ser That will include the same attribute definitions that are used in radiusclient library so the client and server will understand each other.
File <filename>radiusd.conf</filename> Digest authentication is disabled by default and you must enable it in this file. There are two sections, "authorize" and "authenticate". Both sections contain line containing word "digest". Both of them are commented and you must un-comment them to enable digest authentication. There is also another line containing word "digest" followed by curly braces and it is enabled by default. The section is supposed to contain digest module parameters but because digest module has no parameters, it is empty. This is not the line you are supposed to uncomment ! There are two more.
File <filename>users</filename> This file contains authentication information for each user. For testing purposes we will create user "test". Put the following into the file: test Auth-Type := Digest, User-Password == "test" Reply-Message = "Hello, test with digest" The username and password is for testing only, you can safely remove the entry once your RADIUS server works and you are able to authenticate.
Test The Server This step is optional. The basic configuration of FreeRADIUS server is done it now we are going to test if it really works. Start the server with parameter -X. That will cause the server to stay in the foreground (it will not turn into daemon) and produce a lot of debugging information on the standard output: root@/usr/local/src# radiusd -X Create file digest and put the following into the file: User-Name = "test", Digest-Response = "631d6d73147add2f9e437f59bbc3aeb7", Digest-Realm = "testrealm", Digest-Nonce = "1234abcd" , Digest-Method = "INVITE", Digest-URI = "sip:5555551212@example.com", Digest-Algorithm = "MD5", Digest-User-Name = "test" All the attributes must be on a single line. Run radclient to test the server: root@/usr/local/src# radclient -f digest localhost auth <shared_secret> I suppose that you run the test utility directly on the RADIUS server since it comes with the FreeRADIUS server package. That also means that you have to enable access from localhost in your clients.conf file. Don't forget to replace <shared_secret> with the shared secret configured for localhost clients in clients.conf. If your server works properly then you should see the following response: Received response ID 224, code 2, length = 45 Reply-Message = "Hello, test with digest"
Authentication Configuration To create user "joe" in domain "sip-router.org" with password "heslo" put the following into file /usr/local/etc/raddb/users: joe@sip-router.org Auth-Type := Digest, User-Password == "heslo" Reply-Message = "Authenticated", Sip-Rpid = "1234" Attribute "Sip-Rpid" is optional. The attribute contains a phone number associated to the user. SIP-router can be configured to put the phone number into Remote-Party-ID header field of the SIP message. The header field can be then used by PSTN gateways to display the number as the number of the caller on regular phones. You can omit the attribute if you don't need it.
Accounting Configuration By default the FreeRADIUS server will log all accounting requests into /usr/local/var/log/radius/radacct directory in form of plain text files. The server will create one file for each hostname in the directory. The following example shows how the log files look like. Example of Accounting Report Tue Jun 24 00:20:55 2003 Acct-Status-Type = Start Service-Type = 15 Sip-Response-Code = 200 Sip-Method = 1 User-Name = "gh@192.168.2.16" Calling-Station-Id = "sip:gh@192.168.2.16" Called-Station-Id = "sip:jiri@192.168.2.16" Sip-Translated-Request-URI = "sip:jiri@192.168.2.36" Acct-Session-Id = "b9a2ffaa-0458-42e1-b5fd-59656b795d29@192.168.2.32" Sip-To-Tag = "cb2cfe2e-3659-28c7-a8cc-ab0b8cbd3012" Sip-From-Tag = "a783bd2f-bb8d-46fd-84a9-00a9833f189e" Sip-CSeq = "1" NAS-IP-Address = 192.168.2.16 NAS-Port = 5060 Acct-Delay-Time = 0 Client-IP-Address = 127.0.0.1 Acct-Unique-Session-Id = "9b323e6b2f5b0f33" Timestamp = 1056406855 Tue Jun 24 00:20:56 2003 Acct-Status-Type = Stop Service-Type = 15 Sip-Response-Code = 200 Sip-Method = 8 User-Name = "jiri@192.168.2.16" Calling-Station-Id = "sip:jiri@192.168.2.16" Called-Station-Id = "sip:gh@192.168.2.16" Sip-Translated-Request-URI = "sip:192.168.2.32:9576" Acct-Session-Id = "b9a2ffaa-0458-42e1-b5fd-59656b795d29@192.168.2.32" Sip-To-Tag = "a783bd2f-bb8d-46fd-84a9-00a9833f189e" Sip-From-Tag = "cb2cfe2e-3659-28c7-a8cc-ab0b8cbd3012" Sip-CSeq = "4580" NAS-IP-Address = 192.168.2.16 NAS-Port = 5060 Acct-Delay-Time = 0 Client-IP-Address = 127.0.0.1 Acct-Unique-Session-Id = "b2c2479a07b17c95" Timestamp = 1056406856
Group Checking Configuration If you want to make user "joe" in domain "sip-router.org" member of group "pstn" then add the following to your /usr/local/etc/raddb/users file: joe@sip-router.org Sip-Group == "pstn", Auth-Type := Accept Reply-Message = "Authorized"
SIP-router Configuration We will describe installation from sources here. If you use binary packages then there is an additional package containing RADIUS related modules. You will need to install the package. RADIUS-related modules are not compiled by default. To compile them, edit Makefile, find variable exclude_modules and you should see "auth_radius", "acc_radius", and "misc_radius" among excluded modules. Simply remove the three modules from the list. If you need RADIUS accounting then edit also sip_router/modules/acc/Makefile and uncomment lines containing: DEFS+=-DRAD_ACC LIBS=-L$(LOCALBASE)/lib -lradiusclient Then recompile and re-install SIP-router: root@localhost:/usr/local/src/sip_router# make proper root@localhost:/usr/local/src/sip_router# make all root@localhost:/usr/local/src/sip_router# make install
Authentication Configuration Edit configuration file of SIP-router and instead of auth_db.so load auth_radius.so and also replace www_authorize with radius_www_authorize. radius_www_authorize takes just one parameter (as opposed to www_authorize which takes 2).
Accounting Configuration To enable RADIUS accounting simply use radius_log_flag and radius_log_missed_flag parameters instead of log_flag and log_missed_flag. Mark transactions that should be logged with flags configured in the parameters.
Group Membership Checking Instead of group.so load group_radius.so. The module exports the same functions as group.so, the only difference is that all the function names exported by group_radius.so have "radius_" prefix.
Frequently Asked Questions I compiled SIP-router RADIUS modules and installed radiusclient library, but when I try to start the server I get the following error message: libradiusclient.so.0: cannot open shared object file: No such file or directory Make sure that the directory which contains the library (usually /usr/local/lib) is listed in /etc/ld.so.conf and run ldconfig -v (as root). I configured everything as described in this HOWTO, but I get the following message from radiusclient library "check_radius_reply: received invalid reply digest from RADIUS server". What does that mean ? That means that radiusclient library was unable to verify digest of the RADIUS message (it is not related to SIP digest) because shared secret of the client and server do not match. FreeRADIUS server has two files that can contain definitions of clients and corresponding shared secrets--clients and clients.conf. If you have proper shared secret in one file and you still get the mentioned error message then check also the other file. This can easily happen to clients running on the same host (127.0.0.1 or localhost), because clients.conf contains definition for localhost by default with secret "testing123".
kamailio-4.0.4/doc/ser_radius/Makefile0000644000000000000000000000012312223032460016336 0ustar rootrootdocs = ser_radius.xml docbook_dir = ../../docbook include $(docbook_dir)/Makefile kamailio-4.0.4/doc/dns.txt0000644000000000000000000003610412223032457014101 0ustar rootroot# $Id$ # # History: # -------- # 2006-09-08 created by andrei # 2007-06-18 added naptr & friends, dns_srv_lb, more compile options (andrei) # SIP-router and DNS Overview --------------------------- The dns subsystem in sip-router can either directly use libresolv and a combination of the locally configured dns server, /etc/hosts and the local Network Information Service (NIS/YP a.s.o) or cache the query results (both positive and negative) and look first in its internal cache. When its internal dns cache is enabled, sip-router can also use dns failover: if one destination resolves to multiple addresses sip-router can try all of them until it finds one to which it can successfully send the packet or it exhausts all of them. sip-router (The tm module to be more precise) uses the DNS failover also when the destination host doesn't send any reply to a forwarded invite within the SIP timeout interval (whose value can be configured using the tm fr_timer parameter). When SRV based load balancing is enabled sip-router can even do DNS based load balancing (see RFC2782 and the dns_srv_lb option below). DNS Cache and Failover Drawbacks -------------------------------- Using the DNS cache and the DNS failover has also some drawbacks: 1. only the locally configured DNS server (usually in /etc/resolv.conf) is used for the requests (/etc/hosts and the local Network Information Service are ignored). Workaround: disable the dns cache (use_dns_cache=off or compile without -DUSE_DNS_CACHE). 2. the DNS cache uses extra memory Workaround: disable the DNS cache. 3. the DNS failover introduces a very small performance penalty Workaround: disable the DNS failover (use_dns_failover=off). 4. the DNS failover increases the memory usage (the internal structures used to represent the transaction are bigger when the DNS failover support is compiled). Workaround: compile without DNS failover support (-DUSE_DNS_FAILOVER). Turning it off from the config file is not enough in this case (the extra memory will still be used). On the other hand using the DNS cache saves lots of DNS queries and makes DNS based failover and DNS based load balancing possible. If the destination blacklist is enabled, sip-router can do failover even if forwarding in stateless mode. In the ideal case with the DNS cache enabled sip-router will do only one query for a NAPTR (if enabled) or SRV lookup and then it will use the results for the record's TTL (for example if all the resulting records have 1 minute TTL, the server won't make another query for this domain for 1 minute). Even negative answers will be cached. Without the DNS cache, each NAPTR or SRV lookup will result in at least 2 queries. These queries will happen every time, for each message (even if all of them go to the same domain). DNS Resolver Options -------------------- The DNS resolver options control how sip-router will interact with the external DNS servers. These options (with the dns_try_ipv6 exception) are passed to libresolv and are used each time a dns request is made. The default values are system specific and generally depend on the /etc/resolv.conf content. For servers doing a lot of DNS requests it is highly recommended to change the default values in the sip-router config file (even if using sip-router's internal dns cache). dns_try_ipv6 = on | off - if on and sip-router listens on at least one ipv6 socket, ipv6 (AAAA) lookups will be performed if the ipv4 (A) lookups fail. If off only ipv4 (A) lookups will be used. Default: on if sip-router is compiled with ipv6 support. dns_try_naptr = on | off - if on sip-router will first try a NAPTR lookup for destinations that don't have the protocol or port specified and are not simple ip addresses (as described in RFC 3263). This will introduce a slight performance penalty and will probably cause extra DNS lookups. For example a lookup for a non-existing domain will produce one extra query: NAPTR(domain), SRV(_sip._udp.domain) and A/AAAA(domain). If the result of a query contains several NAPTR records, sip-router will select among them according to the RFC2915 and sip-router preference towards a specific protocol (see dns_udp_pref, dns_tcp_pref and dns_tls_pref below). For an RFC3263 compliant configuration (choose the remote side preferred protocol if supported), set dns_udp_pref, dns_tcp_pref and dns_tls_pref to the same value (>=0), e.g. 0. Default: off dns_udp_pref = number - udp protocol preference when doing NAPTR lookups. This option works together with dns_tcp_pref, dns_tls_pref and dns_sctp_pref. If all this options have the same positive value and more NAPTR records are available, ser will select the NAPTR record preferred by the remote side (according to RFC2915). If the values are positive but different, ser will select the NAPTR record whose protocol it prefers the most (the protocol with the highest dns__pref number). If there are several NAPTR records with the same preferred protocol, ser will select among them based on their order and preference (see RFC2915). To completely disable selecting a specific protocol, use a negative number. For example dns_tcp_pref=-1 will completely disable selection of tcp NAPTR records, even if this will result in the NAPTR lookup failure. Note: if a protocol is disabled in ser (e.g. tls_disable=1) the corresponding NAPTR records selection will be also disabled, irrespective of the dns__preference value. Default: dns_udp_pref=30, dns_tcp_pref=20, dns_tls_pref=10 and dns_sctp_pref=20. (prefer udp, but if no udp NAPTR record found or no SRV-resolvable udp NAPTR record found use tcp or sctp records and if this fails too use tls) dns_tcp_pref = number (see dns_udp_pref above) dns_tls_pref = number (see dns_udp_pref above) dns_sctp_pref = number (see dns_udp_pref above) dns_retr_time = time - time in s before retrying a dns request. Default: system specific, depends also on the/etc/resolv.conf content (usually 5 s). dns_retr_no = no. - number of dns retransmissions before giving up. Default: see above (usually 4) dns_servers_no = no. - how many dns servers from the ones defined in /etc/resolv.conf will be used. Default: all of them. dns_use_search_list= yes/no - if no, the search list in /etc/resolv.conf will be ignored (=> fewer lookups => gives up faster). Default: yes. HINT: even if you don't have a search list defined, setting this option to "no" will still be "faster", because an empty search list is in fact search "" (so even if the search list is empty/missing there will still be 2 dns queries, eg. foo+'.' and foo+""+'.') dns_search_full_match = yes/no - controls the check of the name part which is found in the answer expanding the searched name before the answer is treated as correct and "link" (fake CNAME record) between the short name (query) and long name (answer) is created which is then stored in dns_cache and reused for next queries. If set to no - no additional check is done. If set to yes - the additional part is checked against the search list. The maximum time a dns request can take (before failing) is: (dns_retr_time*dns_retr_no)*(search_list_domains) If dns_try_ipv6 is yes, mutliply it again by 2. The option combination that produces the "fastest" dns resolver config (the "faster" in the sense that it gives up the quickest) is: dns_try_ipv6=no dns_retr_time=1 dns_retr_no=1 dns_servers_no=1 dns_use_search_list=no The recommended dns configuration is to have a "close" dns caching recursive server configured in /etc/resolv.conf, set the dns resolver options in ser's config as in the above example and enable the dns cache (in ser). Pay particular attention to dns_servers_no and dns_use_search_list. It's a good idea to make sure you don't need / use the search list or more then one dns server (to avoid unnecessary extra lookups). DNS Resolver Compile Options ---------------------------- USE_NAPTR - if defined the naptr lookup support will be compiled in. NAPTR support still has to be enabled from ser's config file (it's off by default). RESOLVE_DBG - if defined, the resolver will be very verbose: it will log a lot of debugging information at L_DBG level. NAPTR_DBG - if defined the NAPTR related resolver functions will be very verbose. DNS Cache and Failover Config Variables --------------------------------------- use_dns_cache = on | off - if off the dns cache won't be used (all dns lookups will result into a dns request). When on all the dns request results will be cached. WARNING: when enabled /etc/hosts will be completely bypassed, all the dns request will go directly to the system configured (resolv.conf) dns server. Default: on. use_dns_failover = on |off - if on and sending a request fails (due to not being allowed from an onsend_route, send failure, blacklisted destination or, when using tm, invite timeout), and the destination resolves to multiple ip addresses and/or multiple SRV records, the send will be re-tried using the next ip/record. In tm's case a new branch will be created for each new send attempt. Default: off. Depends on use_dns_cache being on. If tm is used along with dns failover is recommended to also turn on dst_blacklist. dns_srv_lb = on | off or dns_srv_loadbalancing = on | off - if on instead of doing simple dns failover (like above), ser will load balance requests to different srv records of the same priority based on the srv records weights (like described in RFC2782). For a destination which has different priorities for all its srv records, this option will be equivalent with simple dns failover. Note: this option requires having dns failover enabled (see use_dns_failover above). Default: off. dns_try_ipv6 = on | off - shared with the resolver (see resolver description). dns_try_naptr = on | off - shared with the resolver (see resolver description). dns_udp_pref = number - shared with the resolver (see resolver description). dns_tcp_pref = number - shared with the resolver (see resolver description). dns_tls_pref = number - shared with the resolver (see resolver description). dns_cache_flags = dns cache specific resolver flags, used for overriding the default behaviour (low level). Possible values: 1 - ipv4 only: only DNS A requests are performed, even if ser listens also on ipv6 addresses. 2 - ipv6 only: only DNS AAAA requests are performed. Ignored if dns_try_ipv6 is off or ser doesn't listen on any ipv6 address. 4 - prefer ipv6: try first to resolve a host name to an ipv6 address (DNS AAAA request) and only if this fails try an ipv4 address (DNS A request). By default the ipv4 addresses are preferred. Default: 0 dns_cache_negative_ttl = time to live for negative results ("not found") in seconds. Use 0 to disable. Default: 60 s. dns_cache_min_ttl = minimum accepted time to live for a record, in seconds. If a record has a lower ttl, its value will be discarded and dns_cache_min_ttl will be used instead. Default: 0 dns_cache_max_ttl = maximum accepted time to live for a record, in seconds. If a record has a higher ttl, its value will be discarded and dns_cache_max_ttl will be used instead. Default: MAXINT dns_cache_mem = maximum memory used for the dns cache in Kb. Default: 500 Kb dns_cache_gc_interval = how often (in s) the dns cache will be garbage collected. Default: 120 s. dns_cache_del_nonexp = yes | no or dns_cache_delete_nonexpired = yes | no - allow deletion of non-expired records from the cache when there is no more space left for new ones. The last-recently used entries are deleted first. Default: no dns_cache_init = on | off - if off, the dns cache is not initialized at startup and cannot be enabled runtime, that saves some memory. Default: on DNS Cache Compile Options ------------------------- USE_DNS_CACHE - if defined the dns cache support will be compiled in (default). If not needed/wanted the dns_cache can be disabled from the ser's config file. The only advantages for not compiling the dns cache support is a slight decrease of the executable size and an extremely small performance increase (1 less comparison per dns request). USE_DNS_FAILOVER - if defined the dns failover support will be compiled in. (default). Compiling the dns failover support has a few disadvantages, see the "Drawbacks" section. DNS_SRV_LB - if defined (default) support for load balancing using srv records weights (as described in RFC2782) will be compiled in. Note however that it still must be enabled from the ser config, it's disabled by default (see the dns_srv_lb config option). USE_NAPTR - (shared with the resolver) if defined NAPTR support will be compiled in (default). Note that even if compiled, NAPTR support must be enabled also from the ser config (see the dns_try_naptr option). NAPTR_CACHE_ALL_ARS - if defined all the additional records in a NAPTR answer will be cached. Normally ser would cache only "related" records (records that are directly referred), but for answers with lots of A/AAAA records it might happen that not all of the SRV records will fit in the AR section. In this case, without this compile option ser will not cache the un-referred A/AAAA records. BY default this option is disabled. CACHE_RELEVANT_RECS_ONLY - if defined (default), records in the AR section of an answer will be cached only if they are "related" to the query. For example if the query is for a SRV record, A & AAAA records in the AR section will be cached only if there are SRV records pointing to them. This avoids adding possible garbage to the cache. If this option is not defined (experimental), everything in the AR section will be added to the cache. DNS_CACHE_DEBUG - if defined the dns cache will be very verbose (it will log lots of messages at the L_DBG levell). Note: To remove a compile options, edit sip-router's Makefile.defs and remove it from DEFS list. To add a compile options add it to the make command line, e.g.: make proper; make all extra_defs=-DUSE_DNS_FAILOVER or for a permanent solution, edit Makefile.defs and add it to DEFS (don't foget to prefix it with -D). Some options require editing dns_cache.c or resolve.[ch] (just grep after them). kamailio-4.0.4/doc/sip/0000755000000000000000000000000012223032460013335 5ustar rootrootkamailio-4.0.4/doc/sip/sip_introduction.xml0000644000000000000000000013375212223032460017466 0ustar rootroot
Jan Janak jan@iptel.org 2003 FhG FOKUS A brief overview of SIP describing all important aspects of the Session Initiation Protocol. SIP Introduction
Purpose of SIP SIP stands for Session Initiation Protocol. It is an application-layer control protocol which has been developed and designed within the IETF. The protocol has been designed with easy implementation, good scalability, and flexibility in mind. The specification is available in form of several RFCs, the most important one is RFC3261 which contains the core protocol specification. The protocol is used for creating, modifying, and terminating sessions with one or more participants. By sessions we understand a set of senders and receivers that communicate and the state kept in those senders and receivers during the communication. Examples of a session can include Internet telephone calls, distribution of multimedia, multimedia conferences, distributed computer games, etc. SIP is not the only protocol that the communicating devices will need. It is not meant to be a general purpose protocol. Purpose of SIP is just to make the communication possible, the communication itself must be achieved by another means (and possibly another protocol). Two protocols that are most often used along with SIP are RTP and SDP. RTP protocol is used to carry the real-time multimedia data (including audio, video, and text), the protocol makes it possible to encode and split the data into packets and transport such packets over the Internet. Another important protocol is SDP, which is used to describe and encode capabilities of session participants. Such a description is then used to negotiate the characteristics of the session so that all the devices can participate (that includes, for example, negotiation of codecs used to encode media so all the participants will be able to decode it, negotiation of transport protocol used and so on). SIP has been designed in conformance with the Internet model. It is an end-to-end oriented signaling protocol which means, that all the logic is stored in end devices (except routing of SIP messages). State is also stored in end-devices only, there is no single point of failure and networks designed this way scale well. The price that we have to pay for the distributiveness and scalability is higher message overhead, caused by the messages being sent end-to-end. It is worth of mentioning that the end-to-end concept of SIP is a significant divergence from regular PSTN (Public Switched Telephone Network) where all the state and logic is stored in the network and end devices (telephones) are very primitive. Aim of SIP is to provide the same functionality that the traditional PSTNs have, but the end-to-end design makes SIP networks much more powerful and open to the implementation of new services that can be hardly implemented in the traditional PSTNs. SIP is based on HTTP protocol. The HTTP protocol inherited format of message headers from RFC822. HTTP is probably the most successful and widely used protocol in the Internet. It tries to combine the best of the both. In fact, HTTP can be classified as a signaling protocol too, because user agents use the protocol to tell a HTTP server in which documents they are interested in. SIP is used to carry the description of session parameters, the description is encoded into a document using SDP. Both protocols (HTTP and SIP) have inherited encoding of message headers from RFC822. The encoding has proven to be robust and flexible over the years.
SIP URI SIP entities are identified using SIP URI (Uniform Resource Identifier). A SIP URI has form of sip:username@domain, for instance, sip:joe@company.com. As we can see, SIP URI consists of username part and domain name part delimited by @ (at) character. SIP URIs are similar to e-mail addresses, it is, for instance, possible to use the same URI for e-mail and SIP communication, such URIs are easy to remember.
SIP Network Elements Although in the simplest configuration it is possible to use just two user agents that send SIP messages directly to each other, a typical SIP network will contain more than one type of SIP elements. Basic SIP elements are user agents, proxies, registrars, and redirect servers. We will briefly describe them in this section. Note that the elements, as presented in this section, are often only logical entities. It is often profitable to co-locate them together, for instance, to increase the speed of processing, but that depends on a particular implementation and configuration.
User Agents Internet end points that use SIP to find each other and to negotiate a session characteristics are called user agents. User agents usually, but not necessarily, reside on a user's computer in form of an application--this is currently the most widely used approach, but user agents can be also cellular phones, PSTN gateways, PDAs, automated IVR systems and so on. User agents are often referred to as User Agent Server (UAS) and User Agent Client (UAC). UAS and UAC are logical entities only, each user agent contains a UAC and UAS. UAC is the part of the user agent that sends requests and receives responses. UAS is the part of the user agent that receives requests and sends responses. Because a user agent contains both UAC and UAS, we often say that a user agent behaves like a UAC or UAS. For instance, caller's user agent behaves like UAC when it sends an INVITE requests and receives responses to the request. Callee's user agent behaves like a UAS when it receives the INVITE and sends responses. But this situation changes when the callee decides to send a BYE and terminate the session. In this case the callee's user agent (sending BYE) behaves like UAC and the caller's user agent behaves like UAS.
UAC and UAS Picture showing UAC and UAS
shows three user agents and one stateful forking proxy. Each user agent contains UAC and UAS. The part of the proxy that receives the INVITE from the caller in fact acts as a UAS. When forwarding the request statefully the proxy creates two UACs, each of them is responsible for one branch. In our example callee B picked up and later when he wants to tear down the call it sends a BYE. At this time the user agent that was previously UAS becomes a UAC and vice versa.
Proxy Servers In addition to that SIP allows creation of an infrastructure of network hosts called proxy servers. User agents can send messages to a proxy server. Proxy servers are very important entities in the SIP infrastructure. They perform routing of a session invitations according to invitee's current location, authentication, accounting and many other important functions. The most important task of a proxy server is to route session invitations "closer" to callee. The session invitation will usually traverse a set of proxies until it finds one which knows the actual location of the callee. Such a proxy will forward the session invitation directly to the callee and the callee will then accept or decline the session invitation. There are two basic types of SIP proxy servers--stateless and stateful.
Stateless Servers Stateless server are simple message forwarders. They forward messages independently of each other. Although messages are usually arranged into transactions (see ), stateless proxies do not take care of transactions. Stateless proxies are simple, but faster than stateful proxy servers. They can be used as simple load balancers, message translators and routers. One of drawbacks of stateless proxies is that they are unable to absorb retransmissions of messages and perform more advanced routing, for instance, forking or recursive traversal.
Stateful Servers Stateful proxies are more complex. Upon reception of a request, stateful proxies create a state and keep the state until the transaction finishes. Some transactions, especially those created by INVITE, can last quite long (until callee picks up or declines the call). Because stateful proxies must maintain the state for the duration of the transactions, their performance is limited. The ability to associate SIP messages into transactions gives stateful proxies some interesting features. Stateful proxies can perform forking, that means upon reception of a message two or more messages will be sent out. Stateful proxies can absorb retransmissions because they know, from the transaction state, if they have already received the same message (stateless proxies cannot do the check because they keep no state). Stateful proxies can perform more complicated methods of finding a user. It is, for instance, possible to try to reach user's office phone and when he doesn't pick up then the call is redirected to his cell phone. Stateless proxies can't do this because they have no way of knowing how the transaction targeted to the office phone finished. Most SIP proxies today are stateful because their configuration is usually very complex. They often perform accounting, forking, some sort of NAT traversal aid and all those features require a stateful proxy.
Proxy Server Usage A typical configuration is that each centrally administered entity (a company, for instance) has it's own SIP proxy server which is used by all user agents in the entity. Let's suppose that there are two companies A and B and each of them has it's own proxy server. shows how a session invitation from employee Joe in company A will reach employee Bob in company B.
Session Invitation Picture showing a session invitation message flow
User Joe uses address sip:bob@b.com to call Bob. Joe's user agent doesn't know how to route the invitation itself but it is configured to send all outbound traffic to the company SIP proxy server proxy.a.com. The proxy server figures out that user sip:bob@b.com is in a different company so it will look up B's SIP proxy server and send the invitation there. B's proxy server can be either pre-configured at proxy.a.com or the proxy will use DNS SRV records to find B's proxy server. The invitation reaches proxy.bo.com. The proxy knows that Bob is currently sitting in his office and is reachable through phone on his desk, which has IP address 1.2.3.4, so the proxy will send the invitation there.
Registrar We mentioned that the SIP proxy at proxy.b.com knows current Bob's location but haven't mentioned yet how a proxy can learn current location of a user. Bob's user agent (SIP phone) must register with a registrar. The registrar is a special SIP entity that receives registrations from users, extracts information about their current location (IP address, port and username in this case) and stores the information into location database. Purpose of the location database is to map sip:bob@b.com to something like sip:bob@1.2.3.4:5060. The location database is then used by B's proxy server. When the proxy receives an invitation for sip:bob@b.com it will search the location database. It finds sip:bob@1.2.3.4:5060 and will send the invitation there. A registrar is very often a logical entity only. Because of their tight coupling with proxies registrars, are usually co-located with proxy servers. shows a typical SIP registration. A REGISTER message containing Address of Record sip:jan@iptel.org and contact address sip:jan@1.2.3.4:5060 where 1.2.3.4 is IP address of the phone, is sent to the registrar. The registrar extracts this information and stores it into the location database. If everything went well then the registrar sends a 200 OK response to the phone and the process of registration is finished.
Registrar Overview Picture showing a typical registrar
Each registration has a limited lifespan. Expires header field or expires parameter of Contact header field determines for how long is the registration valid. The user agent must refresh the registration within the lifespan otherwise it will expire and the user will become unavailable.
Redirect Server The entity that receives a request and sends back a reply containing a list of the current location of a particular user is called redirect server. A redirect server receives requests and looks up the intended recipient of the request in the location database created by a registrar. It then creates a list of current locations of the user and sends it to the request originator in a response within 3xx class. The originator of the request then extracts the list of destinations and sends another request directly to them. shows a typical redirection.
SIP Redirection Picture showing a redirection
SIP Messages Communication using SIP (often called signaling) comprises of series of messages. Messages can be transported independently by the network. Usually they are transported in a separate UDP datagram each. Each message consist of "first line", message header, and message body. The first line identifies type of the message. There are two types of messages--requests and responses. Requests are usually used to initiate some action or inform recipient of the request of something. Replies are used to confirm that a request was received and processed and contain the status of the processing. A typical SIP request looks like this: ;tag=76ff7a07-c091-4192-84a0-d56e91fe104f To: Call-ID: d10815e0-bf17-4afa-8412-d9130a793d96@213.20.128.35 CSeq: 2 INVITE Contact: User-Agent: Windows RTC/1.0 Proxy-Authorization: Digest username="jiri", realm="iptel.org", algorithm="MD5", uri="sip:jiri@bat.iptel.org", nonce="3cef753900000001771328f5ae1b8b7f0d742da1feb5753c", response="53fe98db10e1074 b03b3e06438bda70f" Content-Type: application/sdp Content-Length: 451 v=0 o=jku2 0 0 IN IP4 213.20.128.35 s=session c=IN IP4 213.20.128.35 b=CT:1000 t=0 0 m=audio 54742 RTP/AVP 97 111 112 6 0 8 4 5 3 101 a=rtpmap:97 red/8000 a=rtpmap:111 SIREN/16000 a=fmtp:111 bitrate=16000 a=rtpmap:112 G7221/16000 a=fmtp:112 bitrate=24000 a=rtpmap:6 DVI4/16000 a=rtpmap:0 PCMU/8000 a=rtpmap:4 G723/8000 a=rtpmap: 3 GSM/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-16 ]]> The first line tells us that this is INVITE message which is used to establish a session. The URI on the first line--sip:7170@iptel.org is called Request URI and contains URI of the next hop of the message. In this case it will be host iptel.org. A SIP request can contain one or more Via header fields which are used to record path of the request. They are later used to route SIP responses exactly the same way. The INVITE message contains just one Via header field which was created by the user agent that sent the request. From the Via field we can tell that the user agent is running on host 195.37.77.100 and port 5060. From and To header fields identify initiator (caller) and recipient (callee) of the invitation (just like in SMTP where they identify sender and recipient of a message). From header field contains a tag parameter which serves as a dialog identifier and will be described in . Call-ID header field is a dialog identifier and it's purpose is to identify messages belonging to the same call. Such messages have the same Call-ID identifier. CSeq is used to maintain order of requests. Because requests can be sent over an unreliable transport that can re-order messages, a sequence number must be present in the messages so that recipient can identify retransmissions and out of order requests. Contact header field contains IP address and port on which the sender is awaiting further requests sent by callee. Other header fields are not important and will be not described here. Message header is delimited from message body by an empty line. Message body of the INVITE request contains a description of the media type accepted by the sender and encoded in SDP.
SIP Requests We have described how an INVITE request looks like and said that the request is used to invite a callee to a session. Other important requests are: ACK--This message acknowledges receipt of a final response to INVITE. Establishing of a session utilizes 3-way hand-shaking due to asymmetric nature of the invitation. It may take a while before the callee accepts or declines the call so the callee's user agent periodically retransmits a positive final response until it receives an ACK (which indicates that the caller is still there and ready to communicate). BYE--Bye messages are used to tear down multimedia sessions. A party wishing to tear down a session sends a BYE to the other party. CANCEL--Cancel is used to cancel not yet fully established session. It is used when the callee hasn't replied with a final response yet but the caller wants to abort the call (typically when a callee doesn't respond for some time). REGISTER--Purpose of REGISTER request is to let registrar know of current user's location. Information about current IP address and port on which a user can be reached is carried in REGISTER messages. Registrar extracts this information and puts it into a location database. The database can be later used by SIP proxy servers to route calls to the user. Registrations are time-limited and need to be periodically refreshed. The listed requests usually have no message body because it is not needed in most situations (but can have one). In addition to that many other request types have been defined but their description is out of the scope of this document.
SIP Responses When a user agent or proxy server receives a request it send a reply. Each request must be replied except ACK requests which trigger no replies. A typical reply looks like this: ;q=0.00;expires=120 Server: Sip EXpress router (0.8.11pre21xrc (i386/linux)) Content-Length: 0 Warning: 392 195.37.77.101:5060 "Noisy feedback tells: pid=5110 req_src_ip=66.87.48.68 req_src_port=5060 in_uri=sip:iptel.org out_uri=sip:iptel.org via_cnt==1" ]]> As we can see, responses are very similar to the requests, except for the first line. The first line of response contains protocol version (SIP/2.0), reply code, and reason phrase. The reply code is an integer number from 100 to 699 and indicates type of the response. There are 6 classes of responses: 1xx are provisional responses. A provisional response is response that tells to its recipient that the associated request was received but result of the processing is not known yet. Provisional responses are sent only when the processing doesn't finish immediately. The sender must stop retransmitting the request upon reception of a provisional response. Typically proxy servers send responses with code 100 when they start processing an INVITE and user agents send responses with code 180 (Ringing) which means that the callee's phone is ringing. 2xx responses are positive final responses. A final response is the ultimate response that the originator of the request will ever receive. Therefore final responses express result of the processing of the associated request. Final responses also terminate transactions. Responses with code from 200 to 299 are positive responses that means that the request was processed successfully and accepted. For instance a 200 OK response is sent when a user accepts invitation to a session (INVITE request). A UAC may receive several 200 messages to a single INVITE request. This is because a forking proxy (described later) can fork the request so it will reach several UAS and each of them will accept the invitation. In this case each response is distinguished by the tag parameter in To header field. Each response represents a distinct dialog with unambiguous dialog identifier. 3xx responses are used to redirect a caller. A redirection response gives information about the user's new location or an alternative service that the caller might use to satisfy the call. Redirection responses are usually sent by proxy servers. When a proxy receives a request and doesn't want or can't process it for any reason, it will send a redirection response to the caller and put another location into the response which the caller might want to try. It can be the location of another proxy or the current location of the callee (from the location database created by a registrar). The caller is then supposed to re-send the request to the new location. 3xx responses are final. 4xx are negative final responses. a 4xx response means that the problem is on the sender's side. The request couldn't be processed because it contains bad syntax or cannot be fulfilled at that server. 5xx means that the problem is on server's side. The request is apparently valid but the server failed to fulfill it. Clients should usually retry the request later. 6xx reply code means that the request cannot be fulfilled at any server. This response is usually sent by a server that has definitive information about a particular user. User agents usually send a 603 Decline response when the user doesn't want to participate in the session. In addition to the response class the first line also contains reason phrase. The code number is intended to be processed by machines. It is not very human-friendly but it is very easy to parse and understand by machines. The reason phrase usually contains a human-readable message describing the result of the processing. A user agent should render the reason phrase to the user. The request to which a particular response belongs is identified using the CSeq header field. In addition to the sequence number this header field also contains method of corresponding request. In our example it was REGISTER request.
SIP Transactions Although we said that SIP messages are sent independently over the network, they are usually arranged into transactions by user agents and certain types of proxy servers. Therefore SIP is said to be a transactional protocol. A transaction is a sequence of SIP messages exchanged between SIP network elements. A transaction consists of one request and all responses to that request. That includes zero or more provisional responses and one or more final responses (remember that an INVITE might be answered by more than one final response when a proxy server forks the request). If a transaction was initiated by an INVITE request then the same transaction also includes ACK, but only if the final response was not a 2xx response. If the final response was a 2xx response then the ACK is not considered part of the transaction. As we can see this is quite asymmetric behavior--ACK is part of transactions with a negative final response but is not part of transactions with positive final responses. The reason for this separation is the importance of delivery of all 200 OK messages. Not only that they establish a session, but also 200 OK can be generated by multiple entities when a proxy server forks the request and all of them must be delivered to the calling user agent. Therefore user agents take responsibility in this case and retransmit 200 OK responses until they receive an ACK. Also note that only responses to INVITE are retransmitted ! SIP entities that have notion of transactions are called stateful. Such entities usually create a state associated with a transaction that is kept in the memory for the duration of the transaction. When a request or response comes, a stateful entity tries to associate the request (or response) to existing transactions. To be able to do it it must extract a unique transaction identifier from the message and compare it to identifiers of all existing transactions. If such a transaction exists then it's state gets updated from the message. In the previous SIP RFC2543 the transaction identifier was calculated as hash of all important message header fields (that included To, From, Request-URI and CSeq). This proved to be very slow and complex, during interoperability tests such transaction identifiers used to be a common source of problems. In the new RFC3261 the way of calculating transaction identifiers was completely changed. Instead of complicated hashing of important header fields a SIP message now includes the identifier directly. Branch parameter of Via header fields contains directly the transaction identifier. This is significant simplification, but there still exist old implementations that don't support the new way of calculating of transaction identifier so even new implementations have to support the old way. They must be backwards compatible. shows what messages belong to what transactions during a conversation of two user agents.
SIP Transactions Message flow showing messages belonging to the same transaction.
SIP Dialogs We have shown what transactions are, that one transaction includes INVITE and it's responses and another transaction includes BYE and it responses when a session is being torn down. But we feel that those two transactions should be somehow related--both of them belong to the same dialog. A dialog represents a peer-to-peer SIP relationship between two user agents. A dialog persists for some time and it is very important concept for user agents. Dialogs facilitate proper sequencing and routing of messages between SIP endpoints. Dialogs are identified using Call-ID, From tag, and To tag. Messages that have these three identifiers same belong to the same dialog. We have shown that CSeq header field is used to order messages, in fact it is used to order messages within a dialog. The number must be monotonically increased for each message sent within a dialog otherwise the peer will handle it as out of order request or retransmission. In fact, the CSeq number identifies a transaction within a dialog because we have said that requests and associated responses are called transaction. This means that only one transaction in each direction can be active within a dialog. One could also say that a dialog is a sequence of transactions. extends to show which messages belong to the same dialog.
SIP Dialog Message flow showing transactions belonging to the same dialog.
Some messages establish a dialog and some do not. This allows to explicitly express the relationship of messages and also to send messages that are not related to other messages outside a dialog. That is easier to implement because user agent don't have to keep the dialog state. For instance, INVITE message establishes a dialog, because it will be later followed by BYE request which will tear down the session established by the INVITE. This BYE is sent within the dialog established by the INVITE. But if a user agent sends a MESSAGE request, such a request doesn't establish any dialog. Any subsequent messages (even MESSAGE) will be sent independently of the previous one.
Dialogs Facilitate Routing We have said that dialogs are also used to route the messages between user agents, let's describe this a little bit. Let's suppose that user sip:bob@a.com wants to talk to user sip:pete@b.com. He knows SIP address of the callee (sip:pete@b.com) but this address doesn't say anything about current location of the user--i.e. the caller doesn't know to which host to send the request. Therefore the INVITE request will be sent to a proxy server. The request will be sent from proxy to proxy until it reaches one that knows current location of the callee. This process is called routing. Once the request reaches the callee, the callee's user agent will create a response that will be sent back to the caller. Callee's user agent will also put Contact header field into the response which will contain the current location of the user. The original request also contained Contact header field which means that both user agents know the current location of the peer. Because the user agents know location of each other, it is not necessary to send further requests to any proxy--they can be sent directly from user agent to user agent. That's exactly how dialogs facilitate routing. Further messages within a dialog are sent directly from user agent to user agent. This is a significant performance improvement because proxies do not see all the messages within a dialog, they are used to route just the first request that establishes the dialog. The direct messages are also delivered with much smaller latency because a typical proxy usually implements complex routing logic. contains an example of a message within a dialog (BYE) that bypasses the proxies.
SIP Trapezoid Message flow showing SIP trapezoid.
Dialog Identifiers We have already shown that dialog identifiers consist of three parts, Call-Id, From tag, and To tag, but it is not that clear why are dialog identifiers created exactly this way and who contributes which part. Call-ID is so called call identifier. It must be a unique string that identifies a call. A call consists of one or more dialogs. Multiple user agents may respond to a request when a proxy along the path forks the request. Each user agent that sends a 2xx establishes a separate dialog with the caller. All such dialogs are part of the same call and have the same Call-ID. From tag is generated by the caller and it uniquely identifies the dialog in the caller's user agent. To tag is generated by a callee and it uniquely identifies, just like From tag, the dialog in the callee's user agent. This hierarchical dialog identifier is necessary because a single call invitation can create several dialogs and caller must be able to distinguish them.
Typical SIP Scenarios This section gives a brief overview of typical SIP scenarios that usually make up the SIP traffic.
Registration Users must register themselves with a registrar to be reachable by other users. A registration comprises a REGISTER message followed by a 200 OK sent by registrar if the registration was successful. Registrations are usually authorized so a 407 reply can appear if the user didn't provide valid credentials. shows an example of registration.
REGISTER Message Flow Message flow of a registration.
Session Invitation A session invitation consists of one INVITE request which is usually sent to a proxy. The proxy sends immediately a 100 Trying reply to stop retransmissions and forwards the request further. All provisional responses generated by callee are sent back to the caller. See 180 Ringing response in the call flow. The response is generated when callee's phone starts ringing.
INVITE Message Flow Picture showing a session invitation.
A 200 OK is generated once the callee picks up the phone and it is retransmitted by the callee's user agent until it receives an ACK from the caller. The session is established at this point.
Session Termination Session termination is accomplished by sending a BYE request within dialog established bye INVITE. BYE messages are sent directly from one user agent to the other unless a proxy on the path of the INVITE request indicated that it wishes to stay on the path by using record routing (see . Party wishing to tear down a session sends a BYE request to the other party involved in the session. The other party sends a 200 OK response to confirm the BYE and the session is terminated. See , left message flow.
Record Routing All requests sent within a dialog are by default sent directly from one user agent to the other. Only requests outside a dialog traverse SIP proxies. This approach makes SIP network more scalable because only a small number of SIP messages hit the proxies. There are certain situations in which a SIP proxy need to stay on the path of all further messages. For instance, proxies controlling a NAT box or proxies doing accounting need to stay on the path of BYE requests. Mechanism by which a proxy can inform user agents that it wishes to stay on the path of all further messages is called record routing. Such a proxy would insert Record-Route header field into SIP messages which contain address of the proxy. Messages sent within a dialog will then traverse all SIP proxies that put a Record-Route header field into the message. The recipient of the request receives a set of Record-Route header fields in the message. It must mirror all the Record-Route header fields into responses because the originator of the request also needs to know the set of proxies.
BYE Message Flow (With and without Record Routing) Picture showing BYE message flow with and without record routing.
Left message flow of show how a BYE (request within dialog established by INVITE) is sent directly to the other user agent when there is no Record-Route header field in the message. Right message flow show how the situation changes when the proxy puts a Record-Route header field into the message.
Strict versus Loose Routing The way how record routing works has evolved. Record routing according to RFC2543 rewrote the Request-URI. That means the Request-URI always contained URI of the next hop (which can be either next proxy server which inserted Record-Route header field or destination user agent). Because of that it was necessary to save the original Request-URI as the last Route header field. This approach is called strict routing. Loose routing, as specified in RFC3261, works in a little bit different way. The Request-URI is no more overwritten, it always contains URI of the destination user agent. If there are any Route header field in a message, than the message is sent to the URI from the topmost Route header field. This is significant change--Request-URI doesn't necessarily contain URI to which the request will be sent. In fact, loose routing is very similar to IP source routing. Because transit from strict routing to loose routing would break backwards compatibility and older user agents wouldn't work, it is necessary to make loose routing backwards compatible. The backwards compatibility unfortunately adds a lot of overhead and is often source of major problems.
Event Subscription And Notification The SIP specification has been extended to support a general mechanism allowing subscription to asynchronous events. Such evens can include SIP proxy statistics changes, presence information, session changes and so on. The mechanism is used mainly to convey information on presence (willingness to communicate) of users. shows the basic message flow.
Event Subscription And Notification Picture showing subscription and notification.
A user agent interested in event notification sends a SUBSCRIBE message to a SIP server. The SUBSCRIBE message establishes a dialog and is immediately replied by the server using 200 OK response. At this point the dialog is established. The server sends a NOTIFY request to the user every time the event to which the user subscribed changes. NOTIFY messages are sent within the dialog established by the SUBSCRIBE. Note that the first NOTIFY message in is sent regardless of any event that triggers notifications. Subscriptions--as well as registrations--have limited lifespan and therefore must be periodically refreshed.
Instant Messages Instant messages are sent using MESSAGE request. MESSAGE requests do not establish a dialog and therefore they will always traverse the same set of proxies. This is the simplest form of sending instant messages. The text of the instant message is transported in the body of the SIP request.
Instant Messages Picture showing a MESSAGE.
kamailio-4.0.4/doc/sip/figures/0000755000000000000000000000000012223032460015001 5ustar rootrootkamailio-4.0.4/doc/sip/figures/companies.eps0000644000000000000000000024722412223032460017503 0ustar rootroot%!PS-Adobe-2.0 EPSF-2.0 %%Title: companies.dia %%Creator: Dia v0.91 %%CreationDate: Tue Jul 15 14:58:53 2003 %%For: janakj %%Orientation: Portrait %%Magnification: 1.0000 %%BoundingBox: 0 0 823 492 %%BeginSetup %%EndSetup %%EndComments %%BeginProlog [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def /cp {closepath} bind def /c {curveto} bind def /f {fill} bind def /a {arc} bind def /ef {eofill} bind def /ex {exch} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth pop} bind def /tr {translate} bind def /ellipsedict 8 dict def ellipsedict /mtrx matrix put /ellipse { ellipsedict begin /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc savematrix setmatrix end } def /mergeprocs { dup length 3 -1 roll dup length dup 5 1 roll 3 -1 roll add array cvx dup 3 -1 roll 0 exch putinterval dup 4 2 roll putinterval } bind def /dpi_x 300 def /dpi_y 300 def /conicto { /to_y exch def /to_x exch def /conic_cntrl_y exch def /conic_cntrl_x exch def currentpoint /p0_y exch def /p0_x exch def /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def /p2_x p1_x to_x p0_x sub 1 3 div mul add def /p2_y p1_y to_y p0_y sub 1 3 div mul add def p1_x p1_y p2_x p2_y to_x to_y curveto } bind def /start_ol { gsave 1.1 dpi_x div dup scale} bind def /end_ol { closepath fill grestore } bind def 28.346000 -28.346000 scale -2.450000 -19.000000 translate %%EndProlog 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 5.535332 11.195953 m 4.485682 11.158791 2.450000 11.939197 2.736268 13.611496 c 3.022533 15.283795 4.390259 15.655402 4.962796 15.172308 c 5.535332 14.689200 4.072183 17.513512 6.871250 18.256756 c 9.670292 19.000000 11.101633 17.810810 10.688134 16.956079 c 10.274636 16.101348 13.137318 18.962838 14.473236 17.327701 c 15.809155 15.692564 13.105510 14.131767 13.678047 14.354740 c 14.250583 14.577713 16.000000 14.280415 15.427464 11.493250 c 14.854927 8.706086 9.702099 10.861493 10.274636 10.452709 c 10.847172 10.043925 9.415831 8.000000 7.634632 8.408784 c 5.853408 8.817572 5.727068 9.559367 5.536223 11.194504 c 5.535332 11.195953 l f 0.000000 0.000000 0.000000 srgb n 5.535332 11.195953 m 4.485682 11.158791 2.450000 11.939197 2.736268 13.611496 c 3.022533 15.283795 4.390259 15.655402 4.962796 15.172308 c 5.535332 14.689200 4.072183 17.513512 6.871250 18.256756 c 9.670292 19.000000 11.101633 17.810810 10.688134 16.956079 c 10.274636 16.101348 13.137318 18.962838 14.473236 17.327701 c 15.809155 15.692564 13.105510 14.131767 13.678047 14.354740 c 14.250583 14.577713 16.000000 14.280415 15.427464 11.493250 c 14.854927 8.706086 9.702099 10.861493 10.274636 10.452709 c 10.847172 10.043925 9.415831 8.000000 7.634632 8.408784 c 5.853408 8.817572 5.727068 9.559367 5.536223 11.194504 c 5.535332 11.195953 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 6.493149 11.000000 m 6.717307 11.000000 8.062259 11.000000 8.286417 11.000000 c 8.510576 11.000000 8.757150 11.224159 8.734734 11.672476 c 8.510576 11.672476 8.510576 11.672476 8.286417 11.672476 c 8.297625 11.246574 7.613941 11.448317 7.389783 11.448317 c 7.165624 11.448317 6.493149 11.235367 6.493149 11.672476 c 6.268990 11.672476 6.268990 11.672476 6.044832 11.672476 c 6.000000 11.224159 6.268990 11.000000 6.493149 11.000000 c f 0.000000 0.000000 0.000000 srgb n 6.493149 11.000000 m 6.717307 11.000000 8.062259 11.000000 8.286417 11.000000 c 8.510576 11.000000 8.757150 11.224159 8.734734 11.672476 c 8.510576 11.672476 8.510576 11.672476 8.286417 11.672476 c 8.297625 11.246574 7.613941 11.448317 7.389783 11.448317 c 7.165624 11.448317 6.493149 11.235367 6.493149 11.672476 c 6.268990 11.672476 6.268990 11.672476 6.044832 11.672476 c 6.000000 11.224159 6.268990 11.000000 6.493149 11.000000 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 6.493149 11.000000 m 6.717307 11.000000 8.062259 11.000000 8.286417 11.000000 c 8.510576 11.000000 8.757150 11.224159 8.734734 11.672476 c 8.510576 11.672476 8.510576 11.672476 8.286417 11.672476 c 8.297625 11.246574 7.613941 11.448317 7.389783 11.448317 c 7.165624 11.448317 6.493149 11.235367 6.493149 11.672476 c 6.268990 11.672476 6.268990 11.672476 6.044832 11.672476 c 6.000000 11.224159 6.268990 11.000000 6.493149 11.000000 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 7.389783 11.224159 m 8.286417 11.224159 8.286417 12.569110 8.062259 12.569110 c 7.838100 12.569110 6.941466 12.569110 6.717307 12.569110 c 6.493149 12.569110 6.493149 11.224159 7.389783 11.224159 c f 0.000000 0.000000 0.000000 srgb n 7.389783 11.224159 m 8.286417 11.224159 8.286417 12.569110 8.062259 12.569110 c 7.838100 12.569110 6.941466 12.569110 6.717307 12.569110 c 6.493149 12.569110 6.493149 11.224159 7.389783 11.224159 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 7.389783 11.224159 m 8.286417 11.224159 8.286417 12.569110 8.062259 12.569110 c 7.838100 12.569110 6.941466 12.569110 6.717307 12.569110 c 6.493149 12.569110 6.493149 11.224159 7.389783 11.224159 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 7.389783 11.896634 0.448317 0.448317 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 7.389783 11.896634 0.448317 0.448317 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 7.389783 11.896634 0.448317 0.448317 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 7.120793 12.092773 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 7.120793 12.092773 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 7.120793 12.092773 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 7.030008 11.879822 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 7.030008 11.879822 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 7.030008 11.879822 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 7.097256 11.678080 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 7.097256 11.678080 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 7.097256 11.678080 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 7.298999 11.577208 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 7.298999 11.577208 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 7.298999 11.577208 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 7.500741 11.588416 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 7.500741 11.588416 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 7.500741 11.588416 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 7.657652 11.722911 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 7.657652 11.722911 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 7.657652 11.722911 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 7.724900 11.879822 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 7.724900 11.879822 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 7.724900 11.879822 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 7.668860 12.070357 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 7.668860 12.070357 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 7.668860 12.070357 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 7.511949 12.193644 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 7.511949 12.193644 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 7.511949 12.193644 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 7.493149 15.000000 m 7.717307 15.000000 9.062259 15.000000 9.286417 15.000000 c 9.510576 15.000000 9.757150 15.224159 9.734734 15.672476 c 9.510576 15.672476 9.510576 15.672476 9.286417 15.672476 c 9.297625 15.246574 8.613941 15.448317 8.389783 15.448317 c 8.165624 15.448317 7.493149 15.235367 7.493149 15.672476 c 7.268990 15.672476 7.268990 15.672476 7.044832 15.672476 c 7.000000 15.224159 7.268990 15.000000 7.493149 15.000000 c f 0.000000 0.000000 0.000000 srgb n 7.493149 15.000000 m 7.717307 15.000000 9.062259 15.000000 9.286417 15.000000 c 9.510576 15.000000 9.757150 15.224159 9.734734 15.672476 c 9.510576 15.672476 9.510576 15.672476 9.286417 15.672476 c 9.297625 15.246574 8.613941 15.448317 8.389783 15.448317 c 8.165624 15.448317 7.493149 15.235367 7.493149 15.672476 c 7.268990 15.672476 7.268990 15.672476 7.044832 15.672476 c 7.000000 15.224159 7.268990 15.000000 7.493149 15.000000 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 7.493149 15.000000 m 7.717307 15.000000 9.062259 15.000000 9.286417 15.000000 c 9.510576 15.000000 9.757150 15.224159 9.734734 15.672476 c 9.510576 15.672476 9.510576 15.672476 9.286417 15.672476 c 9.297625 15.246574 8.613941 15.448317 8.389783 15.448317 c 8.165624 15.448317 7.493149 15.235367 7.493149 15.672476 c 7.268990 15.672476 7.268990 15.672476 7.044832 15.672476 c 7.000000 15.224159 7.268990 15.000000 7.493149 15.000000 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 8.389783 15.224159 m 9.286417 15.224159 9.286417 16.569110 9.062259 16.569110 c 8.838100 16.569110 7.941466 16.569110 7.717307 16.569110 c 7.493149 16.569110 7.493149 15.224159 8.389783 15.224159 c f 0.000000 0.000000 0.000000 srgb n 8.389783 15.224159 m 9.286417 15.224159 9.286417 16.569110 9.062259 16.569110 c 8.838100 16.569110 7.941466 16.569110 7.717307 16.569110 c 7.493149 16.569110 7.493149 15.224159 8.389783 15.224159 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 8.389783 15.224159 m 9.286417 15.224159 9.286417 16.569110 9.062259 16.569110 c 8.838100 16.569110 7.941466 16.569110 7.717307 16.569110 c 7.493149 16.569110 7.493149 15.224159 8.389783 15.224159 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 8.389783 15.896634 0.448317 0.448317 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 8.389783 15.896634 0.448317 0.448317 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 8.389783 15.896634 0.448317 0.448317 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 8.120793 16.092773 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 8.120793 16.092773 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 8.120793 16.092773 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 8.030008 15.879822 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 8.030008 15.879822 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 8.030008 15.879822 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 8.097256 15.678080 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 8.097256 15.678080 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 8.097256 15.678080 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 8.298999 15.577208 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 8.298999 15.577208 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 8.298999 15.577208 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 8.500741 15.588416 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 8.500741 15.588416 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 8.500741 15.588416 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 8.657652 15.722911 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 8.657652 15.722911 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 8.657652 15.722911 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 8.724900 15.879822 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 8.724900 15.879822 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 8.724900 15.879822 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 8.668860 16.070357 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 8.668860 16.070357 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 8.668860 16.070357 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 8.511949 16.193644 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 8.511949 16.193644 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 8.511949 16.193644 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0.080000 slw 0 slc 0 slj [] 0 sd 0.701961 0.701961 0.701961 srgb n 14.215312 9.999998 m 14.215312 12.511961 l 15.291868 12.511961 l 15.291868 9.999998 l f 0.000000 0.000000 0.000000 srgb n 14.215312 9.999998 m 14.215312 12.511961 l 15.291868 12.511961 l 15.291868 9.999998 l cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 14.322968 10.150716 m 14.322968 10.437797 l 15.184212 10.437797 l 15.184212 10.150716 l cp s 0 slc 0 slj [] 0 sd n 14.322968 10.437797 m 14.322968 10.724879 l 15.184212 10.724879 l 15.184212 10.437797 l cp s 0 slc 0 slj [] 0 sd n 14.322968 10.724879 m 14.322968 11.011960 l 15.184212 11.011960 l 15.184212 10.724879 l cp s 0 slc 0 slj [] 0 sd n 14.322968 11.011960 m 14.322968 11.299042 l 15.184212 11.299042 l 15.184212 11.011960 l cp s 0 slc 0 slj [] 0 sd n 14.322968 11.356458 m 14.322968 11.528707 l 14.861245 11.528707 l 14.861245 11.356458 l cp s 0 slc 0 slj [] 0 sd 0.000000 1.000000 0.000000 srgb n 15.130384 11.385166 0.037679 0.037679 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 15.130384 11.385166 0.037679 0.037679 0 360 ellipse cp s 0 slc 0 slj [] 0 sd 1.000000 1.000000 0.000000 srgb n 15.130384 11.499999 0.037679 0.037679 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 15.130384 11.499999 0.037679 0.037679 0 360 ellipse cp s 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 14.915073 11.413874 m 14.915073 11.528707 l 15.044260 11.528707 l 15.044260 11.413874 l f 0.000000 0.000000 0.000000 srgb n 14.915073 11.413874 m 14.915073 11.528707 l 15.044260 11.528707 l 15.044260 11.413874 l cp s 0 slc 0 slj [] 0 sd n 14.394738 11.758372 m 14.394738 12.386363 l s 0 slc 0 slj [] 0 sd n 14.574164 11.758372 m 14.574164 12.386363 l s 0 slc 0 slj [] 0 sd n 14.753590 11.758372 m 14.753590 12.386363 l s 0 slc 0 slj [] 0 sd n 14.933016 11.758372 m 14.933016 12.386363 l s 0 slc 0 slj [] 0 sd n 15.112442 11.758372 m 15.112442 12.386363 l s 0 slc 0 slj [] 0 sd n 15.291868 11.758372 m 15.291868 12.386363 l s 0 slc 0 slj [] 0 sd 0.600000 0.600000 0.600000 srgb n 14.000001 12.727272 m 14.215312 12.296650 l 14.215312 12.511961 l 15.291868 12.511961 l 15.291868 12.296650 l 15.578949 12.727272 l f 0.000000 0.000000 0.000000 srgb n 14.000001 12.727272 m 14.215312 12.296650 l 14.215312 12.511961 l 15.291868 12.511961 l 15.291868 12.296650 l 15.578949 12.727272 l cp s 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 22.607162 11.195953 m 21.720189 11.158791 20.000000 11.939197 20.241902 13.611496 c 20.483801 15.283795 21.639555 15.655402 22.123359 15.172308 c 22.607162 14.689200 21.370775 17.513512 23.736038 18.256756 c 26.101280 19.000000 27.310789 17.810810 26.961375 16.956079 c 26.611962 16.101348 29.030981 18.962838 30.159856 17.327701 c 31.288732 15.692564 29.004103 14.131767 29.487907 14.354740 c 29.971710 14.577713 31.450000 14.280415 30.966196 11.493250 c 30.482392 8.706086 26.128158 10.861493 26.611962 10.452709 c 27.095765 10.043925 25.886256 8.000000 24.381110 8.408784 c 22.875942 8.817572 22.769183 9.559367 22.607915 11.194504 c 22.607162 11.195953 l f 0.000000 0.000000 0.000000 srgb n 22.607162 11.195953 m 21.720189 11.158791 20.000000 11.939197 20.241902 13.611496 c 20.483801 15.283795 21.639555 15.655402 22.123359 15.172308 c 22.607162 14.689200 21.370775 17.513512 23.736038 18.256756 c 26.101280 19.000000 27.310789 17.810810 26.961375 16.956079 c 26.611962 16.101348 29.030981 18.962838 30.159856 17.327701 c 31.288732 15.692564 29.004103 14.131767 29.487907 14.354740 c 29.971710 14.577713 31.450000 14.280415 30.966196 11.493250 c 30.482392 8.706086 26.128158 10.861493 26.611962 10.452709 c 27.095765 10.043925 25.886256 8.000000 24.381110 8.408784 c 22.875942 8.817572 22.769183 9.559367 22.607915 11.194504 c 22.607162 11.195953 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0.080000 slw 0 slc 0 slj [] 0 sd 0.701961 0.701961 0.701961 srgb n 20.215312 10.999998 m 20.215312 13.511961 l 21.291868 13.511961 l 21.291868 10.999998 l f 0.000000 0.000000 0.000000 srgb n 20.215312 10.999998 m 20.215312 13.511961 l 21.291868 13.511961 l 21.291868 10.999998 l cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 20.322968 11.150716 m 20.322968 11.437797 l 21.184212 11.437797 l 21.184212 11.150716 l cp s 0 slc 0 slj [] 0 sd n 20.322968 11.437797 m 20.322968 11.724879 l 21.184212 11.724879 l 21.184212 11.437797 l cp s 0 slc 0 slj [] 0 sd n 20.322968 11.724879 m 20.322968 12.011960 l 21.184212 12.011960 l 21.184212 11.724879 l cp s 0 slc 0 slj [] 0 sd n 20.322968 12.011960 m 20.322968 12.299042 l 21.184212 12.299042 l 21.184212 12.011960 l cp s 0 slc 0 slj [] 0 sd n 20.322968 12.356458 m 20.322968 12.528707 l 20.861245 12.528707 l 20.861245 12.356458 l cp s 0 slc 0 slj [] 0 sd 0.000000 1.000000 0.000000 srgb n 21.130384 12.385166 0.037679 0.037679 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 21.130384 12.385166 0.037679 0.037679 0 360 ellipse cp s 0 slc 0 slj [] 0 sd 1.000000 1.000000 0.000000 srgb n 21.130384 12.499999 0.037679 0.037679 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 21.130384 12.499999 0.037679 0.037679 0 360 ellipse cp s 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 20.915073 12.413874 m 20.915073 12.528707 l 21.044260 12.528707 l 21.044260 12.413874 l f 0.000000 0.000000 0.000000 srgb n 20.915073 12.413874 m 20.915073 12.528707 l 21.044260 12.528707 l 21.044260 12.413874 l cp s 0 slc 0 slj [] 0 sd n 20.394738 12.758372 m 20.394738 13.386363 l s 0 slc 0 slj [] 0 sd n 20.574164 12.758372 m 20.574164 13.386363 l s 0 slc 0 slj [] 0 sd n 20.753590 12.758372 m 20.753590 13.386363 l s 0 slc 0 slj [] 0 sd n 20.933016 12.758372 m 20.933016 13.386363 l s 0 slc 0 slj [] 0 sd n 21.112442 12.758372 m 21.112442 13.386363 l s 0 slc 0 slj [] 0 sd n 21.291868 12.758372 m 21.291868 13.386363 l s 0 slc 0 slj [] 0 sd 0.600000 0.600000 0.600000 srgb n 20.000001 13.727272 m 20.215312 13.296650 l 20.215312 13.511961 l 21.291868 13.511961 l 21.291868 13.296650 l 21.578949 13.727272 l f 0.000000 0.000000 0.000000 srgb n 20.000001 13.727272 m 20.215312 13.296650 l 20.215312 13.511961 l 21.291868 13.511961 l 21.291868 13.296650 l 21.578949 13.727272 l cp s 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 25.493149 11.000000 m 25.717307 11.000000 27.062259 11.000000 27.286417 11.000000 c 27.510576 11.000000 27.757150 11.224159 27.734734 11.672476 c 27.510576 11.672476 27.510576 11.672476 27.286417 11.672476 c 27.297625 11.246574 26.613941 11.448317 26.389783 11.448317 c 26.165624 11.448317 25.493149 11.235367 25.493149 11.672476 c 25.268990 11.672476 25.268990 11.672476 25.044832 11.672476 c 25.000000 11.224159 25.268990 11.000000 25.493149 11.000000 c f 0.000000 0.000000 0.000000 srgb n 25.493149 11.000000 m 25.717307 11.000000 27.062259 11.000000 27.286417 11.000000 c 27.510576 11.000000 27.757150 11.224159 27.734734 11.672476 c 27.510576 11.672476 27.510576 11.672476 27.286417 11.672476 c 27.297625 11.246574 26.613941 11.448317 26.389783 11.448317 c 26.165624 11.448317 25.493149 11.235367 25.493149 11.672476 c 25.268990 11.672476 25.268990 11.672476 25.044832 11.672476 c 25.000000 11.224159 25.268990 11.000000 25.493149 11.000000 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 25.493149 11.000000 m 25.717307 11.000000 27.062259 11.000000 27.286417 11.000000 c 27.510576 11.000000 27.757150 11.224159 27.734734 11.672476 c 27.510576 11.672476 27.510576 11.672476 27.286417 11.672476 c 27.297625 11.246574 26.613941 11.448317 26.389783 11.448317 c 26.165624 11.448317 25.493149 11.235367 25.493149 11.672476 c 25.268990 11.672476 25.268990 11.672476 25.044832 11.672476 c 25.000000 11.224159 25.268990 11.000000 25.493149 11.000000 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.389783 11.224159 m 27.286417 11.224159 27.286417 12.569110 27.062259 12.569110 c 26.838100 12.569110 25.941466 12.569110 25.717307 12.569110 c 25.493149 12.569110 25.493149 11.224159 26.389783 11.224159 c f 0.000000 0.000000 0.000000 srgb n 26.389783 11.224159 m 27.286417 11.224159 27.286417 12.569110 27.062259 12.569110 c 26.838100 12.569110 25.941466 12.569110 25.717307 12.569110 c 25.493149 12.569110 25.493149 11.224159 26.389783 11.224159 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.389783 11.224159 m 27.286417 11.224159 27.286417 12.569110 27.062259 12.569110 c 26.838100 12.569110 25.941466 12.569110 25.717307 12.569110 c 25.493149 12.569110 25.493149 11.224159 26.389783 11.224159 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.389783 11.896634 0.448317 0.448317 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.389783 11.896634 0.448317 0.448317 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.389783 11.896634 0.448317 0.448317 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.120793 12.092773 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.120793 12.092773 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.120793 12.092773 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.030008 11.879822 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.030008 11.879822 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.030008 11.879822 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.097256 11.678080 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.097256 11.678080 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.097256 11.678080 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.298999 11.577208 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.298999 11.577208 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.298999 11.577208 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.500741 11.588416 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.500741 11.588416 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.500741 11.588416 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.657652 11.722911 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.657652 11.722911 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.657652 11.722911 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.724900 11.879822 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.724900 11.879822 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.724900 11.879822 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.668860 12.070357 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.668860 12.070357 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.668860 12.070357 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.511949 12.193644 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.511949 12.193644 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.511949 12.193644 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 25.493149 14.400000 m 25.717307 14.400000 27.062259 14.400000 27.286417 14.400000 c 27.510576 14.400000 27.757150 14.624159 27.734734 15.072476 c 27.510576 15.072476 27.510576 15.072476 27.286417 15.072476 c 27.297625 14.646574 26.613941 14.848317 26.389783 14.848317 c 26.165624 14.848317 25.493149 14.635367 25.493149 15.072476 c 25.268990 15.072476 25.268990 15.072476 25.044832 15.072476 c 25.000000 14.624159 25.268990 14.400000 25.493149 14.400000 c f 0.000000 0.000000 0.000000 srgb n 25.493149 14.400000 m 25.717307 14.400000 27.062259 14.400000 27.286417 14.400000 c 27.510576 14.400000 27.757150 14.624159 27.734734 15.072476 c 27.510576 15.072476 27.510576 15.072476 27.286417 15.072476 c 27.297625 14.646574 26.613941 14.848317 26.389783 14.848317 c 26.165624 14.848317 25.493149 14.635367 25.493149 15.072476 c 25.268990 15.072476 25.268990 15.072476 25.044832 15.072476 c 25.000000 14.624159 25.268990 14.400000 25.493149 14.400000 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 25.493149 14.400000 m 25.717307 14.400000 27.062259 14.400000 27.286417 14.400000 c 27.510576 14.400000 27.757150 14.624159 27.734734 15.072476 c 27.510576 15.072476 27.510576 15.072476 27.286417 15.072476 c 27.297625 14.646574 26.613941 14.848317 26.389783 14.848317 c 26.165624 14.848317 25.493149 14.635367 25.493149 15.072476 c 25.268990 15.072476 25.268990 15.072476 25.044832 15.072476 c 25.000000 14.624159 25.268990 14.400000 25.493149 14.400000 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.389783 14.624159 m 27.286417 14.624159 27.286417 15.969110 27.062259 15.969110 c 26.838100 15.969110 25.941466 15.969110 25.717307 15.969110 c 25.493149 15.969110 25.493149 14.624159 26.389783 14.624159 c f 0.000000 0.000000 0.000000 srgb n 26.389783 14.624159 m 27.286417 14.624159 27.286417 15.969110 27.062259 15.969110 c 26.838100 15.969110 25.941466 15.969110 25.717307 15.969110 c 25.493149 15.969110 25.493149 14.624159 26.389783 14.624159 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.389783 14.624159 m 27.286417 14.624159 27.286417 15.969110 27.062259 15.969110 c 26.838100 15.969110 25.941466 15.969110 25.717307 15.969110 c 25.493149 15.969110 25.493149 14.624159 26.389783 14.624159 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.389783 15.296634 0.448317 0.448317 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.389783 15.296634 0.448317 0.448317 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.389783 15.296634 0.448317 0.448317 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.120793 15.492773 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.120793 15.492773 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.120793 15.492773 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.030008 15.279822 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.030008 15.279822 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.030008 15.279822 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.097256 15.078080 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.097256 15.078080 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.097256 15.078080 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.298999 14.977208 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.298999 14.977208 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.298999 14.977208 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.500741 14.988416 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.500741 14.988416 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.500741 14.988416 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.657652 15.122911 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.657652 15.122911 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.657652 15.122911 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.724900 15.279822 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.724900 15.279822 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.724900 15.279822 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.668860 15.470357 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.668860 15.470357 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.668860 15.470357 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.511949 15.593644 0.056040 0.061644 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.511949 15.593644 0.056040 0.061644 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.511949 15.593644 0.056040 0.061644 0 360 ellipse cp s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.200000 11.800000 m 13.052257 11.510045 l s 0 slj n 13.082279 11.908917 m 13.850000 11.450000 l 13.022234 11.111173 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 15.700000 11.400000 m 19.112458 12.009367 l s 0 slj n 19.042142 12.403139 m 19.900000 12.150000 l 19.182774 11.615596 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 21.550000 12.450000 m 24.034360 14.106240 l s 0 slj n 23.812480 14.439060 m 24.700000 14.550000 l 24.256240 13.773420 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 20.512668 -6.883836 22.655587 22.655587 79.606262 118.836779 ellipse s 0 slj n 9.791712 12.619639 m 8.900000 12.550000 l 9.379316 13.305153 l f gsave 10.200000 11.300000 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 10.538667 11.300000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 10.708000 11.300000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 10.877333 11.300000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 11.046667 11.300000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 11.486933 11.300000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 2972 3392 lineto 2516 3392 lineto 1585 521 lineto 599 3392 lineto 138 3392 lineto 1346 0 lineto 1806 0 lineto end_ol grestore gsave 11.893333 11.300000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 12.062667 11.300000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 12.435200 11.300000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 16.500000 11.100000 translate 0.035278 -0.035278 scale start_ol 1536 768 moveto 1536 0 lineto 1920 0 lineto 1920 768 lineto 2404 768 lineto 2404 1152 lineto 1920 1152 lineto 1920 3287 lineto 1636 3287 lineto 158 1217 lineto 158 768 lineto 1536 768 lineto 1536 1152 moveto 513 1152 lineto 1536 2591 lineto 1536 1152 lineto end_ol grestore gsave 16.838667 11.100000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 17.008000 11.100000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 17.177333 11.100000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 17.346667 11.100000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 17.786933 11.100000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 2972 3392 lineto 2516 3392 lineto 1585 521 lineto 599 3392 lineto 138 3392 lineto 1346 0 lineto 1806 0 lineto end_ol grestore gsave 18.193333 11.100000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 18.362667 11.100000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 18.735200 11.100000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 22.300000 12.600000 translate 0.035278 -0.035278 scale start_ol 2146 3200 moveto 478 3200 lineto 233 1509 lineto 607 1509 lineto 796 1719 953 1792 1206 1792 curveto 1644 1792 1920 1514 1920 1064 curveto 1920 627 1648 362 1206 362 curveto 851 362 635 529 538 871 curveto 132 871 lineto 187 615 232 491 327 376 curveto 509 137 836 0 1200 0 curveto 1850 0 2304 462 2304 1128 curveto 2304 1750 1883 2176 1268 2176 curveto 1042 2176 861 2112 676 1964 curveto 805 2796 lineto 2146 2796 lineto 2146 3200 lineto end_ol grestore gsave 22.638667 12.600000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 22.808000 12.600000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 22.977333 12.600000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 23.146667 12.600000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 23.586933 12.600000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 2972 3392 lineto 2516 3392 lineto 1585 521 lineto 599 3392 lineto 138 3392 lineto 1346 0 lineto 1806 0 lineto end_ol grestore gsave 23.993333 12.600000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 24.162667 12.600000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 24.535200 12.600000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 16.800000 15.000000 translate 0.035278 -0.035278 scale start_ol 2240 2393 moveto 2163 2899 1821 3200 1335 3200 curveto 984 3200 669 3034 480 2758 curveto 282 2457 192 2077 192 1513 curveto 192 992 273 660 462 385 curveto 632 136 911 0 1261 0 curveto 1868 0 2304 441 2304 1055 curveto 2304 1638 1910 2048 1354 2048 curveto 1047 2048 806 1915 640 1660 curveto 640 2418 888 2838 1335 2838 curveto 1610 2838 1800 2676 1862 2393 curveto 2240 2393 lineto 1300 1664 moveto 1682 1664 1920 1415 1920 1013 curveto 1920 637 1651 362 1287 362 curveto 918 362 640 649 640 1034 curveto 640 1406 909 1664 1300 1664 curveto end_ol grestore gsave 17.138667 15.000000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 17.308000 15.000000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 17.477333 15.000000 translate 0.035278 -0.035278 scale start_ol 384 0 moveto 1855 0 lineto 2163 0 2391 88 2566 282 curveto 2727 458 2816 698 2816 962 curveto 2816 1369 2632 1614 2204 1780 curveto 2478 1925 2624 2178 2624 2525 curveto 2624 2773 2537 2994 2370 3153 curveto 2204 3317 1985 3392 1679 3392 curveto 384 3392 lineto 384 0 lineto 768 1920 moveto 768 3011 lineto 1581 3011 lineto 1816 3011 1947 2978 2059 2889 curveto 2177 2795 2240 2654 2240 2466 curveto 2240 2282 2177 2136 2059 2042 curveto 1947 1953 1816 1920 1581 1920 curveto 768 1920 lineto 768 381 moveto 768 1536 lineto 1823 1536 lineto 2032 1536 2172 1485 2274 1375 curveto 2376 1269 2432 1122 2432 956 curveto 2432 795 2376 648 2274 542 curveto 2172 432 2032 381 1823 381 curveto 768 381 lineto end_ol grestore gsave 17.883733 15.000000 translate 0.035278 -0.035278 scale start_ol 1728 1331 moveto 2991 3392 lineto 2479 3392 lineto 1542 1740 lineto 579 3392 lineto 49 3392 lineto 1344 1331 lineto 1344 0 lineto 1728 0 lineto 1728 1331 lineto end_ol grestore gsave 18.290133 15.000000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 7.050000 10.500000 translate 0.035278 -0.035278 scale start_ol 1536 3392 moveto 1536 1086 lineto 1536 825 1508 677 1429 564 curveto 1346 438 1188 362 1017 362 curveto 693 362 512 573 512 955 curveto 512 1167 lineto 64 1167 lineto 64 876 lineto 64 341 418 0 985 0 curveto 1562 0 1920 356 1920 925 curveto 1920 3392 lineto 1536 3392 lineto end_ol grestore gsave 7.354800 10.500000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 7.693467 10.500000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 25.900000 13.950000 translate 0.035278 -0.035278 scale start_ol 384 0 moveto 1855 0 lineto 2163 0 2391 88 2566 282 curveto 2727 458 2816 698 2816 962 curveto 2816 1369 2632 1614 2204 1780 curveto 2478 1925 2624 2178 2624 2525 curveto 2624 2773 2537 2994 2370 3153 curveto 2204 3317 1985 3392 1679 3392 curveto 384 3392 lineto 384 0 lineto 768 1920 moveto 768 3011 lineto 1581 3011 lineto 1816 3011 1947 2978 2059 2889 curveto 2177 2795 2240 2654 2240 2466 curveto 2240 2282 2177 2136 2059 2042 curveto 1947 1953 1816 1920 1581 1920 curveto 768 1920 lineto 768 381 moveto 768 1536 lineto 1823 1536 lineto 2032 1536 2172 1485 2274 1375 curveto 2376 1269 2432 1122 2432 956 curveto 2432 795 2376 648 2274 542 curveto 2172 432 2032 381 1823 381 curveto 768 381 lineto end_ol grestore gsave 26.306400 13.950000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 26.645067 13.950000 translate 0.035278 -0.035278 scale start_ol 256 3392 moveto 256 0 lineto 640 0 lineto 640 412 lineto 815 133 1048 0 1368 0 curveto 1973 0 2368 489 2368 1242 curveto 2368 1978 1983 2432 1365 2432 curveto 1043 2432 815 2308 640 2036 curveto 640 3392 lineto 256 3392 lineto 1296 2070 moveto 1714 2070 1984 1729 1984 1203 curveto 1984 703 1705 362 1296 362 curveto 901 362 640 699 640 1216 curveto 640 1733 901 2070 1296 2070 curveto end_ol grestore gsave 12.400000 9.600000 translate 0.035278 -0.035278 scale start_ol 256 -1024 moveto 640 -1024 lineto 640 362 lineto 837 111 1057 0 1363 0 curveto 1969 0 2368 472 2368 1194 curveto 2368 1956 1989 2432 1376 2432 curveto 1063 2432 812 2286 640 2003 curveto 640 2432 lineto 256 2432 lineto 256 -1024 lineto 1298 2070 moveto 1713 2070 1984 1729 1984 1203 curveto 1984 703 1709 362 1298 362 curveto 902 362 640 699 640 1216 curveto 640 1733 902 2070 1298 2070 curveto end_ol grestore gsave 12.738667 9.600000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 12.941867 9.600000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 13.280533 9.600000 translate 0.035278 -0.035278 scale start_ol 1346 1258 moveto 2157 2432 lineto 1723 2432 lineto 1143 1550 lineto 562 2432 lineto 124 2432 lineto 931 1239 lineto 78 0 lineto 516 0 lineto 1129 933 lineto 1733 0 lineto 2180 0 lineto 1346 1258 lineto end_ol grestore gsave 13.585333 9.600000 translate 0.035278 -0.035278 scale start_ol 1788 2432 moveto 1120 533 lineto 502 2432 lineto 92 2432 lineto 908 -16 lineto 760 -403 lineto 700 -575 613 -640 452 -640 curveto 396 -640 332 -631 249 -612 curveto 249 -963 lineto 327 -1005 406 -1024 507 -1024 curveto 631 -1024 765 -982 866 -908 curveto 986 -819 1055 -717 1129 -521 curveto 2203 2432 lineto 1788 2432 lineto end_ol grestore gsave 13.890133 9.600000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 14.059467 9.600000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 14.398133 9.600000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 14.567467 9.600000 translate 0.035278 -0.035278 scale start_ol 2139 1595 moveto 2121 1818 2070 1963 1979 2090 curveto 1814 2305 1527 2432 1193 2432 curveto 548 2432 128 1947 128 1194 curveto 128 463 539 0 1188 0 curveto 1759 0 2120 328 2166 888 curveto 1779 888 lineto 1714 534 1516 357 1189 357 curveto 765 357 512 673 512 1195 curveto 512 1747 761 2075 1180 2075 curveto 1503 2075 1706 1902 1752 1595 curveto 2139 1595 lineto end_ol grestore gsave 14.872267 9.600000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 15.210933 9.600000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1478 lineto 704 1819 960 2093 1277 2093 curveto 1565 2093 1728 1922 1728 1621 curveto 1728 0 lineto 2112 0 lineto 2112 1478 lineto 2112 1819 2368 2093 2685 2093 curveto 2968 2093 3136 1918 3136 1621 curveto 3136 0 lineto 3520 0 lineto 3520 1773 lineto 3520 2197 3270 2432 2816 2432 curveto 2492 2432 2298 2335 2071 2062 curveto 1931 2321 1741 2432 1433 2432 curveto 1116 2432 908 2312 704 2021 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 19.150000 10.550000 translate 0.035278 -0.035278 scale start_ol 256 -1024 moveto 640 -1024 lineto 640 362 lineto 837 111 1057 0 1363 0 curveto 1969 0 2368 472 2368 1194 curveto 2368 1956 1989 2432 1376 2432 curveto 1063 2432 812 2286 640 2003 curveto 640 2432 lineto 256 2432 lineto 256 -1024 lineto 1298 2070 moveto 1713 2070 1984 1729 1984 1203 curveto 1984 703 1709 362 1298 362 curveto 902 362 640 699 640 1216 curveto 640 1733 902 2070 1298 2070 curveto end_ol grestore gsave 19.488667 10.550000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 19.691867 10.550000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 20.030533 10.550000 translate 0.035278 -0.035278 scale start_ol 1346 1258 moveto 2157 2432 lineto 1723 2432 lineto 1143 1550 lineto 562 2432 lineto 124 2432 lineto 931 1239 lineto 78 0 lineto 516 0 lineto 1129 933 lineto 1733 0 lineto 2180 0 lineto 1346 1258 lineto end_ol grestore gsave 20.335333 10.550000 translate 0.035278 -0.035278 scale start_ol 1788 2432 moveto 1120 533 lineto 502 2432 lineto 92 2432 lineto 908 -16 lineto 760 -403 lineto 700 -575 613 -640 452 -640 curveto 396 -640 332 -631 249 -612 curveto 249 -963 lineto 327 -1005 406 -1024 507 -1024 curveto 631 -1024 765 -982 866 -908 curveto 986 -819 1055 -717 1129 -521 curveto 2203 2432 lineto 1788 2432 lineto end_ol grestore gsave 20.640133 10.550000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 20.809467 10.550000 translate 0.035278 -0.035278 scale start_ol 256 3392 moveto 256 0 lineto 640 0 lineto 640 412 lineto 815 133 1048 0 1368 0 curveto 1973 0 2368 489 2368 1242 curveto 2368 1978 1983 2432 1365 2432 curveto 1043 2432 815 2308 640 2036 curveto 640 3392 lineto 256 3392 lineto 1296 2070 moveto 1714 2070 1984 1729 1984 1203 curveto 1984 703 1705 362 1296 362 curveto 901 362 640 699 640 1216 curveto 640 1733 901 2070 1296 2070 curveto end_ol grestore gsave 21.148133 10.550000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 21.317467 10.550000 translate 0.035278 -0.035278 scale start_ol 2139 1595 moveto 2121 1818 2070 1963 1979 2090 curveto 1814 2305 1527 2432 1193 2432 curveto 548 2432 128 1947 128 1194 curveto 128 463 539 0 1188 0 curveto 1759 0 2120 328 2166 888 curveto 1779 888 lineto 1714 534 1516 357 1189 357 curveto 765 357 512 673 512 1195 curveto 512 1747 761 2075 1180 2075 curveto 1503 2075 1706 1902 1752 1595 curveto 2139 1595 lineto end_ol grestore gsave 21.622267 10.550000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 21.960933 10.550000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1478 lineto 704 1819 960 2093 1277 2093 curveto 1565 2093 1728 1922 1728 1621 curveto 1728 0 lineto 2112 0 lineto 2112 1478 lineto 2112 1819 2368 2093 2685 2093 curveto 2968 2093 3136 1918 3136 1621 curveto 3136 0 lineto 3520 0 lineto 3520 1773 lineto 3520 2197 3270 2432 2816 2432 curveto 2492 2432 2298 2335 2071 2062 curveto 1931 2321 1741 2432 1433 2432 curveto 1116 2432 908 2312 704 2021 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 24.700000 16.750000 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 25.038667 16.750000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 25.208000 16.750000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 25.546667 16.750000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 25.716000 16.750000 translate 0.035278 -0.035278 scale start_ol 990 1523 moveto 1041 1523 lineto 1211 1527 lineto 1679 1527 1920 1338 1920 972 curveto 1920 590 1661 362 1227 362 curveto 775 362 553 568 525 1011 curveto 119 1011 lineto 137 759 184 596 262 455 curveto 428 155 751 0 1198 0 curveto 1871 0 2304 380 2304 966 curveto 2304 1359 2143 1577 1751 1704 curveto 2034 1818 2176 2036 2176 2347 curveto 2176 2881 1808 3200 1196 3200 curveto 547 3200 202 2856 188 2189 curveto 594 2189 lineto 599 2374 617 2477 668 2572 curveto 760 2740 963 2843 1216 2843 curveto 1575 2843 1792 2645 1792 2322 curveto 1792 2107 1709 1978 1529 1910 curveto 1419 1867 1276 1849 990 1845 curveto 990 1523 lineto end_ol grestore gsave 26.054667 16.750000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 26.224000 16.750000 translate 0.035278 -0.035278 scale start_ol 1536 768 moveto 1536 0 lineto 1920 0 lineto 1920 768 lineto 2404 768 lineto 2404 1152 lineto 1920 1152 lineto 1920 3287 lineto 1636 3287 lineto 158 1217 lineto 158 768 lineto 1536 768 lineto 1536 1152 moveto 513 1152 lineto 1536 2591 lineto 1536 1152 lineto end_ol grestore gsave 6.400000 13.400000 translate 0.035278 -0.035278 scale start_ol 2146 3200 moveto 478 3200 lineto 233 1509 lineto 607 1509 lineto 796 1719 953 1792 1206 1792 curveto 1644 1792 1920 1514 1920 1064 curveto 1920 627 1648 362 1206 362 curveto 851 362 635 529 538 871 curveto 132 871 lineto 187 615 232 491 327 376 curveto 509 137 836 0 1200 0 curveto 1850 0 2304 462 2304 1128 curveto 2304 1750 1883 2176 1268 2176 curveto 1042 2176 861 2112 676 1964 curveto 805 2796 lineto 2146 2796 lineto 2146 3200 lineto end_ol grestore gsave 6.738667 13.400000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 6.908000 13.400000 translate 0.035278 -0.035278 scale start_ol 2240 2393 moveto 2163 2899 1821 3200 1335 3200 curveto 984 3200 669 3034 480 2758 curveto 282 2457 192 2077 192 1513 curveto 192 992 273 660 462 385 curveto 632 136 911 0 1261 0 curveto 1868 0 2304 441 2304 1055 curveto 2304 1638 1910 2048 1354 2048 curveto 1047 2048 806 1915 640 1660 curveto 640 2418 888 2838 1335 2838 curveto 1610 2838 1800 2676 1862 2393 curveto 2240 2393 lineto 1300 1664 moveto 1682 1664 1920 1415 1920 1013 curveto 1920 637 1651 362 1287 362 curveto 918 362 640 649 640 1034 curveto 640 1406 909 1664 1300 1664 curveto end_ol grestore gsave 7.246667 13.400000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 7.416000 13.400000 translate 0.035278 -0.035278 scale start_ol 2396 3200 moveto 212 3200 lineto 212 2796 lineto 1977 2796 lineto 1198 1713 880 1047 636 0 curveto 1069 0 lineto 1249 1021 1659 1898 2396 2856 curveto 2396 3200 lineto end_ol grestore gsave 7.754667 13.400000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 7.924000 13.400000 translate 0.035278 -0.035278 scale start_ol 1821 1738 moveto 2136 1929 2240 2086 2240 2378 curveto 2240 2861 1835 3200 1248 3200 curveto 666 3200 256 2861 256 2378 curveto 256 2090 360 1934 670 1738 curveto 307 1567 128 1308 128 966 curveto 128 395 563 0 1184 0 curveto 1805 0 2240 395 2240 961 curveto 2240 1308 2103 1567 1821 1738 curveto 1248 2838 moveto 1619 2838 1856 2657 1856 2373 curveto 1856 2101 1614 1920 1248 1920 curveto 882 1920 640 2101 640 2377 curveto 640 2657 882 2838 1248 2838 curveto 1184 1536 moveto 1584 1536 1856 1300 1856 951 curveto 1856 598 1584 362 1175 362 curveto 784 362 512 602 512 951 curveto 512 1300 784 1536 1184 1536 curveto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0.080000 slw 0 slc 0 slj [] 0 sd 0.701961 0.701961 0.701961 srgb n 17.165312 2.999998 m 17.165312 5.511961 l 18.241868 5.511961 l 18.241868 2.999998 l f 0.000000 0.000000 0.000000 srgb n 17.165312 2.999998 m 17.165312 5.511961 l 18.241868 5.511961 l 18.241868 2.999998 l cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 17.272968 3.150716 m 17.272968 3.437797 l 18.134212 3.437797 l 18.134212 3.150716 l cp s 0 slc 0 slj [] 0 sd n 17.272968 3.437797 m 17.272968 3.724879 l 18.134212 3.724879 l 18.134212 3.437797 l cp s 0 slc 0 slj [] 0 sd n 17.272968 3.724879 m 17.272968 4.011960 l 18.134212 4.011960 l 18.134212 3.724879 l cp s 0 slc 0 slj [] 0 sd n 17.272968 4.011960 m 17.272968 4.299042 l 18.134212 4.299042 l 18.134212 4.011960 l cp s 0 slc 0 slj [] 0 sd n 17.272968 4.356458 m 17.272968 4.528707 l 17.811245 4.528707 l 17.811245 4.356458 l cp s 0 slc 0 slj [] 0 sd 0.000000 1.000000 0.000000 srgb n 18.080384 4.385166 0.037679 0.037679 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 18.080384 4.385166 0.037679 0.037679 0 360 ellipse cp s 0 slc 0 slj [] 0 sd 1.000000 1.000000 0.000000 srgb n 18.080384 4.499999 0.037679 0.037679 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 18.080384 4.499999 0.037679 0.037679 0 360 ellipse cp s 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 17.865073 4.413874 m 17.865073 4.528707 l 17.994260 4.528707 l 17.994260 4.413874 l f 0.000000 0.000000 0.000000 srgb n 17.865073 4.413874 m 17.865073 4.528707 l 17.994260 4.528707 l 17.994260 4.413874 l cp s 0 slc 0 slj [] 0 sd n 17.344738 4.758372 m 17.344738 5.386363 l s 0 slc 0 slj [] 0 sd n 17.524164 4.758372 m 17.524164 5.386363 l s 0 slc 0 slj [] 0 sd n 17.703590 4.758372 m 17.703590 5.386363 l s 0 slc 0 slj [] 0 sd n 17.883016 4.758372 m 17.883016 5.386363 l s 0 slc 0 slj [] 0 sd n 18.062442 4.758372 m 18.062442 5.386363 l s 0 slc 0 slj [] 0 sd n 18.241868 4.758372 m 18.241868 5.386363 l s 0 slc 0 slj [] 0 sd 0.600000 0.600000 0.600000 srgb n 16.950001 5.727272 m 17.165312 5.296650 l 17.165312 5.511961 l 18.241868 5.511961 l 18.241868 5.296650 l 18.528949 5.727272 l f 0.000000 0.000000 0.000000 srgb n 16.950001 5.727272 m 17.165312 5.296650 l 17.165312 5.511961 l 18.241868 5.511961 l 18.241868 5.296650 l 18.528949 5.727272 l cp s gsave 16.050000 2.300000 translate 0.035278 -0.035278 scale start_ol 384 0 moveto 1660 0 lineto 2495 0 3008 642 3008 1698 curveto 3008 2750 2500 3392 1660 3392 curveto 384 3392 lineto 384 0 lineto 768 381 moveto 768 3011 lineto 1582 3011 lineto 2264 3011 2624 2559 2624 1694 curveto 2624 837 2264 381 1582 381 curveto 768 381 lineto end_ol grestore gsave 16.490267 2.300000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 16.930533 2.300000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 17.336933 2.300000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 17.506267 2.300000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 17.912667 2.300000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 18.251333 2.300000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 18.454533 2.300000 translate 0.035278 -0.035278 scale start_ol 1313 0 moveto 2239 2432 lineto 1806 2432 lineto 1124 459 lineto 479 2432 lineto 46 2432 lineto 894 0 lineto 1313 0 lineto end_ol grestore gsave 18.759333 2.300000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 19.098000 2.300000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 19.618602 8.768606 4.968701 4.968701 179.637979 222.906337 ellipse s 0 slj n 16.197142 5.721310 m 16.650000 4.950000 l 15.761237 5.050499 l f gsave 12.500000 5.600000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 12.838667 5.600000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 13.008000 5.600000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 13.177333 5.600000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 13.583733 5.600000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 13.753067 5.600000 translate 0.035278 -0.035278 scale start_ol 832 1408 moveto 1879 1408 lineto 2141 1408 2348 1488 2527 1654 curveto 2729 1843 2816 2065 2816 2381 curveto 2816 3028 2451 3392 1803 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1408 lineto 832 1792 moveto 832 3011 lineto 1719 3011 lineto 2126 3011 2368 2782 2368 2402 curveto 2368 2021 2126 1792 1719 1792 curveto 832 1792 lineto end_ol grestore gsave 14.159467 5.600000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 14.328800 5.600000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 14.735200 5.600000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 15.175467 5.600000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 2972 3392 lineto 2516 3392 lineto 1585 521 lineto 599 3392 lineto 138 3392 lineto 1346 0 lineto 1806 0 lineto end_ol grestore gsave 15.581867 5.600000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 12.500000 6.400000 translate 0.035278 -0.035278 scale start_ol 1169 2432 moveto 768 2432 lineto 768 2809 lineto 768 2970 860 3053 1035 3053 curveto 1067 3053 1081 3053 1169 3053 curveto 1169 3369 lineto 1081 3387 1030 3392 952 3392 curveto 596 3392 384 3189 384 2844 curveto 384 2432 lineto 61 2432 lineto 61 2116 lineto 384 2116 lineto 384 0 lineto 768 0 lineto 768 2116 lineto 1169 2116 lineto 1169 2432 lineto end_ol grestore gsave 12.669333 6.400000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 13.008000 6.400000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 13.211200 6.400000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 13.380533 6.400000 translate 0.035278 -0.035278 scale start_ol 256 3392 moveto 256 0 lineto 640 0 lineto 640 412 lineto 815 133 1048 0 1368 0 curveto 1973 0 2368 489 2368 1242 curveto 2368 1978 1983 2432 1365 2432 curveto 1043 2432 815 2308 640 2036 curveto 640 3392 lineto 256 3392 lineto 1296 2070 moveto 1714 2070 1984 1729 1984 1203 curveto 1984 703 1705 362 1296 362 curveto 901 362 640 699 640 1216 curveto 640 1733 901 2070 1296 2070 curveto end_ol grestore gsave 13.719200 6.400000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 13.888533 6.400000 translate 0.035278 -0.035278 scale start_ol 2139 1595 moveto 2121 1818 2070 1963 1979 2090 curveto 1814 2305 1527 2432 1193 2432 curveto 548 2432 128 1947 128 1194 curveto 128 463 539 0 1188 0 curveto 1759 0 2120 328 2166 888 curveto 1779 888 lineto 1714 534 1516 357 1189 357 curveto 765 357 512 673 512 1195 curveto 512 1747 761 2075 1180 2075 curveto 1503 2075 1706 1902 1752 1595 curveto 2139 1595 lineto end_ol grestore gsave 14.193333 6.400000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 14.532000 6.400000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1478 lineto 704 1819 960 2093 1277 2093 curveto 1565 2093 1728 1922 1728 1621 curveto 1728 0 lineto 2112 0 lineto 2112 1478 lineto 2112 1819 2368 2093 2685 2093 curveto 2968 2093 3136 1918 3136 1621 curveto 3136 0 lineto 3520 0 lineto 3520 1773 lineto 3520 2197 3270 2432 2816 2432 curveto 2492 2432 2298 2335 2071 2062 curveto 1931 2321 1741 2432 1433 2432 curveto 1116 2432 908 2312 704 2021 curveto 704 2432 lineto 320 2432 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 10.316537 5.021018 7.625160 7.625160 8.894608 34.388567 ellipse s 0 slj n 16.322882 9.048216 m 16.050000 9.900000 l 16.895156 9.607235 l f gsave 17.950000 8.050000 translate 0.035278 -0.035278 scale start_ol 990 1523 moveto 1041 1523 lineto 1211 1527 lineto 1679 1527 1920 1338 1920 972 curveto 1920 590 1661 362 1227 362 curveto 775 362 553 568 525 1011 curveto 119 1011 lineto 137 759 184 596 262 455 curveto 428 155 751 0 1198 0 curveto 1871 0 2304 380 2304 966 curveto 2304 1359 2143 1577 1751 1704 curveto 2034 1818 2176 2036 2176 2347 curveto 2176 2881 1808 3200 1196 3200 curveto 547 3200 202 2856 188 2189 curveto 594 2189 lineto 599 2374 617 2477 668 2572 curveto 760 2740 963 2843 1216 2843 curveto 1575 2843 1792 2645 1792 2322 curveto 1792 2107 1709 1978 1529 1910 curveto 1419 1867 1276 1849 990 1845 curveto 990 1523 lineto end_ol grestore gsave 18.288667 8.050000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 18.458000 8.050000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 18.627333 8.050000 translate 0.035278 -0.035278 scale start_ol 256 -1024 moveto 640 -1024 lineto 640 362 lineto 837 111 1057 0 1363 0 curveto 1969 0 2368 472 2368 1194 curveto 2368 1956 1989 2432 1376 2432 curveto 1063 2432 812 2286 640 2003 curveto 640 2432 lineto 256 2432 lineto 256 -1024 lineto 1298 2070 moveto 1713 2070 1984 1729 1984 1203 curveto 1984 703 1709 362 1298 362 curveto 902 362 640 699 640 1216 curveto 640 1733 902 2070 1298 2070 curveto end_ol grestore gsave 18.966000 8.050000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 19.169200 8.050000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 19.507867 8.050000 translate 0.035278 -0.035278 scale start_ol 1346 1258 moveto 2157 2432 lineto 1723 2432 lineto 1143 1550 lineto 562 2432 lineto 124 2432 lineto 931 1239 lineto 78 0 lineto 516 0 lineto 1129 933 lineto 1733 0 lineto 2180 0 lineto 1346 1258 lineto end_ol grestore gsave 19.812667 8.050000 translate 0.035278 -0.035278 scale start_ol 1788 2432 moveto 1120 533 lineto 502 2432 lineto 92 2432 lineto 908 -16 lineto 760 -403 lineto 700 -575 613 -640 452 -640 curveto 396 -640 332 -631 249 -612 curveto 249 -963 lineto 327 -1005 406 -1024 507 -1024 curveto 631 -1024 765 -982 866 -908 curveto 986 -819 1055 -717 1129 -521 curveto 2203 2432 lineto 1788 2432 lineto end_ol grestore gsave 20.117467 8.050000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 20.286800 8.050000 translate 0.035278 -0.035278 scale start_ol 256 3392 moveto 256 0 lineto 640 0 lineto 640 412 lineto 815 133 1048 0 1368 0 curveto 1973 0 2368 489 2368 1242 curveto 2368 1978 1983 2432 1365 2432 curveto 1043 2432 815 2308 640 2036 curveto 640 3392 lineto 256 3392 lineto 1296 2070 moveto 1714 2070 1984 1729 1984 1203 curveto 1984 703 1705 362 1296 362 curveto 901 362 640 699 640 1216 curveto 640 1733 901 2070 1296 2070 curveto end_ol grestore gsave 20.625467 8.050000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 20.794800 8.050000 translate 0.035278 -0.035278 scale start_ol 2139 1595 moveto 2121 1818 2070 1963 1979 2090 curveto 1814 2305 1527 2432 1193 2432 curveto 548 2432 128 1947 128 1194 curveto 128 463 539 0 1188 0 curveto 1759 0 2120 328 2166 888 curveto 1779 888 lineto 1714 534 1516 357 1189 357 curveto 765 357 512 673 512 1195 curveto 512 1747 761 2075 1180 2075 curveto 1503 2075 1706 1902 1752 1595 curveto 2139 1595 lineto end_ol grestore gsave 21.099600 8.050000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 21.438267 8.050000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1478 lineto 704 1819 960 2093 1277 2093 curveto 1565 2093 1728 1922 1728 1621 curveto 1728 0 lineto 2112 0 lineto 2112 1478 lineto 2112 1819 2368 2093 2685 2093 curveto 2968 2093 3136 1918 3136 1621 curveto 3136 0 lineto 3520 0 lineto 3520 1773 lineto 3520 2197 3270 2432 2816 2432 curveto 2492 2432 2298 2335 2071 2062 curveto 1931 2321 1741 2432 1433 2432 curveto 1116 2432 908 2312 704 2021 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 6.750000 7.750000 translate 0.035278 -0.035278 scale start_ol 4197 3160 moveto 4014 4133 3433 4608 2423 4608 curveto 1804 4608 1305 4421 964 4059 curveto 547 3625 320 2998 320 2286 curveto 320 1562 554 941 989 513 curveto 1343 163 1798 0 2398 0 curveto 3522 0 4153 582 4292 1751 curveto 3684 1751 lineto 3633 1454 3570 1252 3475 1080 curveto 3285 723 2892 521 2398 521 curveto 1479 521 896 1210 896 2292 curveto 896 3404 1454 4087 2347 4087 curveto 2721 4087 3069 3980 3259 3814 curveto 3430 3665 3525 3481 3595 3160 curveto 4197 3160 lineto end_ol grestore gsave 7.351133 7.750000 translate 0.035278 -0.035278 scale start_ol 1722 3328 moveto 809 3328 256 2706 256 1664 curveto 256 622 803 0 1728 0 curveto 2641 0 3200 622 3200 1640 curveto 3200 2712 2660 3328 1722 3328 curveto 1728 2839 moveto 2330 2839 2688 2395 2688 1647 curveto 2688 938 2317 489 1728 489 curveto 1133 489 768 933 768 1664 curveto 768 2390 1133 2839 1728 2839 curveto end_ol grestore gsave 7.816800 7.750000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 2022 lineto 960 2489 1312 2864 1747 2864 curveto 2144 2864 2368 2631 2368 2219 curveto 2368 0 lineto 2880 0 lineto 2880 2022 lineto 2880 2489 3232 2864 3667 2864 curveto 4058 2864 4288 2624 4288 2219 curveto 4288 0 lineto 4800 0 lineto 4800 2427 lineto 4800 3007 4459 3328 3841 3328 curveto 3399 3328 3134 3195 2825 2821 curveto 2634 3176 2374 3328 1954 3328 curveto 1522 3328 1238 3164 960 2766 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 8.511067 7.750000 translate 0.035278 -0.035278 scale start_ol 320 -1407 moveto 832 -1407 lineto 832 495 lineto 1110 152 1419 0 1849 0 curveto 2702 0 3264 645 3264 1634 curveto 3264 2677 2730 3328 1868 3328 curveto 1428 3328 1074 3128 832 2741 curveto 832 3328 lineto 320 3328 lineto 320 -1407 lineto 1773 2833 moveto 2365 2833 2752 2367 2752 1647 curveto 2752 961 2359 495 1773 495 curveto 1206 495 832 956 832 1664 curveto 832 2372 1206 2833 1773 2833 curveto end_ol grestore gsave 8.976733 7.750000 translate 0.035278 -0.035278 scale start_ol 3407 458 moveto 3350 464 3325 464 3293 464 curveto 3109 464 3008 544 3008 683 curveto 3008 2459 lineto 3008 3024 2546 3328 1669 3328 curveto 1153 3328 725 3195 487 2960 curveto 324 2797 256 2617 256 2303 curveto 901 2303 lineto 948 2672 1200 2839 1718 2839 curveto 2217 2839 2496 2675 2496 2382 curveto 2496 2254 lineto 2496 2049 2360 1961 1934 1914 curveto 1173 1826 1056 1803 850 1727 curveto 456 1580 256 1305 256 907 curveto 256 351 682 0 1366 0 curveto 1792 0 2134 145 2515 487 curveto 2552 152 2715 0 3051 0 curveto 3157 0 3239 13 3407 60 curveto 3407 458 lineto 2496 1127 moveto 2496 966 2443 868 2277 735 curveto 2051 556 1778 464 1453 464 curveto 1021 464 768 643 768 948 curveto 768 1265 1014 1427 1605 1502 curveto 2190 1571 2310 1594 2496 1669 curveto 2496 1127 lineto end_ol grestore gsave 9.442400 7.750000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1776 lineto 960 2434 1318 2864 1869 2864 curveto 2291 2864 2560 2618 2560 2231 curveto 2560 0 lineto 3072 0 lineto 3072 2445 lineto 3072 2982 2668 3328 2041 3328 curveto 1556 3328 1246 3139 960 2680 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 9.908067 7.750000 translate 0.035278 -0.035278 scale start_ol 2458 3328 moveto 1540 717 lineto 691 3328 lineto 127 3328 lineto 1248 -38 lineto 1045 -569 lineto 963 -806 843 -896 621 -896 curveto 545 -896 456 -883 342 -858 curveto 342 -1327 lineto 450 -1383 558 -1408 697 -1408 curveto 868 -1408 1052 -1351 1191 -1248 curveto 1356 -1127 1451 -987 1552 -719 curveto 3029 3328 lineto 2458 3328 lineto end_ol grestore gsave 10.331400 7.750000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 10.568467 7.750000 translate 0.035278 -0.035278 scale start_ol 3003 1408 moveto 3478 0 lineto 4137 0 lineto 2515 4608 lineto 1755 4608 lineto 108 0 lineto 735 0 lineto 1223 1408 lineto 3003 1408 lineto 2839 1920 moveto 1369 1920 lineto 2129 3986 lineto 2839 1920 lineto end_ol grestore gsave 24.300000 8.000000 translate 0.035278 -0.035278 scale start_ol 4197 3160 moveto 4014 4133 3433 4608 2423 4608 curveto 1804 4608 1305 4421 964 4059 curveto 547 3625 320 2998 320 2286 curveto 320 1562 554 941 989 513 curveto 1343 163 1798 0 2398 0 curveto 3522 0 4153 582 4292 1751 curveto 3684 1751 lineto 3633 1454 3570 1252 3475 1080 curveto 3285 723 2892 521 2398 521 curveto 1479 521 896 1210 896 2292 curveto 896 3404 1454 4087 2347 4087 curveto 2721 4087 3069 3980 3259 3814 curveto 3430 3665 3525 3481 3595 3160 curveto 4197 3160 lineto end_ol grestore gsave 24.901133 8.000000 translate 0.035278 -0.035278 scale start_ol 1722 3328 moveto 809 3328 256 2706 256 1664 curveto 256 622 803 0 1728 0 curveto 2641 0 3200 622 3200 1640 curveto 3200 2712 2660 3328 1722 3328 curveto 1728 2839 moveto 2330 2839 2688 2395 2688 1647 curveto 2688 938 2317 489 1728 489 curveto 1133 489 768 933 768 1664 curveto 768 2390 1133 2839 1728 2839 curveto end_ol grestore gsave 25.366800 8.000000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 2022 lineto 960 2489 1312 2864 1747 2864 curveto 2144 2864 2368 2631 2368 2219 curveto 2368 0 lineto 2880 0 lineto 2880 2022 lineto 2880 2489 3232 2864 3667 2864 curveto 4058 2864 4288 2624 4288 2219 curveto 4288 0 lineto 4800 0 lineto 4800 2427 lineto 4800 3007 4459 3328 3841 3328 curveto 3399 3328 3134 3195 2825 2821 curveto 2634 3176 2374 3328 1954 3328 curveto 1522 3328 1238 3164 960 2766 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 26.061067 8.000000 translate 0.035278 -0.035278 scale start_ol 320 -1407 moveto 832 -1407 lineto 832 495 lineto 1110 152 1419 0 1849 0 curveto 2702 0 3264 645 3264 1634 curveto 3264 2677 2730 3328 1868 3328 curveto 1428 3328 1074 3128 832 2741 curveto 832 3328 lineto 320 3328 lineto 320 -1407 lineto 1773 2833 moveto 2365 2833 2752 2367 2752 1647 curveto 2752 961 2359 495 1773 495 curveto 1206 495 832 956 832 1664 curveto 832 2372 1206 2833 1773 2833 curveto end_ol grestore gsave 26.526733 8.000000 translate 0.035278 -0.035278 scale start_ol 3407 458 moveto 3350 464 3325 464 3293 464 curveto 3109 464 3008 544 3008 683 curveto 3008 2459 lineto 3008 3024 2546 3328 1669 3328 curveto 1153 3328 725 3195 487 2960 curveto 324 2797 256 2617 256 2303 curveto 901 2303 lineto 948 2672 1200 2839 1718 2839 curveto 2217 2839 2496 2675 2496 2382 curveto 2496 2254 lineto 2496 2049 2360 1961 1934 1914 curveto 1173 1826 1056 1803 850 1727 curveto 456 1580 256 1305 256 907 curveto 256 351 682 0 1366 0 curveto 1792 0 2134 145 2515 487 curveto 2552 152 2715 0 3051 0 curveto 3157 0 3239 13 3407 60 curveto 3407 458 lineto 2496 1127 moveto 2496 966 2443 868 2277 735 curveto 2051 556 1778 464 1453 464 curveto 1021 464 768 643 768 948 curveto 768 1265 1014 1427 1605 1502 curveto 2190 1571 2310 1594 2496 1669 curveto 2496 1127 lineto end_ol grestore gsave 26.992400 8.000000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1776 lineto 960 2434 1318 2864 1869 2864 curveto 2291 2864 2560 2618 2560 2231 curveto 2560 0 lineto 3072 0 lineto 3072 2445 lineto 3072 2982 2668 3328 2041 3328 curveto 1556 3328 1246 3139 960 2680 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 27.458067 8.000000 translate 0.035278 -0.035278 scale start_ol 2458 3328 moveto 1540 717 lineto 691 3328 lineto 127 3328 lineto 1248 -38 lineto 1045 -569 lineto 963 -806 843 -896 621 -896 curveto 545 -896 456 -883 342 -858 curveto 342 -1327 lineto 450 -1383 558 -1408 697 -1408 curveto 868 -1408 1052 -1351 1191 -1248 curveto 1356 -1127 1451 -987 1552 -719 curveto 3029 3328 lineto 2458 3328 lineto end_ol grestore gsave 27.881400 8.000000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 28.118467 8.000000 translate 0.035278 -0.035278 scale start_ol 512 0 moveto 2563 0 lineto 2994 0 3312 120 3555 386 curveto 3779 627 3904 956 3904 1317 curveto 3904 1874 3658 2209 3086 2437 curveto 3495 2633 3712 2973 3712 3440 curveto 3712 3775 3587 4072 3350 4286 curveto 3112 4507 2800 4608 2362 4608 curveto 512 4608 lineto 512 0 lineto 1088 2624 moveto 1088 4087 lineto 2219 4087 lineto 2545 4087 2728 4043 2885 3923 curveto 3048 3797 3136 3608 3136 3355 curveto 3136 3110 3048 2914 2885 2788 curveto 2728 2668 2545 2624 2219 2624 curveto 1088 2624 lineto 1088 521 moveto 1088 2112 lineto 2508 2112 lineto 2790 2112 2978 2042 3115 1890 curveto 3253 1744 3328 1542 3328 1313 curveto 3328 1091 3253 889 3115 743 curveto 2978 591 2790 521 2508 521 curveto 1088 521 lineto end_ol grestore showpage kamailio-4.0.4/doc/sip/figures/message.png0000644000000000000000000002262612223032460017143 0ustar rootroot‰PNG  IHDR?"ŒB¹sBITÛáOà pHYsÐй‹çŸ IDATxœíÝyX×þ?ð“(.,‚ŠPZÙ ‹@élÁ[74à^A±–Ö‹¶¶¥µßÇ«€XlZŸzÕ*bEAP‘ź+ ÊêV¹ Š‚  ²„ÌïóëÜ4$!%™™dòyý59æœ óÎ93™œp‚@Äeº€^‚ô «¸!‡#SÚ½D#íìì¨Ø2E @›Ñ×÷^ºtI,^¸p¶JYƒó§þýûûùùݺu‹éæõÞÜÜ\'''CCÃáÃ‡ïØ±vuu­^½zРA}úô™?þ«W¯p9‡ÃÙ´iÓ°aÃø|~÷M%''‡……-\¸099™,|óæMXXXß¾}­¬¬~üñG² URů¿þ:|øpCCCww÷7n ?;^|p«õbh7‚ ‚øã?<<<æÏŸÏts€Àל‰¿"K,--333ÛÚÚjkkÃÃÃqaLLÌĉÇãñx\îÿ¿èÕ}»2üýýwïÞ—SSSE"^æñxb±/wvv’[P± ò¡>¤ëÛ·ï¤I“ªªªÈréwLkkë[·náåêêjkkk¼òóÏ?'$$¬ZµŠ ˆ{÷î :´££ƒ ˆ%K–dddк3ÌQ’Þœœ¡Ph`` ¶oߎ Åbñ·ß~;pà@ccãàààÖÖVò¯¤‡<2"""bcc7nܸdɲðõë× .ìÓ§ÏàÁƒøá²^%UlÛ¶M ¸¹¹•••½5ƒÀéµ±±©©©!«©®®~ûí·e”““cee…—‡úàÁU^¬¡¡AæL˜Ïç?~ü˜ kkk¹}¯ŠUèUzU)—y7$Ç&™™™£GdìSRRºººìííñ[°ì$5uêÔ»wïŠÅâ»wïN™2eõêÕøéyóæUTT´··;vŒ<;Š‹‹óóó»}ûvgggyyypp°¢—û駟¤K/^üÓO?±bÅŠ€€€§OŸ>yò$ €Ü‚ŠUÍÌ̪««åÖÎ*¦×ÚÚúöíÛx¹¦¦ÆÆÆ/7nçΛ7oþøãqÉ•+WÞ}÷Ýßÿý“O>¡¬ÕZ‡e'‰ˆ ˆööö¨¨(gccóí·ßâ1Aûöí³··çóùNNN'NœÀ…]]]6lÀ}ú˜1c233½4˜««+yÕ+**ruuÅÍ 5664hÐ÷ßO^ŒQ± òáúõëûõëÇâXÅôJ¿Áûùù­\¹’ ˆüü|±X,‹ÇŒsãÆ ¼²¯¯¯··wQQÕ×,;IԢýªªjĈL·BK©˜^ékÎáááxHìããsàÀ¼Baaá”)SðòÑ£GD"¡²áÚ…e'‰Ì§÷‹/¾hjjzôè‘¿¿ÿ×_ÍtsôÈ–-[¾ûî;¦[A+–$2ŸÞÿüç?ƒ êׯßüùó[ZZ˜n޾xñâ…ƒƒC]]Ó ¡ËN9|CPÿp8·yóæˆˆ¦Û¢ª««§M›vïÞ=5·#ç–FÀzð–͈åË—¯[·®££cÕªU3gÎTƒ^s÷îÝË—/›™™™þÉÌÌ ¥€–³··wtt|ýúõôéÓcbbÔß ŒœuÌÎ;?ýôS™B>Ÿobb‚“l``ÐÞÞneeeoo/p™å·Þz‹‘ö³[CCAƒ ÂÿPMÝôªóµxãè…üüü¤¤¤—/_¾xñâåŸZ[[ÿîvÞ¼yî…†††®®.EϺ»»744\»vmðàÁÝŸŸoaaaaaºzõ*BÈÖÖöË/¿dº],4xð`%=ÖãÇ%‰¢>V³/¢îªN5»¿.ôÐõëוJÊ=êââ"¬­­ÝÝݳ³³qù³gÏ,--e*R^5`úyv¦!ôùçŸÇÅÅEGGõÕW2OÉ}¿´±±IJJª¯¯¯ªª²´´ T^žœœ†—ÃÂÂ’““ñò€¥+ê±jÀ óõé<@½ýöÛm_ ¼S‰ÄÎÎÎÉÉIºPmøÚÚÚj`` ¤¼¡¡ÏÿËÈðù|<`ž;wîŽ;º·GŪշwï^„Ðüùói¨ H‹Å!G[¬í{9NMMÍÍ›7U\ßßß¿¸¸¸­­­±±1>>~̘1JÊ÷îÝ+sb¹pá´´4„К5kÖ¬Y“––ÖÜÜÜÑÑQ\\LÑmÃÀ1OÑ»Bmm-Bhذam_ ¹;…äc¥_cÇŽ}ðÁFFF&&&þþþwîÜQRîêêZXX(½ý¢¢"WWW¼|ñâÅ &ôíÛ·OŸ>|ðÁ‘#G”WÝ£æææˆˆˆ‡ª²2ô½LéììDñù|Új¤*½÷ïßG ж¯Wš››BáááäÛŠ"xLOÛ©££ÿ›h«‘µ#g6ár¹¡ÎÎÎ;w:88,Z´´ ýÇ<¤Wàôbb±899yôèÑ!!!•••ÝW†{­˜érH§ëêêJKKsvv ,++c¤U@¤ÈÑ=½˜D"ÉÌÌtss›5kVII Í­2 ½@EéÅ‚8|ø°§§§¿¿ÿÅ‹aäÌúy~Ï«ô ¹']]]¤UÍV%ŠA?~üøñã...ýOrlKïíÛ·eîI½£ú÷“ÚÚÚ¨n ‹méEñx<®.Ó’öÛÛÛKcY.OOϘ˜˜æææ   9Ó=éÅìíí«««)­BOp¹\%éuss‹ŽŽö÷÷çp8û÷ï§³a€W­€|\®œOB$ƒc^ƒw]J«¨¨@=š¢í뛾}û’ÿ2[[Û””±X,wMáŽVÍþ£µúõë×ã«]]]½pá¡C‡"H/P~Í mll˜n ˜˜˜¨¸¦@ xðनËÈÈhÀ€ªÌ¹£%Sÿh-''§_m__ßèèèáÇ>Ò Ô5~üxr¢  ®Òó^ooïèèè‰'"„ðowAzAïÁU+ÍR”^OOÏèèè©S§’%ôóZôMQ´P÷ôº¹¹­[·ŽœHŒÄžôBß ØA:½NNNëÖ­S4‘¤¨ FΚ…Óëàà°víÚÀÀ@%§Áp·††¼`Á×ãš ó}ó°°°!4~üxŠ¶ß ¯^½222ÂË^^^§OŸÆË§OŸ&§PRT.mÆŒ»ví"&%%Íœ9/ã×3++kÔ¨Q•••ÔìGð$æþþþŒÔ®ÏðφØÛÛÓV#Ëû^i§NòòòÂËï¾û.^öððÀ³O+)—vîÜ9‘HD>‰DçÎ#nß¾ý»ï¾ËÏÏwtt¤b/€Ö¢ÿ˜×—«V%%%‘‘‘yyyøakk+yÿjÿþý[ZZ”—K{þü¹……ùÐÒÒòùóçäÃ¥K— *öB0rf ýǼ^ô½………sæÌÉÈÈ5j.éׯ_kk+^niiÁs+)—fnnŽçÝÆšššÌÍÍɇ‰‰‰AAAx2z W ½š—‘‘±`Á‚¬¬,²P(^½z/—””…BååÒ¼½½srrȇÇ÷öö&.^¼8>>~„ %%%ß Í8æ):Ÿ>yò$BhÒ¤IªÿÉÇ#""š››5ØŒ„„„aÆu¿€´mÛ6//¯úúúººº±cÇ&&&*/—vîÜ9++«‚‚‚ööö‚‚++«óçÏã§È×3==ÝÂÂâìÙ³Ü?~!4mÚ4ú«ÖsxN\r^kP•Þ'N „&Ož¬ÊÊwïÞ 744Di6½Ýß­ZZZ‚H$+W®433333ûæ›o$ ^_Q¹Œììl¡Ph``àèèHÎBJüu†Ô¬¬,:Šée {Ò‹¯M™2Eùj555aaaÒ“å·¶¶RÔ$=GõS§Neº!z‡þ_ÿaìšseee\\\FF†ÌOÂ+¿­Õã1¯q ¤·¬¬,..Nf>>¤è(–§·¤¤dýúõøDQÑBzÕŸ÷2…µé½xñâúõësrrz<ª ½@G±0½gϞŠ©ÂÁÁÃáà)…˜rIGk'þ¼ôMÑ(ÂÂô¶··“w/õˆÃáܹs‡¢&@)¦w„ &LÈËËû÷¿ÿ}åÊåÅårkjjÈ©t™pXGkÇHÑ¿(ÁžôÊðóó›2eʱcÇÖ®]‹ÚW..—ûÎ;ïÐÓ$¶:yò$þ˜é†èúÓKß}Î'  ¤¤äàÁƒÎÎÎò[—¬XJ&9asz1‡3{öìÒÒÒôôôî¯)¤—­ôa’úÓKÕ’YYY¡Y³f)YG,§¤¤ØÚÚ’éÛ·/Eíѽø~ÍØ:ÉÉ… BcÇŽ¥§:‚Ù¹5x<^hhheeeRRÒˆ#ô½ú­“œ¨rÌkóßïåóù‹/®©©ùõ×_œ’‚5´ü^+<ÉIbb"~Hé$'‰‰‰tQú˜^ÌÀÀ`É’%×®]£¨=@°{’ýM/f``@MsóX?É ýé¥êªUFFB(00¢íEòóóB&L`º!¡“œœ9s!ôÁÐPFUzÓÓÓBAAAm(òwÓ[WW·|ùò‚‚J[Õ½Û`ß$'ÅÅÅ!ê¨Jï¾}ûBóæÍ£hû@ÕÓ{ÿþýˆˆ###„P^^ mc·¢¢"„¯¯/m5êË|ÎúC•kÎwïÞONNîììÄ%ðY.‚ôê—[·nÅÅÅ¥¥¥‰ÅbérH¯úè?æ!½l£¨ï­¨¨ˆ‹‹Û¿¿ÌDb¤W}^ yeeeëׯÏÊÊRòÍAH¯ú ½@“JJJbcc³³³•Ÿ#H¯&@zºðkÞÒÒ"‰rss{Ì-¶bÅŠššMÍΣ:W;¾ñ Ò Ôehhhkk[PPÐÖÖ¦Êúmmmrï%¤¨ËÐÐpÓ¦M«V­Ú°aÃÎ;;::”¯¿eË777MÍΣ:Wû›7ož={é½'}ÍÙÆÆfË–-QQQqqq»wï&?ÝíÎØØXî7€êNœ8áçç§¿ßRT;v쨮®^´h‘ôOFIãÂU+µÑÌÃÿŒm}Þ;räÈß~û­¢¢"$$„ÇãÉ< éU{Ò }¯v²³³KMM½qãF`` tb!½êƒô:Œ=:##ãúõë³fÍÂÿ#H¯ú ½@]ªÏŒãââ’••uõêU‘HÔ}, þ.¸[ÐÍÝÝ]z&dÐkÐ÷ « ½@]Z>§$‹AzÐU^ .è{™é@WAzÐU^ .9ëH/š}/º Ò Ô#g¦@zÐU^ .è{™é@WAzÐU^ .93Ò €®bOzÐ7ìI/ô½LajäÌáp¥KrssÉ€Ó .¯««ûè£ÌÍÍßzë­÷ßÿèÑ£ÊË1GGG;;;™”––ŠD"SSScccŸÃ‡+¯Zã ½@‡%&&Jÿ¼è?ü ý¬Ìï¾ã   ;;»;wî¼|ù2&&fëÖ­ÊËB—.]‹Å†††.\ +**üüükkkŸ?¿gÏåUkÌkÔÅàU+Ÿôôô„Pvv¶P(,**Rþ'—/_>uê”±±1BèÃ?üðÕ—#„’““à“““½¼¼paLLÌÚµkÃÂÂðCooשּׁ, ï^O ï:lÙ²e7nÄý[ttô—_~ÙãŸxxx¬]»ö?þP±¼½½}ÿþý , =pà@{{;./,,œ={¶Fö¢× ½@‡1B :tèСCC† ±µµ•~VîÉçšššÆgnnR__¯¼üèÑ£...ÀÚÚÚÝÝ=;;—?{öÌÒÒR¦"åUk¤¨‹ÙÏ{?ÿü󸸸èè课úJæ)¹'Ÿ666IIIõõõUUU–––ÊËñ°/‡……%''ãå466JWÔcÕét«¯¯ÿòË/ 5²µÉ“'·¶¶J$’‰'þ­?´²²Ú°aÃåË—•”?yò$//oÑ¢E¸ =qâDCCBÈ×ח쇙éêR½ï­­­]ºtéÈ‘#ùå—àWõÚkjjnÞ¼©âúþþþÅÅÅmmmñññcÆŒQR¾wïÞé^táÂ…iii¡5kÖ¬Y³&--­¹¹¹£££¸¸X#»ó·@zîÞ½ûé§ŸÚÚÚnÛ¶ _ø¡áwŒäž|FDD¬Y³ÆÌÌìwÞ)--Ý·oŸ’òäääE‹IosáÂ…xðìââräÈ‘]»vY[[›››¯Y³æÈ‘#Ê«Ö8øÄPëÖ­[qqqiiib±Xº\ýôÊííÉBEc‘H$‰T,/--•)ñõõ% ÿñäçç«Ø0*ЭÒË6ŠFÎqqqû÷ï—¾¡‚¿!¨)Ð÷M*++[¿~}VV–D"Q´¤W}0ršTRR›Ýã Ò«>H/P~Í_¿~-‰rssU<[±bEMM ÷¯x<—9:Wû‹/¤¨ÏçÛÚÚ´µµ©²~[[[KK Õ­Ò^Ð{ø5çóù›6mZµjÕ† vîÜÙãǹ[¶lqss“üUWW—„9:W{KKKss3¤h†Í–-[¢¢¢âââvïÞÝÙÙ©hMccãþýûÓÙ6öÙ¼yóòåËán  I`ÇŽÕÕÕ‹-âóå¿_s᪕Úà^+ .EŸ÷Ž9ò·ß~«¨¨ áñx2ÏBzÕÇžô2…£÷ó³(ggg—ššzãÆÀÀ@éÄBzÕÇžô2Ø÷êùü,ª=ztFFÆõë×gÍš…ÿG^õ1pÌÔX¹r%BèǤhûŠ „"##SSSñÃ#GŽ,[¶ŒÜMEûk``ðúõkÕË ‚ˆˆˆˆÝ¸qã’%KÈÂÀÀÀ­[·*j˜ê{¡Ž²²2„‹‹‹Šë—””ˆD¢ŠŠ J[¥B+V¬ ­Fö½z>?Ëßåîî~ìØ±Ñ£G3ÝGÿ1ÏÂôêùü,ª¿h¤W3ôy~ÀýM/ÌÏt>¦ægÑ,93E¿Ò ó³6¡?½ÌÜç ó³öaza~ªÁÈ™)lN/ÌÏ€fÑ‘^˜Ÿ…NÐ÷2…m}ïÇa~škúô)EÿS ÛÒkffó³=Á¶ôšššFEEÁü,tÖÞÑÑñôéS9ÓméÅ{ó³Ð©ªª ¾rÀ6ß­ó³vcsz1˜Ÿ…jpÍ™)ìO/ó³öÑ—ôb0?  ïe {Ò«:—¬¬¬«W¯ŠD¢îcit;¯9«ÏÏBQc {ú^fçÖÐg0rf ¤]é@WAzº`äÌH/º Ò Ô}/S ½è*H/º Ò Ô#g¦@zª‚ô Ð÷uÁÈ™)^t¤¨ ú^¦@zÐU^t¤—B'Ožœ}:BèÈ‘#m_M¯^½222ÂË^^^§OŸÆË§OŸ7nœòri3fÌØµkù0))iæÌ™x¿¶YYY£Fª¬¬¤f?ä¸ÿ>BH ÐV#Àþõ¯!„¶mÛF[ÌÏJLjS§Nyyyá劊Šwß}/{xxTTT(/—vîÜ9éŸç‰DçÎ#nß¾ý»ï¾ËÏÏwtt¤b/€V!ôvV::•””DFFæååᇭ­­ýúõÃËýû÷'MQ¹´çÏŸ[XX---Ÿ?N>\ºtiqq±@  b/€¶¡ÿ˜×£ó^¬°°pΜ9£FÂ%ýúõkmmÅË---ä))*—fnnÞÔÔD>ljj277'&&&•——S±#ŠÀU+¦@z©•‘‘±`Á‚¬¬,²P(^½z/—””…BååÒ¼½½srrȇÇ÷öö&.^¼8>>~„ %%%ß m8æ):Ÿž6mBèøñãm¿† ÖýÒ¶mÛ¼¼¼êëëëêêÆŽ›˜˜¨¼\Ú¹s第¬ ÚÛÛ ¬¬¬ÎŸ?Ÿ"_Ûôôt ‹³gÏöºå¿ÿþ»Š+×ÖÖ"„† Ö:Èq¼-IDATëê@ï|òÉ'¡;wÒV#Ué:u*B(''‡¢í÷B÷w®––‚ $ÉÊ•+ÍÌÌÌÌ̾ùæ‰D‚×WT.#;;[(8::J_c—~gÌÊÊ277ïuËMLLB¾¾¾ùùù=®üßÿþ!4tèÐ^WzgñâÅ¡¤¤$Új¤*½~~~¡ÜÜ\ж¯WÌÌÌÈwoooå¯*¤—)ü1BHúDªé×y¯Ž’þy§óçÏO:õ½÷Þ;zô(!o@W­˜Bÿ1éÕÝZñÊ•+Ó§OwwwÏÊÊ’H$Œ´ È€ô9ý0jiiéœ9s\]]322 ÃŒƒô9”ÿ¬qyyù¼yó„BajjªX,†‘³þ ö^«gÏž=|øPB®®.ж¬mÈ[¾”¨®®^°`Á¿ÿýï/¾ø‚¢ÿ)PŽþ‹ÚôQ´}½‚?1RÅÈ‘#GŽIic€"lK¯………±±1—J<Òík''§_pŸ˜˜__ßúúz#g&°-½?~Ï8wî\„Pff&m5BßË6Z8r–Æâùé?æáª »ç¤ÿ˜‡¾—m´¶ïeý|€^ÀNº;àôéÓÏž=«Êš óO¿÷Þ{¡K—.Q´} HCCBhРAL7ätw>@‚ LMM‘j“ŠÍ˜1!tøðauªû[¨J¯§§'BèòåËm(‚?M8p Ó ùŸî}†®ÌH„ô€\ù¤bôÿúUéÅ× ¯\¹BÑö"Z˜^&}m óôôÌÎÎîþž€ÊÎΦ­mpÞ €2ÝoQ4©ýÇ<¤—m´öš³ŽRts[÷IÅ ½hå·¦JO*†3 wk€ÞïyWWW}}=uSmÁ¤bÒð¤b}úôAôŽz ½ìôòåK¦[ÁªO*fllüúõk:¿Fée'.—keeEõ„[0©†'‹ÏË˃‘3è=üš›˜˜ÔÕÕ1Ý6àª<©Ø÷ßà¼í¡(½Ý'£ÿ˜‡ô L÷ô*šT Ò ÔŸ÷jWåIÅ ½hîŸ“Š­]»600PÉi0¤¨ ú^Ͳ³³[½zõüùó¥EY.H/ÚåðáÃ*®Éž;%Ð7ìI/ô½L‘3S ½è*H/º Ò Ô#g¦@zÐU^ .è{™é@WAzÐU^ .93Ò €®‚ôTée93ú^t¤¨ ú^¦@zÐU^t¤¨ FÎLô « ½@]Ð÷2…=é@ß°'½Ð÷}éê‚‘3S ½è*H/º Ò Ô#g¦@zÐU^ .è{™é@WAzÐU^ .93Ò P¤Í€¾¨ FÎLô « ½@]Ð÷2Ò €®‚ô « ½@]0rf ýÇ<Ÿ¢íBz¾4hX,æó©ÊTwŠÞ¤¹\.AÐÐ .—Ëáp$ ÓmÔ¢ê}¦ÀxGPÕ÷¨öÿ¦ŒšÊ½[€õIEND®B`‚kamailio-4.0.4/doc/sip/figures/companies.png0000644000000000000000000011440412223032460017471 0ustar rootroot‰PNG  IHDRCZ’jÎsBITÛáOà pHYsÐй‹çŸ IDATxœìÝg\ÉßðIBï¡KQåD”¢*ž rˆg—³ bÁ†§`÷DTìí¬'¢ÞÙËé)XUŠˆ¤÷&5mŸó¿}r!Ä€$›„ù¾à³ÙÝý%$ûÛ™%aADb‘‰AA¾ Êd‚ ˆdC™ A:‡D"‘H$YYYCCC//¯””ÎEÎÎÎ\…áDqqñŒ3¨Tª‚‚ˆ#ÂÃÃÛoY2‚´‡2‚t†ammm #FŒ3fLRR¾HYYùÎ;íWñòò277ÏÉÉ©¯¯>uêT×Ê Â† HgpýjŽ?>iÒ$|Qjjê€èt:WaYYÙææfþ[î¨ “Éܸq£ŽŽŽ¢¢â¬Y³ñ9rÄØØ`ffÆf³á|6›mjjšššúÍ)JW>3èš A¾Ë´iÓ^¿~¿´²²rvvþã?¸Š :tûöíyyy|6ÕQ™ÄÄÄäääòòryyù 6à‹âãã“““1 SSS‹‰‰3£££©Tª••Õ7Wd2™]{×"^ˆN¥"a¸~5 CFF†sQII‰‰‰I}}=gᢢ"Ÿ^½zihhÌž=»¸¸¸ý–;*Ó§OŸ>Àé²²2===|w%%%púøñã³f͂ӿüòËÉ“'\A¤ Cýɤ3H¤ÿüjJKK­­­+**8·µµíÞ½›«0 ¬¬lÏž=III¯^½êh\edee±“"›Í&‘Hl6›+’ÚÚZ†aX¿~ýòòò444YA¤úB#Hçp¥'NDGGÃVø¢ææf›˜˜##£ö?±¦¦&*•J§Óùì…³Lï޽߼ycddÄ?///''' î\¹"øŠ"éÐ}2é 6›]ZZzâĉ   mÛ¶q-URRÚ²e ç|OOÏ/^´¶¶VUUíÝ»×ÆÆ¦ý6;*³lٲŋçää0™ÌŒŒŒÙ³gó ÉÇÇçܹsaaa>>>ZA$ž¨«3DÂÁ…BéÕ«×Ì™3SSS9áÓ,ËÎΟ1räHyyy555OOÏœœœö[î¨ ‹Å éÓ§¬¬¬Í­[·Úï366îÛ·/ÞˆQÀDÒ¡JAD²¡ÚEAD²¡L† ‚H6”ÉAɆ2‚ "ÙP&CA$Êd‚ ˆdC™ A‘l(“!‚ ’ e2AD²¡L† ‚H6”ÉAɆ2‚ "ÙdˆAzÐÐÐ?þøã;7âááÜ-ñ ˆt@™ AD§´´4))©oß¾22]üéåææZXXtoT"éP&CQ ÒÒÒêÚºÓ§OïÞ`D  L†  Ñh­­­Z…B¡üðÃBŠA$jñ ˆŠŠbtRdd$ÑQ#ˆ˜B×dBUUUkkëN­’‘‘!¤`DÒ¡k2AD²¡L† ‚H6”ÉAɆî“!’““;{ß«ººzöìÙBŠA$ÊdB[[ÛY³fuj•k×® )‘t(“!H$™Ü¹º}‰$¤`DÒ¡ûd‚ ˆdC™ A‘l(“!‚ ’ Ý'C¤¤¤¤§§wj•††ÔvAxB™ A`ccÓÙ´tõêU!ƒ ’Õ."‚ ’ e2AD²¡L† ‚H6tŸ A€a“ÉìÔ*l6[HÁ ˆ¤C™ AðþýûÌÌÌN­R]]=wî\!Ń  e2!À!CPÛEé.è>‚ "ÙP&CA$Êd‚ ˆdC÷É„_¿~MIIéÔ*UUUB A$ÊdBGGǦ¦&>ð—………îîî A$ÊdB€/^ 8°£¥,«¸¸xÆŒðeN^ÎùÆóSò¦˜››‹*@‘$(“!´µµÇ×ÑR&“y÷îÝÓ§O6,##ÃÕÕÕ²ÖÒÃÕC”"ˆA->DL 6,((è矮««1"—f‚ˆ)tM† hnnæÓÓ™ÍfëêêÒh´   ‚‚‚Aƒ:¬‡D L† „ðõõ°¤ _¿d2ªAAÞÐoA$JcÂúy ‚ ’ e2AD²¡L† ÄHLL¬¨¨€Ó>„ÅÅÅøØøLÎi6›ÝÖÖ&Â0D L† ÄhllĶYWW'˜LfKK ×L@}}=œÀ0 Ã0†‰ e2AD²¡L† ‚H6ÔŸ A¾ƒÁ Ñh………EEEóçÏG-æDÄP&C´µµÁtUø_555°Ì’%K.\Hh˜Ò¡L† ¼½{÷îêÕ«xÞª¨¨àßÔ‚J¥îÞ½[dá!‚C™ Ax333»~ýzee¥€åwìØ¡­­-Ôá Uè#BôäÉ777EEE--­¹sçâݧ:ÊE¥RFŒç“H$|RUUuwwÿüùs§6‚¯;nܸ5kÖxyyµ_ÝËËëìÙ³cÇŽ øNìçç'`aAºÊdˆ:tè×_­ªªÊÌÌTWWŸ5kÿò^^^æææ999õõõÁÁÁ§Nj_v¨ÊËË:tèìÙ³;µ¸nQQÑèÑ£###cccóóó9×ÍÏÏ?>|9kÖ¬‰' òN;&#ƒj8„(“!BùÓO?)++ëêê8pàõë×üËÇÇÇoݺUKKK^^~̘1=ꨤ¶¶ö¶mÛÒÒÒº°uuõÀÀ@æççwôèQÎEÇŽ[¶l™‚‚>çÔ©SêêêüÞ1cÆ?þÈ¿LwÁ0ŒÍf‹f_")P&CD$**jøðáüË :tûöíyyyßÜZUUÕ®]»¬¬¬º°‘úúúýû÷ÛØØ,_¾üÚµkøPõõõ×®][¾|9ga##£ýû÷ó‰DIIéàÁƒß ߟGE ÂËË‹Á`|ÏDú L†ˆBRRÒºuëBCCù»}ûvuuµ³³3•J3gNIIIû2ð^Wß¾}ããã/_¾Ü©Àu544Μ9sãÆ --­3fœ9s. :ujûVÓ¦Msqqé(æ7öîÝ›ÿûÂ¥¥¥á|šššòòòpºW¯^pBAA¿Ägôõõ9ßÅÖ­[Ü#‚ô$4†"l111ÞÞÞÿý÷СC\¥¬¬lÏž=III¯^½Hÿû¢â]Þ†a¹¹¹>>>'N¤Ñh£GÎÍÍ%‘Hýû÷ŠŠêׯçFÚÚÚÆŸ››[]]ˆˆ311ùøñ#gm$ëÖ­kkkûòåËôéÓµ´´|\®]»fkkûèÑ£GÉÉÉum#"eP&C„ëÆëÖ­»ÿþ!C:µbSS•J¥Óé «™¬£ ?þ¬¢¢2cÆ OOO99¹[·nݹs‡su ÃæÏŸùòe###Ÿàà`®íß¹sgÊ”)ãêê:}úôððp:.`òkOEEeèСmmm‰‰‰\Ñ"H…j!:|øðúõ룢¢¸ÒÞªž‹§§ç‹/Z[[«ªªöîÝkccÓ… ²‘Þ½{>üêÕ«€€€€C‡>|8 €«Øž={._¾¬ªªþÛo¿ÙÛÛs.uuu<­[·î‡~ ‘HòòòŠŠŠ¤®jjj’““ÓÖÖVVVFuŒ¡k2DˆÚg¬¯_¿ª¨¨ttiõàÁƒ}ûöÅÇÇËËË5êèÑ£fff “×dßܹeË–ÄÄDÀ¨Q£˜Læ›7o8·1iÒ$@TTÔ˜1c>|:t(¼Â“••MMMµ°°ð£€d¥ð.À0ìæÍ›¨ŽAÊdÂSvv¶µµuKKKHHȦM›ðùAAA;vìüú믇pkðÏ––ߣªª Õ1"@™ AÚknnvttLOO÷ôô¼ÿ>ç…N:theeeVVÖ7û™áºý‚ Û§OŸ]»vuû–D‚ L† Ü-Ztþüù$$$¨¨¨p-MHHÈÌÌÄÇù¦?z{{ëêêvw˜ÿS^^/¤#ˆD@™ AþãÚµk³gÏVRRŠ‹‹4hÑá òm¨í"‚ü¿üü|8ðï¿ÿŽÒ‚H tM† ÿÃb±ìíí“““§M›vûöm¢ÃADPèš ¢ëׯëééu¹¥ƒ0šHðqäÈ‘äädøTA$º&C„¨wïÞׯ_wrrêÚêÑã{deeÙÚÚ¶´´þ¼öˆ H7Bµ‹ˆ°À>>99™Éd¶ßÈË—/ÓÒÒòòòJKKûí·ö‚‚‚>|øœœL£Ñ àL>û*///,,ܽ{÷ܹsËËË 6nÜرcÇ”””ºõ3@D$0Î/˜ÁçÏŸáô§OŸ ð2%%%­ž §³²² Û—122ÊÊÊâšÉg_ÕÕÕ†Á«48=oÞ<€»»{—Þ¢°”——‚H tŸ "Î]222mmm Àd2á³ùÜ #‘HL&³ý*œdddZ[[¹j&ÙœNHHpttd³Ùyyy}ûöí¾·þ]JJJ œœ^¿~Mt,"Pí"""zzzyyypšF£ééé ²ç*œÏMÆõêÕ‹F£um_†­Y³†ÍfÄ'222%:(!ª¯¯·°°`2™D" ׯ_üòË/DÒ¡ˆˆÀ„ Äsäg(“ýÏÙ³g}||æÌ™“™™ÙÜÜA"‘¤ûŽEXXXiié­[·ˆDè***bccÄpä{\xx8ÀÓÓ“è@Da†¥¥¥™˜˜TUUqÍ !$`±XfffOžÇÒÒ/|ðàAÎMíÛ·oîܹ<÷ÛÐÐЫW/|E<BOŸ>µ±±Á0ŒÁ`¥§§ã‹jjj444ÊÊÊxn655®ˆý[ÍH£Ñ:zï’.** `kkKt ª©©!“É222íë·1ñæÍ›)S¦èêêÊÈÈèééM:5**Šè º×uŽŠŠŠÝï¿ÿÎb±ˆíP&Ã02™¦¦fyy9眲²2---8 ¨¬¬Äµ´´´Ÿ#//...æÜTQQ‘¾¾>œnnn ìÛ·¯ŒŒ ü2‘Éd|E®«Š¦¦&8²hÑ"|ÑæÌ™ÓÑÛñññ¹páþrÏž=þþþ|Þ¾D lذè@:tîÜ9€››Ñ ¼ýñÇ ¸}ûvuu5NOHH€Ïh%:®îÇù¦Ølv}}}LLŒ“““ø¤ðŸÑfff999| P(&“É9‡Á`P(8Ýþ;Íg ý¦dddà´¯¯ïÏ?ÿœœœ ïü3 ÎۆϬªªÒÔÔ„é“Åb™˜˜¼}û–ç{©¬¬ìÓ§g殨¨ÐÖÖ®¯¯çY^Ò 2ðìÙ3¢éиqãPÕ¢ØêQÍÁxdŠ‹‹ÕÔÔDL§ L†aæëë{òäI>455+**8çp]“q•çŸÉÚ_“áUˆœ;ÊÉÉ0“a¶xñâ;wbvïÞ=;;»ŽÞË®]»xÞ1åªó”uuu ENN®¹¹™èXx+--%‘HòòòµµµDÇÒiRy]ÂeÑ¢Eü†Ý¿ßÁÁAIIIIIÉÁÁ!""_¨¯¯÷ññ¡R©jjjþþþ ãëׯ‹-RSSSWW_¹r%ƒÁÀ 744x{{«©©)))¹¹¹¥¥¥q´tÙ²eŠŠŠºººË—/ojjÂW¤Óé[·n500••íß¿ÿñãÇá"‡ððp®€SSS ñýrFÛþÝ•––jhh|ãc"šô‘––fjjÊç´ËÃÃ㯿þâ\ÆyŸŒkEþ™ìðáÜ‹80oÞ<8­¨¨XWW‡/Úºu«à™,##ÃÀÀ€N§»ºº^¼x‘ç;¥Ó鯯Æyyy\ósrrLLL¸.¥lbêììLt :tè`Ê”)|Ê<|øÐÅÅE^^žJ¥zyy‰,<þzB&ëQÍÁ¸þ¡ /^¼9r¤Oç>5‘“þ/¢€Îœ9caaqûöíšššöUáÏž=Ó××ojjjjjúçŸtuu9¿¬\[ãŸÉ¼¼¼ÂÂÂêëëÏž=«§§‡Y§L™âããSUUÕÐÐpüøñ©S§ žÉ0 suuݲe‹ŽŽNG·ý®^½:qâDž‹&L˜póæMž‹$ìG±qãF¢éµµ5àÎ;|ÊüøãwîÜ©­­mhhصk×ðáÃE=!“õ¨æ`Å—Þ»wÏÞÞ^QQQQQÑÞÞžó‚½³™ Ö6hjj „ÔÔT¼XeeåÔ©S•””¨Tª··w}}}§2ìU¶iÓ¦ŽÞ£ƒƒCdd$ÏE?vttìhE oAÁÞZbèÇ--­¶¶6WimmÅ›ùt°wï^]]]%%%oooü@ 8r䈱±1¼ÅÛÒÒâë뫦¦¦¦¦¶xñâ–– Ã\]]a÷ ⢢œœœ´µµñæKt:]GG¯ïhGœètúÚµkµµµ544:gòÜ5Ü`HH•JÕÓÓ»{÷nHHˆ¦¦¦žž^G_ZèQÍÁ¸Ž'Ïž=4hŸCŠ˜@™LÔ„zK§Ó „· Âf³544ÚÄǪU«xÑ7566îÝ»?ßïÀÃ㬬¬¼¼ÜÝÝ}ýúõøüY³fá‡Ñµk×zxx”———••¹ºº®[·ðääd+++ƒA§Ó­­­?þ¼jÕ*xÿðÇ{zz~sGœ6oÞìææVPPPSSƒ7ã¹k¸AÿÆÆÆsçÎ)++ãÓƒ ð#êv=ª9Ïí¤¤¤˜˜˜ð,/>P&5áe²ÚÚÚÝ»w‹¶Èdee ‰„·––*• HII¤<< çºqÒQI¼LVVþ JJJðbŸ?†ÓŸ>}200€ÓsæÌ9räÈ¡C‡1 £ÑhÆÆÆt:ð¥K—Þ¸qã›;âddd”••Å5³£]ª««1 kmmåœÆƒèõ¨æ`<·C§Óåää:ZEL L&jBÊdEEÅŸþY›À ™óB¬\ºt 0lØ0ÁW©¯¯9r$ÿbœ§öœ'õ\ß=΋ Îb·nÝ8p`Ÿ>}233᜙3g^ºt‰ÅbýðÃxM Ÿqí¥}¹ŽvÍaGÓ"Ösšƒu´÷ïß÷éÓ§£UÄÊdˆÔÚ¾};ÿ»†Ärrr„††vj­ææfü¶JG¸.•ŒŒŒðùœÅ x^Q9;;Ÿ;wîøñãÞÞÞpNBB‚]ll,çí>;âÔÑ5YGWœç9-z=¤9XûUššš¢££ Ä•_ÅÊdˆÔ‚?õ+W®))) A¿Ÿ;wîÇ FIIÉÚµküñG8¿£ã;`„ åååðö>¾ WùuëÖMœ8±¢¢ À0ìéÓ§ `2™L&ÓÆÆïÏäâââääôüùs®óÍmÙ²ÅÍÍ­°°°¶¶võêÕ|v‰k&ÃzFs0¬]ÛE…!C†„……ñYEL L†H-KKKÀû÷___غAÂ7oÞuêTqqñîÝ»»w³'aüã ºººS§NåææÂgõI¢@¡(..niiÑ××·48{ölKK‹§§§0Ò˜0Ô××?~üÙ³gD‚t‰DRTTtuuýóÏ?‰ŽEXP&C¤S^^ÀÔÔ”è@¸ÑéôßÿàïïOt,!‘H åøñãDÇ"å„tAÖ*ÞP&C¤SQQÀØØ˜è@¸]ºt©¼¼ÜÆÆfìØ±BÚE÷¹zÂq‘td¢@¡€™ÌÈȈè@þƒÍfEÅÚÚÚÆÆÆÆÆFQQ‘èx{"t`í‹õöíÛGEGGÇÇÇÃÉ‚#‘Hööö–––'NtwwWPPRœ ªª  ££Ct ÿÃ0ØöoãÆèœFüåååýõ×_=Šç_Å 'H$Rÿþýœœ–,Yâèèˆ.»EµÂÿ¶ÒÒÒÈÈÈGEEEÕÖÖâóuttÌÍÍÍÍÍû÷ïonntas`0¹¹¹Ÿþ•ŸŸ'?…1cÆLœ8ÑÓÓSÜjÀ¤ƒ»»;ü¯¹»»ËÿDDDLœ8ÑÀÀ 77Lj­ììì›7oÞ¸q#==Î!“É&&&ƒÿebbà,‹Íf·µµ}øð!555%%%##ì 033›={öœ9s~øáâÞMAHà©ès IDAT/6ñÇ`0^½zµeË–!C†pžX 0à×_ŽŽîÂC)Y,V|||HHÈðáÃÉäÿÝ¡$‘HãÇüø1›ÍÆé±ìììðlZ°ÙlÞ;+4mïÞ½C† Áìêêê‹-ŠŽŽf±Xn„Íf¿{÷.00POOߎ«««xvÏ—&(“ýƒÁ¸pá‚———²²2þETVVöôô|ø°zõjø” è‡~X³fMdd¤P³KMMÍÞ½{ñ Feeåàààöc‡#Ûz†a›ÍIdètúåË—‡ c011¹sçŽ(>ð›bòŒ›Ë—/ŒŒŒÐ9Š8())Y±b…œœü¹ùøøüý÷ßBý×/[¶LVV`hhøäÉáí«gê¹™ŒÅbݸqÃÂÂ~›ÕÔÔüüü|æ¡ð„‡‡ãÃR¸¹¹¡êˆ.ƒgÁMMMD‚µµµ™˜˜.\¸@t,=ƒÁ8|ø0¼#“ÉS§NMHHÙÞsrrà¥9‰DZ½z5ªgîF=4“…‡‡ã Š455=ú=uâÝ‹Édž(ú?ëíÛ·ø™ëÊ•+E\ï1™ÌøåÔ××ÏÈÈ} R©Çe²üü|¼Yvï޽Ϟ=+>9ŒSUUÕÒ¥K) `èС999DG$a`‡-Á[ IMM <)á|f"b,ë·ß~ƒ †ÍÌÌóãÇ …B¡ˆÏøÅ=Ê¢E‹¦¦¦„ôûÛ·o¨¨¨ —Î’ÎLVPPGP\¾|9ѱ›Íž6mùæË—/D‡ÓC±ÙìÞ½{ããœíܹÃ0&“ ‡£]¹r%ÑöD·nÝ())Ih{ѹsçøõëW¢c‘$R˜É˜L&¬›0a‚?*¥¥¥eôèÑ€ ÖŒ„HHHÿµmÛ¶C‡ÁN÷ DØã|ýúö¿<}ú4ѱtQccã Aƒ³gÏ&:I"…™,88``` õmÆêêꬭ­–––âÖÏ °wï^]]]%%%oooü®;àÈ‘#ÆÆÆ ð––___55555µÅ‹Ã6f®®®111°|TT”“““¶¶6þߤÓé:::ð%F›4i’ŠŠŠ¼¼¼»»{yyyû`ètúÚµkµµµ544:gòÜ5Œ0$$„J¥êééݽ{7$$DSSSOO/22’k³›6míŒ=šD">ŠDϰ··'|l—ï‘™™ pÜ|,zÒ–ÉRRRH$™L·^B‚7Í·NK²²²òòrww÷õë×ãógÍš…§¥µk×zxx”———••¹ºº®[·ðääd+++ƒA§Ó­­­?þ¼jÕ*Xw‡aØãÇ===ñYZZFGG777××ׯ]»vΜ9íƒÙ¼y³››[AAAMM¿¿?Ÿ]ÃýýýÏ;§¬¬ŒO4ˆk³= xÁ‚R\ ¶²³³Éd2…B‘‚–P°ŽTYY9??ŸèX$ƒ´e2WWWÀêÕ«‰DtÒÒÒ`§é7nËÿdggÃ鬬,CCC|~II ^ÌÀÀàóçÏpúÓ§OpzΜ9GŽ9tèP`` †a4ÍØØŽ3´téÒŽÞiSS“ŽŽNûùFFFYYY\3;Ú5¶hmm圆‘¸ŒŒ ži Z¹r%Jf"¶xñbÀ¢E‹ˆ¤{üòË/°ÑH©Êd=‚-ú$®ÍÒw:}ú4@]]=77—èXþ€7€f0222ø|Îb …g±[·n 8°OŸ>øsmfΜyéÒ%‹õÃ?pvt}ûöíÈ‘#ñg|“H¤öÁP(”öc0v´kÎ;šÆ0lçÎ|2`éÒ¥]Ç%YJJJäåå)JûS UZZ Ÿ±wûöm¢c‘Ò“ÉX,–¹¹9ààÁƒDÇB€éÓ§ $&¤r]“áó9‹ð¼tsvv>wîÜñãǽ½½áœ„„;;»ØØX®“n##£ëׯ×ÖÖ²Ùìúúz®íãex^“utÕÈù.xNcfkkË?“|||P2 ØoúôéDÒા¾>j=ôMÒ“Éîß¿022“C¹ˆÕÕÕÁáb2à `„ åååð>Ù† ðùœÅÖ­[7qâÄŠŠ X, ð§OŸ0€Éd2™L›´´4XØÅÅÅÉÉéùóçœÑÒÒºsçNkk+FóòòÂq–Ù²e‹››[aaamm-^óÌsט`™ŒF£}3AsçΕ Î¹ 6ÄÅÅKwb±Xð9òÁÁÁDÇ"î¤'“ÁÑZñ–i=Ѓ<Ûï‰`ß¾}°íâÂ… 9[rãl@èëë ‹5 ¯Q‰‰‰?~<œïÓ§›ÍæÜÈýû÷ÍÌÌdddLLLN:Å3“ÑétMMM*•ŠÇÁsט`™ 6µ„Mbbâ÷|’È7EDDÚ7É‘ð¼MSS]–ñ'%™,++‹D")))ÕÔÔ ‘<<<‹/&:îŒÕ-Nž<)&#¶àOꈹ¹ùöíÛñ›|ˆPÍš5 °gÏ¢ Ø;VZß]w‘’LŸg(GpbeeeÉÉÉ‘ÉdÂG±êöLVWW7`À€âââîÝl”””Àa©ÛëÝ»w`` ´— $IZ¬GEE´´´šššˆŽE|ñþAJø Ø ·'377‡í¿¿Ù²N²H$--­Õ«W ¸wï¬áÄéêê®X±"666??ÿþýC† !*¶èÕ«W­­­öööðáÒgܸq666ÕÕÕ°““4d²?~ùòEOOÏÎÎŽèXˆ·nÝ:YYÙ»wï†aÝ»5&“éçç×Ûì²;wîÀ ooïÈÈÈâââ'NŒ1‚D"[ €UpÒjÅŠ€+W®ˆø’†LöøñcÀøñãÑq```0eʃqöìY¢c‘B555ñññ^^^wïÞ-++;þ¼›››ŒŒ Ñqõ\0“9’è@„hÚ´iòòòÑÑÑ¥¥¥DÇ"¦¤'“¹»»ˆ¸€gp'Nœ`2™DÇ"mH$Ruuõõë×'Ož,//Ot8=][[[||<™L1bѱ•Jõðð`±X7nÜ :1%ñ™¬©©éåË— ÅÍÍèXÄŨQ£LMMkjj’’’ˆŽEÚP©T …BtÈÿ$$$´¶¶ZZZ§8I1ø§˜˜¢S_+ÓÖÖæè訥¥Et,bdܸqgÏž}ùò¥ƒƒѱüG}}ýׯ_áƒÞá_Ÿ£®®.øÖBCCÿøãï ÉÃÃ>?‘8=á&z—˜˜H` ÅÅÅí°ð¯ŠŠ ±uìŸÉPÕ"O£F‚™,00Pd;Å0ìË—/4íË—/•••ÕíTUU±X,þ‘‘‘éÕ«—‘‘‘±±±‘‘>all¬¯¯Ïu=TZZš””Ô·oß.ÿŠrss-,,º¶.B¸žp“ êׯŸ††FIIIII‰ðZðÒéôÄÄÄÏŸ?—••Ááoð¿•••\Mv9ÉÈÈôîÝÛÔÔÔÄÄÿkjj*² ”ÉxƒÑvï6E vÝÞ\KKKî¿h4œÈÏÏg0üWTSSSUU…]@8G‹Ç§ëêê ß¾}˵®‚‚Â!CñV×AAA]þÁÀñ*‘î%šŸ‹Åzóæ @ºo’A$ÉÖÖ6&&æÃ‡ݕɊ‹‹?}ú”É¡¬¬ŒOy|lRü‹ÿ­««£ÑhíGq322úñ_}ûöí–°y’ìLöåË—ÜÜ\*•:lØ0¢c/ð¶AKKKwm°°°0---55þÍÎÎnuE"‘ ÍÌÌÌÌÌ´8hkkãß¼xb0ÅÅÅEEE………ø_8Q^^þæÍx𘙙éêêÂiÂ"8 …ÒÑÆ‰™™Y__ojjjhhHt,¢¿ÙÙï9§¦¦¦ÄÄĸµï¨###cmm=xð`]]]]]]===ο hkkƒõ14-//þÍÍÍ-**ºtéÒ¥K—666sæÌ™5k–0þ_’ÉÒÓÓvvv|>âïÔÚÚºjÕª›7o¼¼¼~ÿýwø00‹µuëÖ°°°ÆÆÆÉ“'‡††â0a|OÛÅœœœ¨¨¨¬¬¬ÔÔÔøøøææf®æææýúõƒyËÔÔþ…Î÷••íÛ·/ÏÓ·úúzüGøîÝ;x!EEE9::vjGOŸ>E™L¢eee,--‰DDºö£nll¼|ùòû÷ïãââÒÓÓ9k©Tª¥¥¥‡Þ½{w­/“¼¼¼¹¹9| ðŒŒŒ˜¥¤¤¤¤¤¬_¿~òäÉ~~~ðY’ÝE2Ù Aƒ„·‹-[¶”””ÀÇÌ›7oëÖ­„„„$&&&''«««¯^½zÆ 'Nœ^OÜ:UÃÓÚÚš””/zÞ¾}[^^ιT[[ÛÚÚÚÊÊ þ8p èÛ «««»¹¹ÁFªl6ûÝ»w7n„wJTUU­­­;µ5þÊ”J$iïÞ½‡nllôòò:}ú4ü'’H¤#GŽ>|¸¤¤„Édò<{sssÛ¼yóèÑ£OŸ>ݾ}ûçÏŸ333µµµ ÃÐÐ0##ŽIØ·o_ûqb07nü믿˜Læ¶mÛÖ®] :>q$‘H!!!““;sæLffæÁƒét:€ëè)ÅàûäGÝØØøâÅ‹¨¨¨§OŸ~üø_EVVÖÖÖÖá_æææBíƒK"‘ 4hРU«VÑéô‡^¹r%""âîÝ»wïÞuppغuë„ º%ÉÎd>|BÎdׯ_þü9¬Å:~üø˜1c`& {ðà‘‘`ïÞ½ÖÖÖb•É"##ßl¸XZZúöí[˜½’““ÛÚÚðEÊÊÊnnn¶¶¶vvvÖÖÖâ0L'2™ìää4nÜ8˜É½|ù2--D"-X°à·ß~Û·oœŸœœ Óϳ·}ûö-\¸)pëÖ­ãÇŸ9sfëÖ­€èèh<ñÙ.((èÇÉÉÉ***;vì€3;:q”——^¿~}îܹ¾¾¾ãÇýúuÿþý…ý¡‰6› ïÛØØð,Àd2aöz÷îL{99977·áÇ»¸¸ØÛÛËÊÊŠ.hrrr“'Ož {DÏÍÍ-**êØ±c«W¯æœÏb±ÒÓÓßü+//_D¡P,--þeff&ò¨;-88xûöí/^ŒŒŒœ={v§Ö½zõêìÙ³§OŸ>cÆ X/õH$Rvvv¿~ýŸ?3fLQQœ_RRÒ«W/XÌÐÐðùóç0Cdee3ÞP™;w®›Í.++Û¿^^ž‹‹Knn®¬¬ì²eËÆŒ3sæLþ;âdllüìÙ3®CXG»†Ò555ÛÚÚà´³³ó›7o¢££üñGa~lb!..ÎÑÑÑÄÄ„«UENNΓ'Ož>} Ÿ4 ‘‘6l˜««ë¸qã‰Ê^|´¶¶ž9sfïÞ½ååå Û¶m üž8%øšŒÁ`|úô‰L&8Px{ÑÓÓËË˃¿I¦§§ç÷êÕëÍ›7ðšLÜìß¿?**ŠB¡L™2ÐÔÔ÷êիׯ_¿{÷®¡¡/©¦¦æààS—£££ššqQ#¢`bb'LMM9+ñ4(//755…ÓfffpzòäÉÛ·oojj‚ †MLL†~ãÆÙ³g?þüèÑ£‚ìWZZŠïå›»hjj`-%œÎÉÉô„k²ÖÖÖeË–&Nœ¨««‹ŽŽ~òäÉ“'O8OFÍÍÍaöúñÇ;Õ/SôÖ¬Y³`Á‚€€€ .lÙ²åï¿ÿ~ôèÞ†«³$8“}þü™N§›™™ µ©Å¬Y³Ö®]†a˜¿¿?|`Ù²e‹/>~üxß¾}³²²vïÞ}õêUá…!¸;vfÏž}øðáW¯^½ÿž³aÿþýaö4hðˈRrrrgï{UWWwö2N pž–éëëó,ÓÑÙÛÑ£G×®]ÛÒÒ²ÿþóçÏýüüúöí;bÄ®Æ>ßÜQ¯^½h4×5YG»n¯¾¾æ9©o¸ˆaØìÙ³SRRäääGŒñîÝ;ü­­­=vìXWWWwww‰û(¨TjXXØŠ+¦NšœœlggwÿþýŽªOù“àL&¼›d­­­ø ê]»v­Zµ þ´fΜ‰?-eãÆ$iܸq%%%–––[¶léö0ºàñãÇ0ð3XÕàü/γo©akk‹ŸdèÚµkB Fœ­Y³&!ÿ9sæð,ÃóìíÙ³gÕÕÕ .ØÙÙ¥§§<ØÎÎNYYyÆ !!!\}Èx̂ V­Z¦¢¢²}ûöcÇŽu´kž²³³ñm~ï‡"ÞBCCáS«èt:¼Ý(++;bÄØúiÈ!’~2:dÈÄÄÄ©S§ÆÆÆ><""bìØ±Ýˆ0“ <¸Û·üçŸâ áBCCëëëëëëCCCñO2™¼iÓ¦üü|:þþý{1é`«ªª ¿ÖjjjnnnÁÁÁÏž=«««‹?räÈôéÓ¥2à9"õG@žF5xð`}}}ü¤‡Ë®]»ôôôúõë׿###xö¼k×. …B¡PŽ9‚P\\Ü~¼¨oîhûöíÖÖÖpH>»æéóçÏ~ó’ >Gпÿ+VÜ»w¯ººúùóç›7oj$QÒÖÖŽŠŠZ°`Akk«§§çóçÏ;» ¾&^ü£G^¸p¡Û7+ÎÎÎõõõUUUBíNH®õëׯ_¿žk&W³/xöÊ9óÅ‹øôèÑ£as|@AAÁœ9s¸.Èà4ÿÉÊÊ=z”ëîÏ]s­§á5ÙæÍ›;|«Òbþüù“&MÂOR¥•¼¼üÅ‹UUUOœ81yòä/^tª_g²ÌÌL œ~‘pËJEEEEE…è(¡¾¾þøñãÏž=ý®a§xXí/õļùF7:vìXeeå7~üñÇ´´4Á›ÔIj’g³Ù_¾|!‘Hí›?!±0 ðùÑx‰‡Êä\töìY//¯ökyyy={®Kâ…çünxcí‚×ÒÒZ½z5!} a³~cccÑï2™ü×_¹¸¸ÔÖÖΟ?_ð®M’zMVVVÖÖÖ¦«««¤¤Dt,ñRRR`m³à„ÔvñÂ… ]è\¨¬¬|çΩS§âsæÏŸ”ŸŸÏYWœŸŸûçŸ.]ºpT»µ´·}WÑîíµk@ÄͦM›V®\Ù…±‰­¬¬œ¹º|ùòk×®ÕÕÕÁ—õõõ×®][¾|y÷Ä*±êêêšššTUUQ_~iõóÏ?{{{·´´¬X±Bò(“I¡ÎÞ‘è»5b%>>>55uݺu][}ç·æ„EKKkÆŒgΜ/CCC§N GGü&)þäÑYOpäÈUUÕgÏž 2¶*ÊdR> ¯kë»5œsæÏŸ ?p¼[3þ|Î=âQr€ýW×¢’kÖ¬ ãz°µàzõêµpáB®Áv×®]{êÔ)ƒÁd2Ož< ÇŒ„ò(“õêêꀎ:>r’ÔûdÊd†½|ùòôéÓ)))ÙÙÙ¢êWKKkàÀ“&Mòöö†ƒÅ‰9 ½[ƒaXgŸÛ„ ¾ç)n\Þ¾}ËÙÁ±}+Œo °±±á¬?455upp¸zõªœœœ­­-!íÎCCC¹ª=»ÀÃÃ#88¸[âÿL†Ž9ÝÂßßÿèÑ£ÑÑÑ/_¾lßûž“ôg2¶xñâèèha‡ÄSuuulllllìo¿ývàÀ???1¯çÁïÖ¬\¹Ÿ¹|ùòihh€ïÖÀ1VÄÄû÷ï;Û °ººzîܹ€cÜ£ïÇ™·8Ó˜à)MIIiË–-Û¶m㜰xñbyyy8¤“è•––&%%õíÛ÷›üîHnn®……EwÅ#æ™ sº‹ººú¯¿þúÛo¿>|X 3›Í.(( ‘H}úôá_2..n̘1ÍÍÍššš~~~S¦L±±±éråO”””|úôéСC>\±bųgÏnݺ%æ}õwîÜéìì<þ|üv:~·fãÆ ówk8_ ©škÈ!]n»øùóg6›->ÿ”yóæq=ëÎÁÁACCƒÉd:99 ¾nÿ䃂‚´´´º¶n÷ç3™x>‰sº×’%K‚ƒƒ=zTSSÃçS"3YQQN×××WTTäS,))ÉÅÅ¥­­ÍÓÓóêÕ«ªªª"‹g`````0f̘øøø±cÇÞ¹sÇ××ެ*¶ð»5»wïÆg®]»vôèÑëÖ­#‘H'OžŒŠŠpk< ÿý·ššÚ¸qãÄálñëׯOžÓï.'OžìöL&•¢¢¢;µÊÓ§O»–ÉZZZ¢£££££+**j9TVVÂgšˆ[&CÇ!™={vddäÕ«W% “555%%%%$$ÄÇÇ'$$p>>Ž'*•êééɧÀŽ;¾|ùbmm}îܹÎ~¥mkkƒ3eeemmm‡ 6lØ0{{{ ‹ïù²Þºu«¹¹yĈC‡p•ššš}ûöíß¿ //ÿ믿þòË/\‚FUíáÇasvvŽˆˆxóæ ñ™ŒÉdFDDœ}:&&æË—/ø=‰DZ´hÑ¡C‡øŒ‚#''7yòäÉ“'ûûûÏ›7/--ÍÎή­­MKKËÈÈÈÄÄdúôéS§N•““ã¹úôéÓ—-[ß“«³øÜkhh8}úôéÓ§ýüü¦OŸŽ?¿›/^ð©kb±XÅÅÅ3fÌ€/sòrÎ7žŸ’7ÅÜÜŸ>Íb±8¿„ÔÓÓ³²²}ú„'¶ôôô¢¢"x ÁW122³š••Õ€$ýRƒ ‰Dêìe(þ“Ç0ŒJ¥®ZµÊßß_š²>:戀M\\ܧOŸD”É’““7oÞ ¯£•••ýüüôôôº}Gõì töâÅ ÀéÓ§û÷ïÏf³}||0 [¿~ý¤I“†-Y²$""‚B¡¬^½úÈ‘#JJJ­­­G•‘‘¡P(²²²MMM.../^¼˜4iÒÍ›7g̘A&“¯^½jbb’’’ráÂ…E‹<þüÁƒoÞ¼iÿ­‚÷±ñÖ.âãëׯü“ÔØØ(ÈÖÈd²mmmΗëׯçŸÉFŒqâÄ Û|ûúú ô°³³ôëw‚óˆ ï±}üø‘ÏŠ²²²ƒæl‡RSSÃyÑ–‘‘QTTTTTôèÑ#X@NNnÀ€œmZ·Ó-H$ÒêÕ«·oßN¥R‰Ž¥›¡cŽÀëÝœœžK»3“ÕÔÔlݺõìÙ³,KMMmÅŠ¿þú«ŽŽN7îBð[U]] ÇQæòîÝ;ðouöåË—“’’zõêõÛo¿Á¥AAA.\°X,oooørÉ’%?~|õê“É´···µµýôéSyyùòåËÇG¥RÕÔÔN:õË/¿¬^½ú—_~QVVvrrzðàAû3}@VV€N§ ñSàÀf³kkkùd&|Ž€!ÉÉɵÏLíçP©T>çïû÷ïÏÍÍå¹H__ÿþýsçÎÞþü¦M›ªªª(ÊîÝ»:jH#lðHÔØØxèÐ!žŒa÷ºÓ§OBBBàѤ¡¡aïÞ½UUÕÆÆÆõë×gddTVV***ž>}º¡¡aÔ¨Qmmm'N„]†544ªªª6oÞ ·ãååµwïÞ”””;wîÌ›7oÈ!€·oß¾}û–gßÿ­b0߬⫮®®­­í¨‡)>™ Ù-cðð¼W*##³råÊ   uuõïß…à”””úöíK£Ñrss;û¬g™Lîׯ_¿~ý¦L™ç455eddpVHVWW?þ\ô­HbbbN:µuëÖmÛ¶ÕKÄÿSQê9ÇÁq…„˜ÉÊËËþùçøøxÀ˜1cŽ?Nlß{ ƒò)`bbÈÉɉ‹‹SVVž9s&œýúu:®££SYY ضm›……ÅÍ›7åååÉd²††Æëׯ‹ŠŠðú%[[ÛØØØ?þøcÓ¦M½{÷øùù-]ºôÏ?ÿ„µÕ‚„ÁSsssG—Mœ/¿~ý*H£ ‰D¥Rù''H”OiiŸÉ\\\Nœ8ÁùTQ²°° Ñh?~ìr&kOYYÙÞÞÞÞÞŸS\\LH+’´´4‹µcÇŽ—/_^¹rv<è‚”””ôôôN­ÒÐÐÐÙÁ%ŽsÄŸ©©)‰D¢Ñh<ÇûþÞLvñâÅU«V566ÁG[}翟’’’ í½té†a3fÌÀ{_ž;w //ogggnnïÆ9H±ªªª……Źsç`MwBB‚³³ó‹/à·jæÌ™«V­ŠŽŽ.**222êÚ³ƒ›››¬q’‘‘á_¿_R©T1ì£Ã™É 80kÖ¬ï©NLLLìÝ»7ì×ñðáC@qqqee%¼¯ŽÏäœf³ÙðæÁÀá‚sÝyóæÝ¼yó¯¿þúžoUuuu—וZZZâP#$“µ'ŒV$íëÙlvHHHllìÕ«W¥£G—øócŽD033{õêUNNÎèÑ£¹u%“EGGO™2¥¡¡ÁÖÖöÚµkÝþð!HLLLœ8‘ë$ï®›ŸŸoiiÙÑê{÷îuqqinn†/¿|ù‚/?~<…BÉÌÌliiáÿø´nÚ´iË–-#:ŠÿgaaA"‘²²²¸Æ¬"V·´"IMMå¹ñØØX[[Û?ÿü¯tE„s¾Ÿæ‹n%uûömx711QÓø·M*ç½}¼šNMMíÌ™3‡b0­ž™™I&“•••a8|$++kjjŠaXvv¶°¢— ¢ïžÁŸªªª‘‘Qkkk~~>ѱð[‘øúú;v,&&¦ªª Ö=îÛ·oîܹVVV222° IPPÐÔ©Sû÷ï²Ó^UU•§§çúõëù|Û9aÆì¤ŽzYõ4è˜óý`&ãÙ¿»s×dgΜY¹r%‹Åò÷÷?|ø°ä:÷éÓ'g–‘‘;vì³gÏCCCããã[[[/_¾Üþ=>|øÐÇLJÉdêèè`ƒÜÀ³³³³²²¬¬¬„ÿVnchhXXXX^^þý#c‰Ò7[‘TTTðYð¼zõêÚµk}úô`÷ïßgffv*¼êêê¹sçvj©„Ž9ßް?‰S'2ÙéÓ§—/_N"‘vïÞ½yóæn‹ŽpT®öÖ¾¾¾Ïž=Û·o_cc£ŽŽÎµk× 8€ÈËË;qâÄ¥K—˜L¦‘‘QQQQee%™Lælf 7{ïÞ=øÅE$lNÉÙ8Bqµ"¡Óé‚´¦yûöí!CΟ?Où;2dÈÔv±kÐ1çûÁQëxŽ $èEÕßÿ½råJ@hh¨¤§±¦¦¦ŠŠ 999ØŒ7yòduuu8Suu5™LvvvÆ—»¸¸þ¿öÎ3®‰ìëãwjè½* ‚XX Á‚ŠTTë®°üAAY+*º**vT,¬²Š`AE\ŠJ•Žt¤$”ÔçÅýì<‘!$™æûÂÌÜÜ9)3çž{ÏýãÇÝÝÝåååKKKáî+__ßvŠþpØ•““#ˆ7ƒÃ;àέ>–n“••ÕÍý°uuuóçÏïÃiÜ‚?sxÜ\&³Ó-Oöþý{www&“yäÈ‘N«ˆ0-[FF¦]/%%wÎÃ5ØsçÎÍ›7ž"“ÉÖÖÖ%%%ÊÊÊÁÁÁÞÞÞŠŠŠ­­­ý:,ç SŒpDˆ¾“µ£›™¼yóæ§OŸ8p€ß&qò3Ýiþ‡Ý=À#¡¡¡n~uqq …¯E:£Óã¿4æð„^Åd………ŽŽŽ---kÖ¬ñññá½uŽk:Ótss €©D999h¦‰D‚¥`ëë륥¥ÃÃÃëëë ÂåË—;êbÀž©—ÃúdLÆž¯ßqqñ©S§=z4333//ïĉvvvÝ©kŠ,6zôBTE²lÙ²„„„vÙ=……… è~/ökµ».ëg~iþÌá hLÖñ3ÿÅ:F›5kVeeåôéÓÏœ9Ã/ ‡_Àßß¿¤¤äÒ¥KG=sæÌîÝ» ¤©©éãã“ššš››K¥R³³³õõõïÝ»…ÎÚGF}þWÕ7`±Xhr]?‰ÉÔÔÔfΜéàà`ooÿËr9íhjjê¨NË™N×ç ”š3g*"%%µ~ýú'N°‹ŸæÉ`L† ȨQ£,,,¸Î:?~<…BáÐ@ZZšý—_RR‚&Uö•¦¦&ƒ+Vlß¾½ûÛþ~ûí7++«óçÏÃÅ~Ȇ †ÝyCCí[·>þÌ+kÙÁŸ9¼BAAB¡444ôÀ“ýûï¿ÇŽ#‰aaa}lÚšZeeeNNNW666666/^¼xüøqqqqEEEiiéøñãgΜ9sæL¨´Ôpݵݒ,?HOOGï^eeetf UÈ•’’B%ØÙes¡09A??¿?ÿü³ïÍ.’ÉdssóÝ»wÏ;—kÕ`v^½zÅA“…Á`”••9;;Ã?ó ò.“/Ï/˜Ï¾ù‰kà„ƒÁøòåË–-[ªªª‚ƒƒ»ÿòÀÀ@++«eË–ÉËËÃ#***ÎÎÎçÎÛ¹s'àÂ… ,à|k£´[ëÎ#þÌá ŠŠŠß¿¯¯¯o'‰Ò¥'£Óéžžžt:}Ë–-hIø>ÃôéÓÃÂÂbbb8K%M:uêÔ©=ínDµ³³ãÞ>œnàåå5tèPTì€}' ú­ihh U^Ù¿JöÿOš4)&&†J¥¢cs~[.0dee=zÄÃUUU9Át:ýþýûgÏž;vì—/_ìììFü1Ë®s‘ßÿ}íÚµ=ÝÿD$ûí·›7o6¬GžLKKkÅŠ‡Þ¿?zpÛ¶mÖÖÖ^^^‚œ9s&66¶›½õt¡àÏ£Rt¥KOvüøñOŸ>üùçŸü5 fΜÍóœã¬¬¬‚‚EEűcÇò¶gœv¤§§;99†‡‡s=¯"++‹ ˆ­­­««+,“È…Ò<;cÇŽ ¸~ýzyy™.¡¼¤${øð1íÚÄÅÅ…„„œ?ÞÛÛ;  §_‚ \H‡x{{›ššnذ=2hÐ ‹›7oJHH˜™™Á X|æð¨Þñ&íÜ“544À‘ËÅ‹SÓVÀØÛÛ„W¯^9;;óVÂÿíÛ·GGGáÑîë“À€ AIIÉÞ|Ô EBBBNNNFFæÉ“' ,6pMss3‡ÎL&S]]=???  ¸¸xäÈ.ƒ++«;v=zôðáÃ=ºråÊ/'~ÜÜÜ|}}‹ŠŠ6mÚ„Îa¢º…¿„D"ùúúÂ’•(ÞÞÞ«W¯–””Í~ÄÂÂBQQ‘N§[ZZv¿.ÖÉ„ê™#º@OÖq¯H'žŒN§ÿõ×_€­[· À2¬X±bÅÂ… yþ«’puuíÑàˆ@ TTTlܸ‘ý ”””šš¬F¦Ú:::Ý\î{ û„sŒ ¢«&*„pþ0-,,>}úÐàlÑ¢Eh©evºò"ì{¿Øí%ÁØyýú5ç>9ÿÙ}„ç™#ºôÀ“Ý¿¿¨¨hèС}¾Öƒœœœ‡‡ÖV€ŸŸß;wØÅfZ[[KJJJJJºzIdd$:ÁÒß@2ÞCá_ªèâð.‚3‘Fxž9"J<ÌÚ¼y3>8RRRçÏŸ·±±éæpoÚ´iýÖ}ýúµ±±‘C¥’^’Ý'³œ„™g8ý™îz²¢¢¢wïÞÉËË÷}EÑÂÚÚÚÃÃãòåË¿l)!!qêÔ)˜$œ >üýû÷üèùôéÓ7ndß<‹Ó‘?8n]zòä œ¹)++«®®†¥FЃìÿg2™])\@ú[p†Ã]y²öQ׋/S§N혂Ão‚‚‚ÐÍOضm[»Ú8<ê*¡;©q:…L&ÃLhÀ¦åJ§Ó¡H.øYàÝœ×M}Bœíرpøðasss>ZpD‘¶¶¶ÖÖV11±Žã›Î=Y_MYr”••ÙUà:eÀ€~~~‚±§¿×)y"HÃ508{ûöí°aÃ`p¶sçÎ>¯òŽÓ X,–žž^Ç•¯.c2™†ó3®®®œmŽ;†'èó‰ŽU}q°Îp:RPP000èxê'O–››[RR¢®®>räH™&pºYX(--mÆŒ²²²:::®]µë§Ó)©œœ{{{{{ûnÖÁ i'މbffæççwïÞ=®ó€qº‚ÅbÁ/÷dá¼ÄÅ'ðà §ÝõdIII€É“'óv§Ž°ñËÂB999sçÎ]³fMUUÕ»wï8wr÷îÝ5kÖtlãââbiiYVVVZZjaaáêêÚóôôô;—””TSSËÉÉqrr?~üׯ_»ÓN7)--¥P(=-tÒ¨¬¬ÄêÒxp†ƒÒ]O¯íês÷Cüýý,X@"‘xåÊÎí9²eË–ŽÇ³²²víÚ¥¨¨¨¤¤äëë '¯ºÃÆ;J¨yyy=~üøìÙ³ššš>|1bĬY³ºYç—dgg< ë´ü#&àÁ¤»ž ænõyå ‰¡C‡|xæÌ™Ý´H$†††²×1ÒÓÓóõõ[·n]^^^`` œœ\LLŒ©©©››[nnn7{Æé |‘Œh^"†àÁN~~>è¾'SSSŒY˜Àb±jkk[ZZîܹ åÏÛQSS“‘‘QZZJ"‘Ö¯_Ï¡Ãýû÷ûøøtz꯿þºxñ"ŒÉ®]»ÖNð3¦¦¦Û¶mCÿ &‘Hðÿ222~~~;vì ·oß:tèÒ¥KËËË»ß?N;âããÁj8í’Îú3 åË—/D"±Ó²8ý1&lUŽ®^½Úñ¬ŒŒLpp°ŠŠŠ’’ÒñãÇ?~ÜU?111Æ ëôìÊ•+—/_þãÇ?~,Y²dÅŠ=2ÒßßðàÁ€3fÌŸ?¿ÝY‰tèСÂÂBOOO"‘>pà@???Î%}q:¥­­íùóç‚ôy…6 Îú'‰‰‰T*ÕÜܼÓJýÔ“Aºªr4räHöœù/ðŽê”W¯^ùúú******úúú¾zõªGæ‘H¤³gÏJIIAAçNÑÑѹpáÂׯ_/^Ìd2÷ï߯¦¦æååU]]Ý£kõs^¿~ÝÔÔ4jÔ(]]]¬mFzïÉX,źÎú!/_¾X[[wzö'OF¥RA_¯4èææöùóg:þíÛ·•+W²W9BÛ¬X±bëÖ­uuuõõõ>>>³gÏîØGñVVVìÙÛŒ1âàÁƒõõõõõõ‡âboƒ]ll¬¡¡!çf†††áááééé .lmm=~ü¸ÁöíÛ«ªªzzÅþ TqtppÀÚ!eâĉJEwÆ+{ xpÖ¯€‘@·<ÔRëÛ?XåHZZzÊ”)ÆÆÆ zzzêêêéèè466ž9s¦Ó®·oßÎáZááá ÚÚÚÚÚÚ ááá\²²2ªz§¥¥ÿ#%%…n©D455Ñÿ#Âsy<8ë'P(”ääd"‘ØÕóð§r««V­º|ùò… º_OG8ùôéÓÞ½{£££Y,–„„„¯¯¯··7š3‚Ãοÿþknn®¦¦VQQ×舗—W[[[QQ‘“““ŠŠ wܺuËÌÌ,&&&&&†š®­­­PJŸÁ` 6 —Òïc|è´ÁOZøP¾Ã]8¼ÂÜÜüáÇiii{÷î}ðà¿¿ÿ<<¿ýö[DDÄÑ£GwîÜyëÖ­°°°°°0GGÇtº3£_qüøq:¾lÙ2\Ú¦SÐòÜÓ§OçI‡'NŒŒŒ¤R©|ª%ŠÁŒ±š››OŸ>½xñâÏŸ?cm‘pqíÚ5*•êè設­ÝU›Ÿb²pÁˆ>É€ÂÃÃóóó7lØ --ýðáÃ#FØØØ\ºt kÓ0£¦¦æÒ¥K‚tµ·½Ÿƒd<ìcäaŸÅàŒD"mذ!//þLã) IDATÙÚÚºzõj…5kÖ°§±>|XCCCFFfåÊ•˜ˆ; ‹ŸQœ³7~òdfffâââøîÚ¾Š¾¾þ™3g víÚ¥  ðâÅ OOÏ!C†DFFöÃÉ÷€€€ææf‡>\ü¡7¤§§›˜˜ð¼[~Ï1BD.­±¹¹ùìÙ³¨àª¯¯ï÷ïßssssrr Ù?®×¯_§§§”——ïÙ³#{Äãdz³³uuu9«ýµ_<;vìÇ_¼xÑUÚ>NŸF£…††îÙ³§®® ««»iÓ¦Õ«W÷1øøøøiÓ¦‰‰‰¥¦¦â³¬ùúõ«‡‡Ü™Ã*++»ÊCã-Ü¥5²X,Á”a¿Š¬¬ì«W¯ÌÍÍ:::/_¾„{I³³³mllÊÊÊ`ûÜÜÜ!C†rrrlllJKK`'&Ðh4“ììì'NlÞ¼™SSÖÏüñÇ€ƒ²pú4íСCÊÊÊð÷ ''·yóæüü|¬íâ/úúú€ÀÀ@¬m 6mÚ8tèÖ†pIRRT•#‰;vìhiiáÐ877wýúõ‚1 }777Ÿ8qbÊ”)ðO"‘H§Óáÿi4š˜˜Ú¾Óã}’'N†J¥R9·lïÉnܸ˜;w.ßlÃF˜LfLLŒ ôg‚Ì›7ïåË—XÛÅ/V¯^ ;v,FÃÚÑ®U¸¹¹am÷´´´ìرƒH$† –””Ôi3ƒ1yòdœœ,«ØÃ 2™,)) ÿ¯­­›› ÿŸ­££ƒ¶g?®««+#1¡¦¦FII ð÷ßÿ²q{OöíÛ7€ššlÃvÒÒÒV¬XŠ8¨««õ±ÇýÅ‹RRR_¾|ÁÚ‘!%%:¬ é-¿ ÎNž< üVVVL&“ßö°ÇdÇŽC?a//¯9sæTUUUVVΘ1ÃÛÛmïààPYY ïØ±ƒßbÅܹsöööÝiÜÞ“±X,(0“’’ÂkÃpD†ŠŠ tDNNîСCd2k»x@DD•ß¼yk[D‰ÖÖVqqqPUU…µ-½…Cp–››+##ƒ.¾Ü¾}›ßÆ ×=z4¶´´xzzÊËËËËË{zz¢pøðauuu‰´bÅ ÎÓ¤¢ Ô+––ÎËËëNûN<Ùï¿ÿصk¯mÃ1 Fxx8ª_L$çÏŸÿüùs¬íâž'OžÀmLþù'Ö¶ˆööö€ððp¬ á ƒ38¯ÈžF §§×ÜÜŒµ¥ýޏ¸8111A¢¢¢ºù’N<Ù‹/†††<µ G„ILLÜ´iÃŒŒŒ‚ƒƒüøµ]=ãÎ;ð-øøø`m‹H‡ÉË–-ÃÚžÁœ 8Ðßß¿cNž$`Š‹‹ae1__ßOF§Ó¡ØGjj*ïÌÃyêëëÙeˆá䯻wï°¶ë×P©ÔmÛ¶Átç¥K— `ñ£Oµ'455û؃3==½N%¶eeeËÊʰ¶±¿wÙØØ0Œî¿°OÆb±Ö­[ðóóã‘y8}:~ÿþ};;;ôV9rä©S§êëë±6­sJKKá ‰¿þú ksD(ôï¿ÿbm¡R©cÆŒéèÆ Ë—/ÇÚÀ~ALL srrjmmíÑk;÷dÏŸ?‡áv¼"N¿"??ÇŽjjjðn'ÎÎί^½ž{SS“¿¿?\À0`@bb"Ö‰íé¬@RRÒÚµkQÁ­ñãÇ¿yó†;p:Â`0`}»øøx¬má ó»Bù¢N +//ïNc*•fjj ?Þ±cÇfddp}é.=…B $ \÷ŽÓyö왳³3Z³CRRrãÆ=Ù«««ƒ=ÈÉÉ999]¾|9==½ÓA1“ÉüúõëùóçÝÝÝá*ÄÂÂâäÉ““É|ùò¥‹‹ËСC\‹\EEeÒ¤IG­­­åë§×(vÈŽ˜˜Ø˜1c&L˜0yòäaƵû(FŒqùòåž&wt 'OV[[ «Äâó38\óùóçíÛ·«¨¨ ?ßñãÇwG1¤¦¦fРAð%fffìñ;‚¹ººž>}:55ÏQÓ¦M¢¬&Œ’ŸŸýúõÀÀÀU«VÙÚÚ}úúõ뻪ç[^^nooÿùóg}}ý7oÞhii¥¥¥Ñh4ƒA§Ó144„kÅ8‚äùóçvvvªªªùùùrrrX›ÃK˜LfYYYQQQáÏ”””P©TÀòå˯^½Ê¡‡÷ïßÛØØ477+++¯_¿~þüù¦¦¦‚|x~ÿþ=++ëØ±cOž<,X°àÎ;‹ ÛÚÚââ✒’ÒÜÜüýûw*•J¥R•””FŒÁ¯‚³£knn†Å/Μ9ÃCÿ‰Óo¡ÑhW¯^;w.:^#‰«W¯ït’L&=0`À€œœÁŒÓ)'Npi&r0™Ìâââ„„„ëׯsÈÿøñ#Tßž={vcc£ -ìÈû÷ïeee‚¼nKKËo¿ý°²²ØÊâ/<‹Åºÿ>@II©ººZáôZ[[ÏŸ?okk‹ŽÅÄÄV®\yóæÍv¢¨ “&Mhii}þü+ƒ¹&55uúôé222ÚÚÚ—.]âÜbJHHXYY¥¥¥ÙØØ<|ømðàÁ[[[VgS)ü}?·œª¨¨`þ¼Èd2Lì\¾|9׳Ü?~|ôèQhhèîÝ»ƒ‚‚nܸñüùónfväíÛ·ÒÒÒ€»wïr×w|ÿþ~ .Ì„·~ýÓ§O8::â;*px,ú>yòd´x.‘H\¼xñÕ«Wѵ42™ ×fTUU?}ú„­Á=";;{àÀ÷îÝ£P(EEE+V¬àÜuH åðáÃ#FŒHJJ;v,Ú`̘1ïß¿gý\× àð±DñññŒ5Š‹*Hÿý÷š5k`9®Ž baaqðàA.t³Îž= oœ¦¦¦ž¾¶7|ùò¾-[¶àrݺŠŠŠGå·A8ý–æææÐÐÐ)S¦ Q‚ sæÌ9}útMMMKKËìÙ³áô@W5…77·_Æaì°û§¦¦&XwÑÑÑ1&&†Åb=yòdÞ¼y[bBJJ ‘Hûøñ#¶–MMMP¶±§ŸFjjª­­-ê´tuuV­Z°uëV77·‰'¢w‰DÚ³gO|“É411œ?¾‡ï©·¼xñεóûZݽ>|ˆ ˆ¸¸8¾½ ‡ß´¶¶^½zÕÞÞž=÷ÉÒÒ2 ê=JIIÁ¸DøÑÐÐØ¿¿––‰Drvv®««ãÜž=&;räÈĉY,VFFƤI“X,ÖĉÑùUÌ=‹Åòòò˜ššr½ÿ½Ïpùòeü¾ºImmíöíÛáÏ[RRrçÎ]‰¶·µµÝ¿åÊ•°±––Vÿ®_¿7n\÷_Â+nÞ¼‰ @øçŸøz¡Ü ðCWTTÄ•¡qC]]]XXØ‚ à\?n’““KKKÃÚÀ_C$—-[VSSSWW·|ùrÎíÙç”deeÑ‚·îîî»víb¯¨‚í:„B¡Àxé8aÀ9þÎÉÉÙºu«©©)û,"‚ žžžÝÜœ—––“)H$‘HTWW777_¸pá­[·ÚÚÚºzUKK ¼kJKK{üÆzÍ¡C‡òòò¹¹¹ü»J~ý4m„ cc㊊ þÙ„ƒÓŽæææèèèÕ«W£e¬á#`úôéçÏŸ/))ÁÚÀ.‘——G7¨ÖÖÖÊÊÊrn:¤æææ'NL™2þ™ŸŸÈÏÏïØ[`ê‘H„óŸý@vvv§g[ZZ`¶A;H$Ò£GÐft:ýâÅ‹óæÍ5j”††‘H´°°ð÷÷OJJBó&Z[[—.] h·[KLLìâÅ‹]™—™ÙS‡É¢E‹£GæÉ&èNéÙÍP[[ e²Fމ§2â&“ùþýû;w¶“f9r¤Odd$*Ú|ÅÒÒQ¬­­•““ãÜžÝ?‘Éd¸NÖñTÇ?1dïÞ½p¶&++ k[°¡©© .¾túókmm…³ârrr¿ÿþû»wï |’„„Z‡J¥^¸p¡«íÿ€±cÇ }ÂÂ[JJJQQQ¨˜‚ ]UôöööìÛ·ïþ×Ô××<°iÓ&>]¢Ç7CUUÕˆ#¦¦¦¸ VTUU444اâââûöíÁªÐÐÐåË—×ÖÖþøñcåÊ•nnnðxW~ˆ=&;vìØ°aÃ:žâ܃àa2™NNNmmmlk `EVVÀÀÀ Ó³ÐÓ«ªªÂ ‘iii‚ òàÁØ€F£ÁÉIèíà¶lÙ™ wòòòhPÅ`0às†kAAAðíÛ·Ž6\¼xðËÔYþ‘œœ,!!Áþ®y 77CyyùСC èóe{ú§~šH““u׬Yïãã3jÔ(öPGGgùòå×®]«ªªÂÊH&“éëë«¢¢B"‘œœœÐø tíÉ bbb£Gf¿¿@OÖþ½‹_B&“ÑÙ®7?‰.yyy==½NÏBå*X!‹Á`ÀþÛ·o‡g™L¦‡‡€H$nݺ@"‘à£FLLLLLLZZZLLlÊ”)ð[ŽŒŒ„/lhh€u‹ÐIEèÛ®_¿Þц›7oÐ&”••‹ŠŠxÞ9—¿þÊÊÊqãÆ¤¥¥;ýàúýY?M$HLL„É ¨ØDssóÕ«W—.]Šv :tݺuQQQÆqzCyyùðáÆ††üxT 3………YYY¯Î€ÙùÐÁ_»v  ¥¥…î•„3   èÕkÖ¬B*€qãÆmÙ² ³©ªª¢ã¡Û·oÃçìmÿþý€ &t´Æ| .Äê#b±XL&sîܹKKK.¶Üq†ûq\[[ÛÚµkáíââÂÛ¸¡­­mË–-ß¿ça·=")) þ•••}}}?~ü(à5˜²²²¸¸¸Y³fÁyÁ‚¸[·nzi ‡……±X¬˜˜ÎfÌ™3GàŸÍOÔÔÔÀµÀ;wò¶ç_(ÿ’ÐÐÐõë×3™L•ǯ\¹Ujàš„„ggçÊÊJÀàÁƒaä.`RRR¬¬¬ÚÚÚfÏž}óæMleR?|ø`kkK&“=<<à¶vþúë¯Í›7(‡Ñ±ƒÁHNN~ñâÅû÷ï?~L§ÓÑS&&&æææVVVVVV0¤Àé  ÅÎÎîÝ»wbbbþþþÿûßÿ¸›“/((˜6mÌØtvvŽŒŒäµ¥¼êÔph```°`Á‚¼¼<###‰TUUGÉ¡¡¡k×®USS«®®lÞ¼ùĉ---’’’ðskjj*--511a0•••Åb8íÁÖÖöùóçõõõ—.]ú¥¼|ç=çíÛ·ÖÖÖ ãõë×hÐÉzï óòòìííao¦¦¦\Ç.---Û¶mƒ_¡ºº:”¿¬¬¬ì½‘=×O9þøã€‘‘Ñ/‹Å0ŒÄÄÄcÇŽÍš5‹}›€D"9888p ..Ÿ„ä¶k×.x3†‹à,>>®€ÂM†††ü°SðÀ‰Dö´ 8ðÒÕÕ3fÌâÅ‹»zÚ\¹rFòòò°Â5úøñã̤æ½(íŒ9’‡sŒ<[%¾uë–¦¦&|"¨««‡„„ô(³±µµ544fj‰Äàà`&“ —âzZn¸÷àúi"Gkk+Ü1ºzõê½0##ãÔ©S...ºººí¾)ssóÕ«W_¸p!%%_¤ì)oß¾EóæÍcß5ņ††uëÖÁ§¶¥¥eyy9@BB‚ßÖ &“ £¨øøxx„F£¡¿7EEEβ¾¾¾PùI^^°{÷nôÜ­%BåâZ[[‡ 8~ü8¯úäe¾ ± Ñg¥¥åéÓ§ß¼yÓ•˜ ™L~ðàÁªU«à—:t(ºÇX>|øÀC# ®Ÿ&¢dddÀ:g÷ïß箇¢¢¢[·níܹsܸqíª(‰‰‰YYYmÞ¼9,,ìË—/ø‚ewhjjò÷÷‡3+ðŽ8uêTJJJÇOÁ`¼zõjíÚµh¡ËM›61 …oLìç-oÞ¼èèè o<ƒŸ @CCƒÃ€ >[Ð g~~~è©èèhÀðáÃùþxÇãÇ¡WæU&ï3w™LfHH{*ˆžžž¥¥åôéÓçÏŸ¿hÑ¢qãÆéèè°Ï¡=úæÍ›ìa\ÌD›`ÀõÓD—“'OÂámïg¤™LfJJJpp°››[ǵ7IIIKKËßÿýìÙ³ÉÉÉîAimm½wïÞêÕ«GÝil€¸¸øäÉ“÷îÝûæÍ›Ž=@I·¼¼<þÙ\?Mta2™S§N°ëò„ÖÖÖ§OŸ/\¸Öžm‡±±±‹‹Ëþýûï޽ˮ)…¡ÓéÑÑÑžžžC‡í4)LOOÏÇǧÝDÌ ÔÒÒÂÊì¶¶¶¤¤¤S§NmÞ¼yêÔ©ƒ溫õë×Nœ8³‹‚ÈËË_¸pÁÃÃsXX˜¬¬¬ŒŒ Ü„ÞNè΄‰„)Jii©””@ÈÈÈè}oÚMÉd2“““_½zuûöíÄÄÄ¢¢"γ4pgû/åÃy ®Ÿ&ÒäççÃÛƒ¯“Ò åùóçAAAëÖ­9rdÇô<`nn¾bÅŠ#GŽÜ»w/77_iC¡ÓéïÞ½{úôiTTTDDħOŸººÇ¿~ý `:á+­­­ïÞ½ ñññqppÐÒÒêx§sîÃ1V;uJ8gsÜÜÜ:}$>~ü>%ÔÔÔ`tt4{ß ›¦E…7/^Üû®„Eð¦#4 A"‘(ÈG®ŸÖرcÀÊÊJ`¿œ–––”””«W¯®[·ÎÞÞ^[[»ã÷Ž ˆ±±ñÂ… wíÚúæÍÁ'劉‰‰€ñãÇó¼ç¶¶¶ÌÌÌGýùçŸk×®µ¶¶î4KKLLÌÔÔtéÒ¥ûöí{òäIoæB`&û½Ïb±nݺ€S¯jjjoooöùùùÛ¶mƒ§Ð¤$ÐîÇó"÷X(..– ‰½—É^O÷“©ªª ò¢¸~Z ¡¡ª!ܾ}+jkk_¿~}îÜ9OOO;;»®F6ÒÒÒfffNNNÛ¶m;wîÜÓ§O?þŒGo(Ož<̘1£7TUU½|ù2""âàÁƒîîî¶¶¶úúúÎpŠ‹‹›™™-^¼x÷îÝ÷îÝËÊÊâ•™L†CÞv!WKK |&À[ž@ °ç+•––Â_ζmÛ`Ê"\PdO\„ÀÕ}^­9 ’U«V<=={Ùðz²ÌÌLÀСCyQ\?­opဦ¦&ÿªHô”¦¦¦”””›7onß¾}ñâÅãÆCóu;¢­­=vìXŸãÇGEE}øð¡°°ë7!hà¯ÝÕÕõ—- Fjjj\\ÜÕ«WýüüÖ¬Y3cÆŒAƒqjÐÕÕ1cÆÆ=úôéÓÂÂBþ!àÆg%%¥Ž§à{„#ÝÐÐPôxSSLUWVVFdß¾}PàÃÀÀ ¥¥¥]'p縓“Ÿìç¹¹¹D"Q\\¼—e/;OÄjkk***‚¼(œŒ®­­…“xíHJJÀ}é7nÜHIIÑÒÒBeÓ®\¹`0ÚÚÚðÏ5kÖ|ýúõÍ›7t:}ܸqfffYYY•••6l˜6mš’’’¼¼|HHˆ««ë¦M›\]]edd,--?~’ššÚΆììlš¿‹Ó)+W®>> !!¡Ó³óçÏ¿víZW#Ó§Ooܸñ?þ8uêÏ,t:][[»ºº:55µ]a¦î#¼³‹ÍÍÍ€v¥ù ‰Dòòòúe3¸‚åìì Ý&bHJJŽ3ÆÈÈèøñãvY?99¹aÆ]¼xqåÊ••••òòò¦¦¦¯_¿NNN†žlÑ¢E7nŒ/--ÕÕÕíŽ8 ‘HÇ ßNA8ÐÌ̬Ó,«²²²¦¦¦¡¡¡ªªêÇ555õõõd2¹®®®±±‘L&766655Q(”¦¦¦ææfx¯  iii999%%%‰¤   ¢¢"##£¨¨¨¡¡!##£¬¬|ûöík×®úùù ØBž£¦¦VYY™““Ó•PµÍ‹/?~\\\\QQQZZ:~üø™3gΜ9 QvENNøO¦Räsvv ¹yófôd­­­@(g~Y, —-[Ðét¸¤QZZJ&“úš‚_±bE^^,T×ÀÒÒÒ.\PTTœ7o^dddxx8Ì©Åé%ëÖ­;qâÄ­[·‚‚‚úØRW ¢©©‰ v‡æÿhiiiiiimm…Qõ?èÿÁ`0˜L&šþËS@†kD"Foâââ0¶“’’"‘H’’’$SüÎ7wIDAT©GSšqqq@(\0}úô°°°˜˜Î%¦N wžõˆ¿ÿþ÷‰"‹/ ¹}ûö¡C‡¸«¦‚{²“˜˜X\\¬££ƒf!¢3´ººº¥¥¥S§N-//ïêûHJJjkk0`@II ûkîîî‘‘‘aaa¸'ã †††¶¶¶±±±ÑÑÑè°§pjk+:ÃÄÚ0sæÌ°°°èèhžO·dee(**vZÒH$°´´ÔÖÖ...ÎÈÈ€âG=÷d=æãÇ€9sæ´ ¼)-- ã0¬8tèД)SJJJà:vQQzjúôéD"133³¥¥¥]Áî˜1cFllìëׯqO&Šô%OfooO ^½zåììÌÛD›·oßÛÉ^‹‚Lž<ùöíÛÉÉÉ}Í“Á¼R{n°æÁ£GP¿%//ìØ±Û·oO:UõnGff&@‘‘™9sæÝ»wŒŒÐSââ⃠ÊÍÍÍÍÍåîëÄi,æôúõk¬ ÁᆾäÉ”••/]ºäááq÷î]žw®££sìØ1žw+HÆŽ =Ü+ÝS„דAOÀ^ÂGH€: C‡Eˆ‰‰ÙÚÚÆÅÅùøø\¸páÇ­­­7nÜè¸ZöäÉ“•+WÒét55µÒÒRÔûG>|xnnnvv6îÉx‚™™™¼¼|nnî÷ïß;‘Âfú’'¬X±báÂ…<÷d®®®¢A Pœñâáõdp^±¥¥kCÚ7=°ÇdOOϸ¸¸Ã‡“Éd55µ[·néèè@Ý)HAAÁéÓ§¯_¿N§ÓárZuu5@€Ê¡(ÆÆÆ>„Χ÷‰D++«˜˜˜×¯_»ººbmNGNN*á´ÃÜÜœH$fdd´µµq1'¸]î=. ›'£P(UUU0ueÞ¼y pOtmm-@°²²BÏ–••M™2åøñãîîîòòò°œÀ××·]â, õ`N-O€Œ°Î!ŽhÑÇb2ÈÊÊS©Ôôôt.^Ž{²ží‘‘‘i7s(%%·T#Âb±Î;7oÞ>^OOïãÇœ›544ìÛ·oÒ¤IOM˜0áóçÏÝ¿¢ˆ¢¥¥(..æâµB“ ­ü+®Ÿ&BÀ˜lذaX‚à cÇŽ}ÿþ}rr² Ö¶pCDD„——×£G~Y¸\^^ÞÛÛ{ÿþýO½{÷näÈ‘èŸP~džb …B©¨¨’’ꃳ‹’’’éééõõõŠŠŠX›ó¸~š¨€Ï.Š4p͘kaYÁÃîfŽ?~òäÉØØØv)ö6îîîÿûßÿ «««=jiiÙ± »ßê“n ŸŸÏb±ôõõ»*SÌ¡Îø––7nƒÁÀEóxœ†îW”––“H$.'ßq°J¤ÃšìBº™ŒÃ®2//¯âââáÇ£›Ï:nû;w®‹‹‹´´´™™YmmííÛ·»i@CCƒÁèÍ[*à`…½ÊUjO„x‚Qäðõõ2dˆ„„„££ãýû÷ûä°®#!!!,ËÙÙ™»æËÉÉuº­CÚ­Ó°ïª ‹Å‚[¦ØÛ8;;§§§Óh´ŠŠŠ«W¯¢{I»ºCáñÜÜÜŠ‹‹Ïš5ëÊ•+}àv¾sç`öìÙ\¾ž‹µ5AòìÙ3€¡¡!Ö†ˆ6T*ULì§™dƒˆˆ&“‰µi|„J¥jjjÞ¾}‹µ-8ÜuÝîÞ½‹µ!BÄ™3gØogccc‘þ‘×ÔÔˆ‹‹‹‹‹sï&ìUkkkMMÍÜÜÜÄÄD¬maÄÅŇ ˆŽŽ¾|ù²¦¦fAA‹‹‹™™Y\\ÖÖñ‹‡VTT˜˜˜ k8¢§Ã1;Ζ›˜˜DFFjjjfeeMœ8qݺu"ªÊE£ÑlmmUTT¸ì‚·®•ìÚµ °lÙ2¬ möìÙðôôd±Xt:=(( æÎ›µ¼gÚ´i€S§NamN¯(**BDFF†L&cm‹°ÐÒÒB"‘©¬¬¤R©þþþ0çY__ÿùóçX[×cà­ §I¹C@––®««ÃÚ&77 ¬¬L¥Rá‘–––ƒB&..¾yóæÚÚZlä!yyy‚H¤?~`m NoQuDDÖ†0TEŸþŸ?†Ù1‚¬_¿¾©© Sëz@ee%‘H”èÍ­*쳋;;»–––ëׯcm‹3dÈ“ºººØØXxDJJjçÎ999«W¯f2™'Ož444zô󺃫«+`ñâŽìG4<‹Å hhhàË\ÓÒÒà\Z‚Á`;v îzÌž=»ã}"ä8::f̘ѷ÷ô7 ü®ªªjcc#Ö¶ S¦L¼xñ¢Ó³éééh ''§²²2ÁZ÷k‘––îý܉Èx2&“ wIÿþûïXÛ"Â@ÕÐÐÀ¹Yccc@@Tú'îîîPKFø‹©ŠŠŠ%%%XÛ‚Ãc&NœøóÏ?±6DX˜7o **ª«t:ýäÉ“ðFVPP8{ö,ƒÁ¤…`2™PÚÏϯ÷½‰Œ'c±XâââD"ñ—ªÒ8]1pà@ðŸÄÙ/©ªªÚ¼y³¤¤$@BBbãÆ•••ü¶°7|ÿþ.÷&Gh) JJJøÄ dùòå€K—.qnV\\|ØÆwïÞÕÖÖ†£R__ß––~›Çââb555:EÌ“555éêê8€µ-¢,®¯¯ÏÅk322Ða¢¢â… „j0ÑÚÚ Ÿq¬ªªÂÚ~ QÊÉÉáß2뿌/_¾t³}}}ýúõë¡©žžÞëׯùj^W´µµÁyEkkk^=FDÌ“±X¬ØØX¨*}ãÆ ¬m1àf†Þ„,oÞ¼™4iôgjjjÂð@a0ÐËjkk‹Êz×899Á‰™¶¶6¬mÁ&..N z]%&&¢ÕΟÒëaéëëó°ª°èy2‹uòäI#ÇÆÆbm‹(±dÉ@@@@/ûùûï¿aÕ(€´´ôÚµk1»ªªª‚‰JJJéééX™#0êêêôõõ[¶lÁÚ,yþü9ÀØØ˜‹×Òéô={öÀ4}°téR ½¼¼RRR¼MwIOÆúoÁL^^>55k[DƒÊÊJIII"‘ȫͯ^½š;w.œ©@ÄÑÑ1**JÀ‰ï_¿~0`ÜÍ»±þÃû÷ï%$$õ‰ú'«V­ìÙ³‡ëª««·lÙSºÄÄÄù½x¶uëV„DGGó¶gQõdL&ÓÍÍ N(bmŽpàÀ€££#o»ÍÊÊZ³f´´4 Ñôôôüüü¢Q©Ô?ÿüSJJ `aaQ^^Îï+âÁÁÁ0/((ÀÚ hmm…iº™™™½ìª°°pÙ²epH*..¾dÉ’>ðÄHvêêêàžIIIž»1–èz2‹ÕÚÚ:uêT€‘‘QEEÖæ5¨ìÅÓ§OùÑ?™Lö÷÷GeÓ±´´@DGGÇØØxذað_A(Jss3…B¡P(uuuqqqïÞ½£R©°½››[pp0ߟÉüs’æÕ«WP/_GG')) ks„‚¤¤$XHsÉ’%XÛÂÊÍÍ={öìÂ… aáö1räH__ß””¬ßŽ0B&“§OŸPPPxùò%Öæð‘üü|(±}ûvA^÷Û·o´°°€ó„ÝÁÌÌ,88X`Å{ûBL†R^^îââ’ &&¶lÙ2(3Ø?III™6mZ}}ýâÅ‹ÃÂÂØ'ú0§µµõÛ·oùlTWWËÈÈH$éÿ••ÕÕÕ533³°°À#0ÎP©ÔåË—ß¾}[BBÂÇÇÇÏϯïýfššš¬¬¬222fÍšõèÑ#Lîh&“YPPõÙÙÙA† yyù &ØÚÚ’H$Z&‡)0ètúÆaޤ¤ä–-[„A„BðDFFÂhÌÙÙOíÃé0™Ì-[¶À aÈ!Ïž=ÃÚ"^òãÇ &†^__µ9BG_ód¼¼¼Å‹C&Ì¢·ü ¥¥eíÚµp˜âää„§Eàô+MLLàïÉ’%}cNaaá°aà yyyX›#ŒôMOIKKƒÙç•   ææf¬â/™™™C† HII={kspp0€J¥>|În)))…††ŠtÍÕ´´4˜sÿÛo¿áU÷º¢/{2ÈÛ·oaeU€¢¢âŠ+bbb¨T*Övñ˜ÊÊÊ 6ÀœW##£ÿýk‹pp°$??Ö¬YðÆ722 ª®®ÆÚ¨žÑÖÖÕmllðIEô}O‰‰‰AEܪªªkÖ¬‰‹‹ë“oÍÍÍû÷baçÎý\#%""Mû’””\¼xñ«W¯D"DKNN†Ó¤‚ìÞ½[¨Š( !ýÅ“A²³³÷íÛ·RB455ÿøã„„Qü¡TWWÂêy€Ù³gw¿RN?N§?zôhöìÙh²ßðáÃOœ8QWW‡µiSVVæîîSW _½z…µE"@ÿòd(~~~FFF¨KÓÖÖ^²dIHHHZZš{5ƒñôéÓ À¹DÀèÑ£qÝ&ÎíÞ½ù„™3gž9sFxÄA>þ¼dÉx_‰Dooï>¿´Ï+úÔ~2.øôéSDDDdddaa!zPAAaüøñVVVVVVãÆ“••ÅÎÀÿ‡N§¿~ýúéÓ§wïÞÍÏψ‹‹;::®]»ÖÖÖ¶û;qpú3t:=::úüùó±±±L&400°···³³³±±AUFKKËýû÷£¢¢`-\qqñ¥K—îØ±ƒ]/ ‡3ýÝ“¡|þüùÍ›7oß¾}ûömAAzœ@ ˜™™ÙÚÚ›››£LC~~þ?ÿüóôéÓøøx2™ xzz®\¹Êšàààô”²²²§OŸ>{ö,66¶®®$ãÆ³··7335j”ÿ hllŒŽŽ¾{÷î?ÿüÓÚÚ ––öôôôööîÏ’Ü{²Nøþý{bb"ôjì¢uuucccTyÌØØxàÀh5„^B&“¿|ù’ÁFMM <… ˆ‰‰ÉŒ3fΜimmÍ“Ëáàà0™ÌOŸ>ÅÆÆ>{ö,11 BGejj ÿ>|8¬KÉ4íóçÏÉÿñåË: 'NtvvvqqbT8=÷d¿€F£½yóæýû÷_¿~ÍÊÊJOOokkk׆H$ª«««ý ~ü˜””ôöíÛ¦¦&ö³A]]]ëgttt¤¤¤ÚÝà€ÚÚÚòòòòòòŠŠŠòòò’’X“½Ã‰'ººº:99ihhô}ö9pOÖ3X,VYYYVVVff&TËÌÌ,//çUÿ’’’Æ 9r¤ÉèêêòªsœQQQ‘ššš–––šššššš››Ë`0¸î ACCñÿaff&ॊ> îÉxN¯¬¬¬î@EEÜÚ:è[Љ‰ijj²Çpªªªƒ Âs7pp„ƒQYYYþ3eee*-(++kjj²Goúúúh]uÞ‚{2ц7y 888888X{2Ñ÷d88888¢ îÉpppppDÜ“áààààˆ6¸'ÃÁÁÁÁmpO†ƒƒƒƒ#Úàž G´ù?›òx;j9IEND®B`‚kamailio-4.0.4/doc/sip/figures/bye.eps0000644000000000000000000012376012223032460016302 0ustar rootroot%!PS-Adobe-2.0 EPSF-2.0 %%Title: bye.dia %%Creator: Dia v0.91 %%CreationDate: Tue Jul 15 14:58:42 2003 %%For: janakj %%Orientation: Portrait %%Magnification: 1.0000 %%BoundingBox: 0 0 601 348 %%BeginSetup %%EndSetup %%EndComments %%BeginProlog [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def /cp {closepath} bind def /c {curveto} bind def /f {fill} bind def /a {arc} bind def /ef {eofill} bind def /ex {exch} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth pop} bind def /tr {translate} bind def /ellipsedict 8 dict def ellipsedict /mtrx matrix put /ellipse { ellipsedict begin /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc savematrix setmatrix end } def /mergeprocs { dup length 3 -1 roll dup length dup 5 1 roll 3 -1 roll add array cvx dup 3 -1 roll 0 exch putinterval dup 4 2 roll putinterval } bind def /dpi_x 300 def /dpi_y 300 def /conicto { /to_y exch def /to_x exch def /conic_cntrl_y exch def /conic_cntrl_x exch def currentpoint /p0_y exch def /p0_x exch def /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def /p2_x p1_x to_x p0_x sub 1 3 div mul add def /p2_y p1_y to_y p0_y sub 1 3 div mul add def p1_x p1_y p2_x p2_y to_x to_y curveto } bind def /start_ol { gsave 1.1 dpi_x div dup scale} bind def /end_ol { closepath fill grestore } bind def 28.346000 -28.346000 scale -4.387600 -13.050000 translate %%EndProlog 0.100000 slw [] 0 sd [] 0 sd 0 slc 0.000000 0.000000 0.000000 srgb n 5.000000 3.000000 m 5.000000 9.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.000000 3.000000 m 9.000000 9.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 13.000000 3.000000 m 13.000000 9.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 5.000000 5.000000 m 12.200000 5.000000 l s 0 slj n 12.200000 5.400000 m 13.000000 5.000000 l 12.200000 4.600000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 13.000000 7.000000 m 5.800000 7.000000 l s 0 slj n 5.800000 6.600000 m 5.000000 7.000000 l 5.800000 7.400000 l f gsave 7.150000 4.750000 translate 0.035278 -0.035278 scale start_ol 384 0 moveto 1855 0 lineto 2163 0 2391 88 2566 282 curveto 2727 458 2816 698 2816 962 curveto 2816 1369 2632 1614 2204 1780 curveto 2478 1925 2624 2178 2624 2525 curveto 2624 2773 2537 2994 2370 3153 curveto 2204 3317 1985 3392 1679 3392 curveto 384 3392 lineto 384 0 lineto 768 1920 moveto 768 3011 lineto 1581 3011 lineto 1816 3011 1947 2978 2059 2889 curveto 2177 2795 2240 2654 2240 2466 curveto 2240 2282 2177 2136 2059 2042 curveto 1947 1953 1816 1920 1581 1920 curveto 768 1920 lineto 768 381 moveto 768 1536 lineto 1823 1536 lineto 2032 1536 2172 1485 2274 1375 curveto 2376 1269 2432 1122 2432 956 curveto 2432 795 2376 648 2274 542 curveto 2172 432 2032 381 1823 381 curveto 768 381 lineto end_ol grestore gsave 7.556400 4.750000 translate 0.035278 -0.035278 scale start_ol 1728 1331 moveto 2991 3392 lineto 2479 3392 lineto 1542 1740 lineto 579 3392 lineto 49 3392 lineto 1344 1331 lineto 1344 0 lineto 1728 0 lineto 1728 1331 lineto end_ol grestore gsave 7.962800 4.750000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 12.500000 3.000000 m 13.500000 3.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 8.500000 3.015000 m 9.500000 3.015000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 4.500000 2.990000 m 5.475000 3.000000 l s gsave 9.600000 6.700000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 9.938667 6.700000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 10.277333 6.700000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 10.616000 6.700000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 10.785333 6.700000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 11.259467 6.700000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore gsave 7.700000 2.650000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 8.106400 2.650000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 8.275733 2.650000 translate 0.035278 -0.035278 scale start_ol 832 1408 moveto 1879 1408 lineto 2141 1408 2348 1488 2527 1654 curveto 2729 1843 2816 2065 2816 2381 curveto 2816 3028 2451 3392 1803 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1408 lineto 832 1792 moveto 832 3011 lineto 1719 3011 lineto 2126 3011 2368 2782 2368 2402 curveto 2368 2021 2126 1792 1719 1792 curveto 832 1792 lineto end_ol grestore gsave 8.682133 2.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 8.851467 2.650000 translate 0.035278 -0.035278 scale start_ol 832 1408 moveto 1879 1408 lineto 2141 1408 2348 1488 2527 1654 curveto 2729 1843 2816 2065 2816 2381 curveto 2816 3028 2451 3392 1803 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1408 lineto 832 1792 moveto 832 3011 lineto 1719 3011 lineto 2126 3011 2368 2782 2368 2402 curveto 2368 2021 2126 1792 1719 1792 curveto 832 1792 lineto end_ol grestore gsave 9.257867 2.650000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 9.461067 2.650000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 9.799733 2.650000 translate 0.035278 -0.035278 scale start_ol 1346 1258 moveto 2157 2432 lineto 1723 2432 lineto 1143 1550 lineto 562 2432 lineto 124 2432 lineto 931 1239 lineto 78 0 lineto 516 0 lineto 1129 933 lineto 1733 0 lineto 2180 0 lineto 1346 1258 lineto end_ol grestore gsave 10.104533 2.650000 translate 0.035278 -0.035278 scale start_ol 1788 2432 moveto 1120 533 lineto 502 2432 lineto 92 2432 lineto 908 -16 lineto 760 -403 lineto 700 -575 613 -640 452 -640 curveto 396 -640 332 -631 249 -612 curveto 249 -963 lineto 327 -1005 406 -1024 507 -1024 curveto 631 -1024 765 -982 866 -908 curveto 986 -819 1055 -717 1129 -521 curveto 2203 2432 lineto 1788 2432 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 17.000000 3.000000 m 17.000000 13.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 21.000000 3.000000 m 21.000000 13.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 25.000000 3.000000 m 25.000000 13.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 17.000000 5.000000 m 20.200000 5.000000 l s 0 slj n 20.200000 5.400000 m 21.000000 5.000000 l 20.200000 4.600000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 21.000000 7.000000 m 24.200000 7.000000 l s 0 slj n 24.200000 7.400000 m 25.000000 7.000000 l 24.200000 6.600000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 25.000000 9.000000 m 21.800000 9.000000 l s 0 slj n 21.800000 8.600000 m 21.000000 9.000000 l 21.800000 9.400000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 21.000000 11.000000 m 17.800000 11.000000 l s 0 slj n 17.800000 10.600000 m 17.000000 11.000000 l 17.800000 11.400000 l f gsave 18.300000 4.500000 translate 0.035278 -0.035278 scale start_ol 384 0 moveto 1855 0 lineto 2163 0 2391 88 2566 282 curveto 2727 458 2816 698 2816 962 curveto 2816 1369 2632 1614 2204 1780 curveto 2478 1925 2624 2178 2624 2525 curveto 2624 2773 2537 2994 2370 3153 curveto 2204 3317 1985 3392 1679 3392 curveto 384 3392 lineto 384 0 lineto 768 1920 moveto 768 3011 lineto 1581 3011 lineto 1816 3011 1947 2978 2059 2889 curveto 2177 2795 2240 2654 2240 2466 curveto 2240 2282 2177 2136 2059 2042 curveto 1947 1953 1816 1920 1581 1920 curveto 768 1920 lineto 768 381 moveto 768 1536 lineto 1823 1536 lineto 2032 1536 2172 1485 2274 1375 curveto 2376 1269 2432 1122 2432 956 curveto 2432 795 2376 648 2274 542 curveto 2172 432 2032 381 1823 381 curveto 768 381 lineto end_ol grestore gsave 18.706400 4.500000 translate 0.035278 -0.035278 scale start_ol 1728 1331 moveto 2991 3392 lineto 2479 3392 lineto 1542 1740 lineto 579 3392 lineto 49 3392 lineto 1344 1331 lineto 1344 0 lineto 1728 0 lineto 1728 1331 lineto end_ol grestore gsave 19.112800 4.500000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 22.400000 6.700000 translate 0.035278 -0.035278 scale start_ol 384 0 moveto 1855 0 lineto 2163 0 2391 88 2566 282 curveto 2727 458 2816 698 2816 962 curveto 2816 1369 2632 1614 2204 1780 curveto 2478 1925 2624 2178 2624 2525 curveto 2624 2773 2537 2994 2370 3153 curveto 2204 3317 1985 3392 1679 3392 curveto 384 3392 lineto 384 0 lineto 768 1920 moveto 768 3011 lineto 1581 3011 lineto 1816 3011 1947 2978 2059 2889 curveto 2177 2795 2240 2654 2240 2466 curveto 2240 2282 2177 2136 2059 2042 curveto 1947 1953 1816 1920 1581 1920 curveto 768 1920 lineto 768 381 moveto 768 1536 lineto 1823 1536 lineto 2032 1536 2172 1485 2274 1375 curveto 2376 1269 2432 1122 2432 956 curveto 2432 795 2376 648 2274 542 curveto 2172 432 2032 381 1823 381 curveto 768 381 lineto end_ol grestore gsave 22.806400 6.700000 translate 0.035278 -0.035278 scale start_ol 1728 1331 moveto 2991 3392 lineto 2479 3392 lineto 1542 1740 lineto 579 3392 lineto 49 3392 lineto 1344 1331 lineto 1344 0 lineto 1728 0 lineto 1728 1331 lineto end_ol grestore gsave 23.212800 6.700000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 22.150000 8.600000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 22.488667 8.600000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 22.827333 8.600000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 23.166000 8.600000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 23.335333 8.600000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 23.809467 8.600000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore gsave 18.250000 10.600000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 18.588667 10.600000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 18.927333 10.600000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.266000 10.600000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.435333 10.600000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 19.909467 10.600000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore gsave 6.000000 1.450000 translate 0.035278 -0.035278 scale start_ol 3428 0 moveto 4281 3392 lineto 3802 3392 lineto 3184 637 lineto 2419 3392 lineto 1958 3392 lineto 1212 637 lineto 581 3392 lineto 101 3392 lineto 963 0 lineto 1433 0 lineto 2184 2787 lineto 2958 0 lineto 3428 0 lineto end_ol grestore gsave 6.575733 1.450000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 6.711200 1.450000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 6.880533 1.450000 translate 0.035278 -0.035278 scale start_ol 320 3392 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1483 2093 1607 2053 1699 1985 curveto 1810 1909 1856 1796 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2184 1949 2432 1479 2432 curveto 1138 2432 930 2326 704 2030 curveto 704 3392 lineto 320 3392 lineto end_ol grestore gsave 7.219200 1.450000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 7.557867 1.450000 translate 0.035278 -0.035278 scale start_ol 2240 0 moveto 2240 2432 lineto 1856 2432 lineto 1856 1121 lineto 1856 648 1599 339 1200 339 curveto 897 339 704 516 704 794 curveto 704 2432 lineto 320 2432 lineto 320 636 lineto 320 249 612 0 1070 0 curveto 1416 0 1636 124 1856 441 curveto 1856 0 lineto 2240 0 lineto end_ol grestore gsave 7.896533 1.450000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 8.065867 1.450000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 8.235200 1.450000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 8.438400 1.450000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 8.777067 1.450000 translate 0.035278 -0.035278 scale start_ol 2139 1595 moveto 2121 1818 2070 1963 1979 2090 curveto 1814 2305 1527 2432 1193 2432 curveto 548 2432 128 1947 128 1194 curveto 128 463 539 0 1188 0 curveto 1759 0 2120 328 2166 888 curveto 1779 888 lineto 1714 534 1516 357 1189 357 curveto 765 357 512 673 512 1195 curveto 512 1747 761 2075 1180 2075 curveto 1503 2075 1706 1902 1752 1595 curveto 2139 1595 lineto end_ol grestore gsave 9.081867 1.450000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 9.420533 1.450000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 9.623733 1.450000 translate 0.035278 -0.035278 scale start_ol 2304 3392 moveto 1920 3392 lineto 1920 2057 lineto 1758 2302 1498 2432 1173 2432 curveto 541 2432 128 1960 128 1238 curveto 128 472 523 0 1162 0 curveto 1489 0 1716 124 1920 421 curveto 1920 0 lineto 2304 0 lineto 2304 3392 lineto 1228 2070 moveto 1652 2070 1920 1733 1920 1208 curveto 1920 699 1647 362 1232 362 curveto 799 362 512 703 512 1216 curveto 512 1729 799 2070 1228 2070 curveto end_ol grestore gsave 9.962400 1.450000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 10.131733 1.450000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 10.334933 1.450000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 10.673600 1.450000 translate 0.035278 -0.035278 scale start_ol 2240 0 moveto 2240 2432 lineto 1856 2432 lineto 1856 1121 lineto 1856 648 1599 339 1200 339 curveto 897 339 704 516 704 794 curveto 704 2432 lineto 320 2432 lineto 320 636 lineto 320 249 612 0 1070 0 curveto 1416 0 1636 124 1856 441 curveto 1856 0 lineto 2240 0 lineto end_ol grestore gsave 11.012267 1.450000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 11.181600 1.450000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 11.317067 1.450000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 11.655733 1.450000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 18.350000 1.450000 translate 0.035278 -0.035278 scale start_ol 3428 0 moveto 4281 3392 lineto 3802 3392 lineto 3184 637 lineto 2419 3392 lineto 1958 3392 lineto 1212 637 lineto 581 3392 lineto 101 3392 lineto 963 0 lineto 1433 0 lineto 2184 2787 lineto 2958 0 lineto 3428 0 lineto end_ol grestore gsave 18.925733 1.450000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 19.061200 1.450000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 19.230533 1.450000 translate 0.035278 -0.035278 scale start_ol 320 3392 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1483 2093 1607 2053 1699 1985 curveto 1810 1909 1856 1796 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2184 1949 2432 1479 2432 curveto 1138 2432 930 2326 704 2030 curveto 704 3392 lineto 320 3392 lineto end_ol grestore gsave 19.569200 1.450000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.738533 1.450000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 19.941733 1.450000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 20.280400 1.450000 translate 0.035278 -0.035278 scale start_ol 2139 1595 moveto 2121 1818 2070 1963 1979 2090 curveto 1814 2305 1527 2432 1193 2432 curveto 548 2432 128 1947 128 1194 curveto 128 463 539 0 1188 0 curveto 1759 0 2120 328 2166 888 curveto 1779 888 lineto 1714 534 1516 357 1189 357 curveto 765 357 512 673 512 1195 curveto 512 1747 761 2075 1180 2075 curveto 1503 2075 1706 1902 1752 1595 curveto 2139 1595 lineto end_ol grestore gsave 20.585200 1.450000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 20.923867 1.450000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 21.127067 1.450000 translate 0.035278 -0.035278 scale start_ol 2304 3392 moveto 1920 3392 lineto 1920 2057 lineto 1758 2302 1498 2432 1173 2432 curveto 541 2432 128 1960 128 1238 curveto 128 472 523 0 1162 0 curveto 1489 0 1716 124 1920 421 curveto 1920 0 lineto 2304 0 lineto 2304 3392 lineto 1228 2070 moveto 1652 2070 1920 1733 1920 1208 curveto 1920 699 1647 362 1232 362 curveto 799 362 512 703 512 1216 curveto 512 1729 799 2070 1228 2070 curveto end_ol grestore gsave 21.465733 1.450000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 21.635067 1.450000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 21.838267 1.450000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 22.176933 1.450000 translate 0.035278 -0.035278 scale start_ol 2240 0 moveto 2240 2432 lineto 1856 2432 lineto 1856 1121 lineto 1856 648 1599 339 1200 339 curveto 897 339 704 516 704 794 curveto 704 2432 lineto 320 2432 lineto 320 636 lineto 320 249 612 0 1070 0 curveto 1416 0 1636 124 1856 441 curveto 1856 0 lineto 2240 0 lineto end_ol grestore gsave 22.515600 1.450000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 22.684933 1.450000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 22.820400 1.450000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 23.159067 1.450000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 16.500000 3.000000 m 17.500000 3.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 20.475000 2.990000 m 21.475000 2.990000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 24.500000 2.990000 m 25.500000 2.990000 l s gsave 4.387600 2.702500 translate 0.035278 -0.035278 scale start_ol 2560 3392 moveto 2560 1091 lineto 2560 651 2220 381 1659 381 curveto 1400 381 1190 439 1022 552 curveto 849 678 768 844 768 1091 curveto 768 3392 lineto 384 3392 lineto 384 1083 lineto 384 415 869 0 1659 0 curveto 2441 0 2944 424 2944 1083 curveto 2944 3392 lineto 2560 3392 lineto end_ol grestore gsave 4.827867 2.702500 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 5.234267 2.702500 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 16.412600 2.692500 translate 0.035278 -0.035278 scale start_ol 2560 3392 moveto 2560 1091 lineto 2560 651 2220 381 1659 381 curveto 1400 381 1190 439 1022 552 curveto 849 678 768 844 768 1091 curveto 768 3392 lineto 384 3392 lineto 384 1083 lineto 384 415 869 0 1659 0 curveto 2441 0 2944 424 2944 1083 curveto 2944 3392 lineto 2560 3392 lineto end_ol grestore gsave 16.852867 2.692500 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 17.259267 2.692500 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 24.387600 2.702500 translate 0.035278 -0.035278 scale start_ol 2560 3392 moveto 2560 1091 lineto 2560 651 2220 381 1659 381 curveto 1400 381 1190 439 1022 552 curveto 849 678 768 844 768 1091 curveto 768 3392 lineto 384 3392 lineto 384 1083 lineto 384 415 869 0 1659 0 curveto 2441 0 2944 424 2944 1083 curveto 2944 3392 lineto 2560 3392 lineto end_ol grestore gsave 24.827867 2.702500 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 25.234267 2.702500 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 12.412600 2.742500 translate 0.035278 -0.035278 scale start_ol 2560 3392 moveto 2560 1091 lineto 2560 651 2220 381 1659 381 curveto 1400 381 1190 439 1022 552 curveto 849 678 768 844 768 1091 curveto 768 3392 lineto 384 3392 lineto 384 1083 lineto 384 415 869 0 1659 0 curveto 2441 0 2944 424 2944 1083 curveto 2944 3392 lineto 2560 3392 lineto end_ol grestore gsave 12.852867 2.742500 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 13.259267 2.742500 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 19.712600 2.692500 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 20.119000 2.692500 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 20.288333 2.692500 translate 0.035278 -0.035278 scale start_ol 832 1408 moveto 1879 1408 lineto 2141 1408 2348 1488 2527 1654 curveto 2729 1843 2816 2065 2816 2381 curveto 2816 3028 2451 3392 1803 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1408 lineto 832 1792 moveto 832 3011 lineto 1719 3011 lineto 2126 3011 2368 2782 2368 2402 curveto 2368 2021 2126 1792 1719 1792 curveto 832 1792 lineto end_ol grestore gsave 20.694733 2.692500 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 20.864067 2.692500 translate 0.035278 -0.035278 scale start_ol 832 1408 moveto 1879 1408 lineto 2141 1408 2348 1488 2527 1654 curveto 2729 1843 2816 2065 2816 2381 curveto 2816 3028 2451 3392 1803 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1408 lineto 832 1792 moveto 832 3011 lineto 1719 3011 lineto 2126 3011 2368 2782 2368 2402 curveto 2368 2021 2126 1792 1719 1792 curveto 832 1792 lineto end_ol grestore gsave 21.270467 2.692500 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 21.473667 2.692500 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 21.812333 2.692500 translate 0.035278 -0.035278 scale start_ol 1346 1258 moveto 2157 2432 lineto 1723 2432 lineto 1143 1550 lineto 562 2432 lineto 124 2432 lineto 931 1239 lineto 78 0 lineto 516 0 lineto 1129 933 lineto 1733 0 lineto 2180 0 lineto 1346 1258 lineto end_ol grestore gsave 22.117133 2.692500 translate 0.035278 -0.035278 scale start_ol 1788 2432 moveto 1120 533 lineto 502 2432 lineto 92 2432 lineto 908 -16 lineto 760 -403 lineto 700 -575 613 -640 452 -640 curveto 396 -640 332 -631 249 -612 curveto 249 -963 lineto 327 -1005 406 -1024 507 -1024 curveto 631 -1024 765 -982 866 -908 curveto 986 -819 1055 -717 1129 -521 curveto 2203 2432 lineto 1788 2432 lineto end_ol grestore showpage kamailio-4.0.4/doc/sip/figures/redirect.png0000644000000000000000000004514312223032460017317 0ustar rootroot‰PNG  IHDR§%ÏõºsBITÛáOà pHYsÐй‹çŸ IDATxœíy ÔÛÿÿÏÛ¾dI!RRi•6Q”Ku‹hQ*íû­n·õjßnÚK*Š6­ˆºÕ•"J¡M)EHÔµ1Ûû÷ǹŸ÷w~3cÌŒe†y=þg}·y?笯C$‰äiЬ€ê _€êÉZZZ<}útCJãùÐÌ=z´GªªªzzzãÇ‹‹“Š@ËTOî ÿGAAÁ¦M›víÚuèÐ!i%€z•tçÎW®\9þyòÄÔÔ” Ùºu«¿¿Ù´ ¯'ïôêÕ+77—ú3!!ÁÎÎNMMÍÌÌ,$$„;eFFÆèÑ£555uttüüü*++©(î¡.N_°`A»ví¸;kBŠÍÍÍ1c†‘‘‘ªªjŸ>}®^½JH ÆZþýûwCCCîÉ««R#³²²zõêÅápxÊçp8=zô¸ÿ¾èE ´9H@žàÿ?{ö¬C‡øsZZš¾¾~dddMMMFFF—.]nݺ…£Þ¿O£ÑN:U^^^VVäååE•ÆýÁ××7**ª¦¦†ªBH±¹¹¹;v (**ª©©ILL7n\]¦ò0pàÀ“'OÖ+¤R~#===CCCyJ8sæÌ AƒÄ- }@õä n)©¬¬¼s玕•ÕáÇqˆ——ב#G¨·oßvvvÆŸ§NÀ]Ôž={ª^`` O¥BŠ9sæŽ;ê5U éééfffƒ úóÏ?Ïž=ûîÝ;+å7òÙ³gfffµµµTHmm­©©)V7±ŠdP=ù‚¿³ß·o_*ÖÀÀ ''‡ú“N§ëêêRQÜEåçç T=žd‹522ÊËË«ËÔz›Ã`0bbb6nÜèíí­¯¯ïààŸŸ_o¥9rä¡C‡¨?<اOÉŠdP=ù‚[JÊÊÊ._¾¬££sþüy¢¤¤Ä£‰xI$IEEE‹Å]“ɨzl6›§R!Å*))1™ÌzMƒ±|ùòÑ£G×[©@#ccci4Zee%I’•••4íÚµk’È8 zò¿”\¼xÑÊÊŠÃá$©¯¯_RR"0£è}=þ¼BŠ544¤zgõšZ/•••ªªªõVZWÉýû÷ߺu+I’[¶l±¶¶ÆÏD²¢YÖpåÉ“'kjj^¹r!äää-0™³³óåË—¹CÂÃÃE¬BH±®®®çÏŸ¥¨¨Èf³…»téR‹ÅRTTÔ®]»z+­‹5kÖdeeíÛ·oýúõÔš¬E2´ehVþÇoܸѭ[76›ýöí[“+W®Ðét:~ïÞ=777œ¯á†„„TTT”——ŸW®\Áá:uÂ.e>yòÄÏÏÏÔÔTIIIKKkðàÁgΜ¥R!RuêÔ)„ÿ† Šd8›€|ózÈ zÈ zÈ zÈ zȼGm@,Ì`0¤mêÝ»wpp°´­Z z@ƒHMMUSS300¢ _¾|QWW—¢@ËTh( X±b… X´h‘kZ0¯€|ª€|ª€|óz@cÂ}…P“¢¨¨¨¡¡Ñ~ü(Ý% åª4&ݺu;vl3TtñâÅf¨h•À¼ò¨ò¨ò¨ò¨ò¨ò¨ò¨ò¨òœÍ ’$322˜Lf3Ô•››Û µ­P= Ñ BKKËØØ¸êúþý{3Ô´J@õ€Æ¤sçÎŽŽŽÍPÑׯ_›¡ UózÈ zÈ zÈ zÈ zÈ zÈ zÈ zÈ zÈp6h4H’ŒMOOo†º ÆÔ©S›¡" õª4A¸¸¸Lš4©ê‚›!‰.ò¨ò¨ò¨ò¨ò¨ò¨ò¨ò¨ò¨òœH ’$ãââšç.‹Å‚s¸€d€êA#GŽ„s¸€Œ#\ä P=ä P=ä P=ä P=ä P=ä P=ä P=ä P=ä P=ä 8‘4&,«yΊµmÛ¶jZ% z@£A„OS×Âáp`ŒH¨Ðh$¬©©ÙBŒêŠ}”þ(<-|ë¯[ÛéµkH-€<ª4&ÚÚÚ ô¹"d€œ™•ˆ+/V®ûuݡهÔÕÕR ·ÀHE •þØl¶©±©Ñ[#•X[S[<@b@õYäÒ¥Kº\ܽ{·¦¦FCCc§çÎ_®ü²xìbi´``„ È"mÚ´±··§þLIIÁTUUGôw’’Q@+úz@Kâúõ%%;_½z"mC€ ¨Ðbxö,ÙÎ.qóæâÄÄ`ƒ!ms€– ŒpÆäÝ»w Ü¥Œ÷âýøñcóæÍT`FFÆ”)S °¿~}XQÑëaÃf«¨¨4ÐT@nÕ ‚ 6mÚÔ(E¹ººrÿÙ¿üÁÓsŧOÙ;[4J-€|#\ …’4P=ä P=ä P=ä P=ä P=ä P=ä P=ä P=ùÂÉÉ)>>¾©k©®®þçŸøÃ###ù“’’¾}û&bbµµµt:]|39TO¾ˆwrrjjíãp8555üá?þäd0l6[ÄÄI’$IR|39TOiíÙTO~íäP=y´7@õ„@ûyTø?@ûyTO.`0qqq¿ÿþ»(‰Aû€Ö xmµTVV>yò$!!!!!!55\®FûzAp¦Ñh™™™“•””èëë—–– ,§´´T__¿¤¤„»L¢n„Ä6°E/^¼X´h‘®®n˪—¯_¿>|xèСÚÚÚ®®®»vízòä ‹Åêß¿ÿªU«D)ÁÑÑñÁƒióz˜ß~û-;;;66–?ÊÍÍíëׯéééTÈ«W¯òòòÜÝÝ%¨HôÕ ccc<ë÷믿.^¼˜$Éœœ„ÐçÏŸ÷<<‹ÅŠŠŠrss311Ù±cGAA™™ÙŠ+ ‚‚‚F%–äÁü Ï´Œ¾BHAA!88ØÛÛûÅ‹jjj|øÐr%oÇŽÙÙÙ²ÐÏ*..Þ´i“´­Z zÍć¼¼¼† òøñcvìØ±W¯^M˜0AÚv5ˆÇK¼_ºq>|xbb"“É”¶!@ T¯ÉùñãÇÒ¥Kmll®]»¦¡¡±qãÆ?.Z´¨¥/YìØ±CGGG`+´´´øÃuttø544"0±À@„A#FŒ˜:uj=F¨^“B’dXXX·nÝŽ=ÊápæÌ™“••µmÛ6ŠÐâxüøñСCFihh899ñ‡ <=`À~W:u%r¶šF£)++Ã8¨XÍh*>}ú´`Áì.aäÈ‘gÏžíСƒ´j4„tô¤ÈðáÃ/_¾Ìd2eÍ0@¦€¾^ãÃf³÷íÛ׳gÏØØØöíÛŸ={öÞ½{­IòÐŽžq.  zLzzúÀÿý÷êêjŸ·oßNŸ>]ÚF52²ÙÑÃÀ8¨á6?þܲe˾}ûX,–™™ÙñãÇ]]]¥mT“ðøñcîùµ=zHוK^^^×®]©?aœ ‡…ÍV­€Ì›7ïãÇŠŠŠK—.ݶm[›6m¤mT“°cǎׯ_S‹eee?~ü®I!cccn¯‹ÅÅÅW¯^•¢I€Ìª×P Æòå˃‚‚B½zõ:uêTc¹Ø“MÜÜÜÜÝÝe¿ߥK—-[¶HÛ@æ€y½‘™™Ù¯_¿   55µíÛ·§¦¦¶nÉ“å=`ß2P z’6`À€×¯_wëÖíÙ³g6lhrÐdséV °ž Ô¨ž$TUUÍš5kæÌ™UUU¾¾¾©©©=zô¶QMN êèa`=¨žØ¼~ýÚÎÎ.44TSSóÌ™3aaašššÒ6ª9hA= çü€ê‰ÇéÓ§øöí[›§OŸÎœ9SÚ5-®£‡q.À¨ž¨ÐéôéÓ§Ïž=»ººÚÏÏïéÓ§Ý»w—¶QÍGKìèa`œ ð;WDâåË—“&Mz÷î]›6m[ßq ádgg7®gÏžÒ6Drž={öîÝ»×WšP½ú‰ýõ×_þüÙ³gÏ+W®tëÖMÚ57µµµÕÕÕÒ¶¢¡hkk+**JÛ @ú€êÕC@@Àš5k8ÎäÉ“CCCUUU¥m æõê„Íf/^¼xõêÕ¡€€€ððp<h€÷ÁTUUM™2%&&FMMíìÙ³^^^Ò¶€ÆTOEEEcÆŒIMMm×®Ý7† "m‹h4@õxÉÌÌtwwÿüù³……Åßÿmee%m‹hL`^ïÿ#!!aÈ!Ÿ?¶··OJJÉ€Ö¨Þÿ>zôèÒÒÒ &Ü¿____ÚÐø€êýÇîÝ»}||jkk—/_~íÚ5uuui[Ôú©¨¨Øºu«´­ä˜×C$IΚ5+,,LAAáÀË—/—¶ErAIIɈ#222Úµk·xñbi›Èò®z$I.]º4,,L]]ýÚµknnnÒ¶H.¨©©qqqÉÈÈèÖ­Ûĉ¥m _ÈûwÕªUÇŽSSS‹ŽŽÉkÊÊʆ öâÅ ‹HÛ"@¾ë¾ÞÚµk8 ªªáìì,ms䂲²2;;»>XZZ>xðÀÐÐPÚr‡üöõüýý÷ìÙ£¬¬|åÊ•_~ùEÚæÈ¥¥¥...”äKÛ"@‘Ó¾ÞöíÛ·mÛ¦¤¤>nÜ8i›#”””888¼yóÆÌÌ,!!¡C‡Ò¶S䱯÷×_ýùçŸŠŠŠçÎóôô”¶9rAqq±““Ó›7o¬¬¬=z’H¹ó4uðàÁ+V(((„††Ê›sPiQXX8räÈÌÌL“§OŸÂ\ ]䫯¸råJ…“'O‚ä5_¾|qppÈÌÌìÕ«Wjj*H u䨯wêÔ©ùóç#„,X msä‚>8;;ùò¥_¿~‰‰‰pâ䥯¶`Á’$<’×<¼ÿ~èС_¾|:th\\H #ÈE_ïÆ&L IrïÞ½¿ÿþ»´Í‘ ’’’ÜÝÝKKKïÞ½«¢¢"m‹à?Z¿êedd :”N§oÛ¶mãÆÒ6G.HMMutt¬ªªòôô¼páxÞdŠV®zÅÅÅvvv¹¹¹3fÌ •¶9rÁ¥K—f̘Á`0¼¼¼.]º¤  /³(@K¡5#kkk=<|¸´-Ihñ}=’$ÝÝÝoß¾=lذøøx8ÕD$&&zzz~ÿþ½[·n111–––Ò¶$¤ÅkÄÁƒo߾ݾ}ûððp¼&âÌ™3NNNß¿=ztrr2HТiÙ2ñâÅ‹µk×qúôéŽ;JÛœVƒÁX¸p¡ŸŸ›Í^µjÕ­[·ttt¤m4ˆ<¯G§Ó§L™Â`0–.]:vìXi›Ó )//ÿå—_’’’ÔÕÕOœ8s¦@ë ÏëÍš5+44ÔÖÖ6996O4:III^^^;vŒ‰‰éÓ§´-€Æ¡¥Žp£££CCC544`¿XS°uëÖ¡C‡ >üÅ‹ y@k¢EªÞׯ_gΜ‰:xð µµµ´ÍiUTWWûúúnÚ´‰ÃáÌ›7ïÞ½{4?Yrròœ9sÌÍÍ•••uuu‡~þüyî¹¹¹ÚÚÚ:::žžžyyyTTbb¢FSSSëÓ§Ï… „ØCĤI“Fy{{7ÿ9k5uÓÌæ5T+ZGsBˆlàY¼1cÆHÛÖFNNN÷îÝBmÚ´ ’²ÿþGÍÌ̬­­­¬¬|ðàÁ€üýýqlee¥¹¹ùöíÛKJJJJJ¶nÝjaaA§Óq,BÈÙÙ999™Á`¤¥¥õíÛ÷Ô©SuU„277ÿüù3OøçÏŸííí›ÿ \o-ôÕ¢VÓ´–׌sçÎ!„tuuóóó¥mK«âÊ•+ÚÚÚ¡.]º|úôIÜìyyyºººøs@@€wì¤I“<ˆ?¯]»–ÃáPQ™™™u‹Ú³gÏòåËy—-[ª×œ´š¦µ°fêéé!„Μ9#m[Zµµµø~t„ФI“***$(äÛ·oíڵßÿùçîØÛ·o1B`ƪª*•ºŠE•”””––R¥¥¥;w®©©á~£££¨¡¡¡¡¡1pàÀ›7o’$ùþýûž={²ÙlžbÙl¶M\\I’ñññ PUU555 æN–žž>jÔ( mmíY³fUTTˆ¥zu•Œ*//÷óókÛ¶­¶¶öòåË™LfeeåìÙ³ñœÀ’%K˜L&•¸¢¢bÖ¬YÚÚÚ£Fzùò%wNå­¬¬œ?>~k¨ð ¨««Óh´E‹UUU IÏóaàÀ111<­ÎÈÈèØ±£ð'#;´0Õ?~}údkk‹RPPؼy³%TWW'%% >|ãÆ8„F£}ûö;Íׯ_ f¿té’­­m]…ã7mÁ‚{öì¡wïÞ½~ýz’ë…Œ‹‹Ó××ŠŠ¢Óét:=22R__?>>ž$IOOÏÐÐPžbÏœ93hÐ ’$ÓÒÒôõõ###kjj222ºtérëÖ-œæýû÷4íÔ©SåååeeeAAA^^^¢«ž’BS§N ¯®®þò勃ƒÃöíÛ§OŸ^UU•——7|øð]»vQ‰½½½¹Í Ñh>|¨·á8¯¯¯oTTTMM ·‘;v ýùóg~~þ´iÓ–.]*$=ê]¼xqäÈ‘<­ž3gÎÖ­[…?Ù¡%©žöÖÑÑÉËË“¶-­„ððp¼ëØÂÂ"55UÜìÜÄNNNT÷DYY™Á`p§¬­­Ø¡ûñ㇅…Ell¬*H’ÌÌÌ466Æe2 SSSü ^È_~ùåôéÓÜO:åîîN’ä³gÏÌÌÌjkk¹155ÅäååuäÈ*êöíÛÎÎÎøóÔ©S¸Ëܳgèª'¤d„PPPõôéS “'Or‡ØØØP‰ù͘6mZ½ Çy…\QQadd$$=ê1™LccãW¯^Q JJJtuuy~çd™£zx… ‘¶-­‹µaÃ,X&Løùó§ÄE•••EFFvêÔ‰ZÍQõ ‡ &DòH®WÎÕÕõìÙ³$I†……Mœ8‘'VOO¯¨¨ˆ;#÷ˆ{äÈ‘‡¢¢<اOüÙÀÀ ''‡Š¢ÓéÔì¤AAAw™ùùù¢«ž’Bß¿§¢~þüÉ¢ªªJ%æ7ƒê8 o8^’$«««W¯^mff¦¤ôß!!éù‡º;wîœ={6•`ïÞ½<Ó¸2N‹Q=ooo„««+÷D8 iiixǺº:w§£!$%%™˜˜àÏü#ÜÂÂBžn^^^Ïž=ïÞ½+¼XêM»}û6ÛÚÚràðEEE‹Å‘Éd***âϱ±±4­²²’$ÉÊÊJvíÚ5E½ùA)StÕR2!BBBüf())‰Òp„ÿœæœ9sÆ÷âÅ ¼ªÎÝ(éùUïÇzzzX¦Ùl¶¹¹yRRR]Diª‹RSSƒ±máp8{÷îÅ/¤±±ñëׯ«äÚÚZª{âääijšqçÎîÕŒ‚‚‚ž={ ïåa¨7ÃáX[[ÿñÇ={öäÕÓÓ+..æÎÈÝå!I²ÿþxâiË–-ÖÖÖÔo§¾¾~II‰ÀªØ×R²¸ªÇo5&ÞpÖêêêrgùøñ#¿®Õe 8wîÜmÛ¶‘$yãÆþýûóç’eZ€êÕÖÖvíÚ!´{÷niÛÒ²)((pqqÁŽE‹UWW7bá Ԣľ}û¦OŸÎ;eÊjçÊ·oßzöìIMí ‡ûM;~ü8Bèĉü±nnnxüKP "ô IDATBMo‘$yõêUmmí÷ïßkkkŸ;wŽ ÷ööæ_ëÀøøøìß¿Ÿ;dïÞ½¢«ž’ÅU=~3¨Ç+¼á­UWW/++£þܸq£ª÷æÍ›:0 —ºš)³´ÕÛ¹s'BÈÚÚš{B—k×®©¨¨ „ôôôêWÖ‹‹‹KdddQQ‹ÅúñãGxxx§Nþþûo[QQann¾sçÎÒÒÒÒÒÒíÛ·sïRîÝ»·ð-ÐÜWî5\CCؘ˜ªªªªªª¨¨(F „I’d³ÙVVV4­sçÎÜC·oßš˜˜\¹r¯Þ»wÚ!€×pCBB***ÊËËOž<‰§YD4XHÉâªÞ¤I“¸Í000à^ÃÒpÖN˜0ÁÏÏïÇGŽñðð@õH’tqqÙ°aƒ¾¾>ϱì#몗““ƒïýÁ[« (++óõõÅSK£Gn”µ¶û÷ï{xx´k×NIIÉÈÈÈÓÓ399™;Áçϟǯ¥¥¥¥¥5a„ÜÜ\* ‚{;7"ªI’7nܰ³³SWWWWW·³³ãßSvêÔ)„÷R)&==ÝÍÍ­M›6êêêÜÓÒÒ\\\ÔÕÕõôôüüüÊËËEW=!%‹«zxsŸžžÞ¯—‘‘ÁRHÃZûýûw ¶mÛΚ5‹»Qb©ÞÍ›7BëÖ­ü dYW=¼AoÊ”)Ò6¤¥òðáC„††Æ±cÇ`-¨ÅQ¯ÎJ ƒ¡¦¦öåËi"62íiêï¿ÿvww×ÖÖÎÌÌìСƒ´ÍiaTUU­]»öرc$Iöë×/** ®Žk‰à•_i[ÁKYYY```vvvHHˆ´mÙõ*ZSS³lÙ2„ÐæÍ›AòÄåáÇ~~~ÙÙÙ***þþþkÖ¬áßH’A„ººº‹‹ > ÝâÝ7aÿþýÙÙÙ={ö\ºt©´miI0™ÌÍ›7ãóL†††wîÜÁ΀Š vôdÐ$±ÅÎ3Bèû÷ï–––=2dˆ´Íi1ÄÅÅÍ;÷óçÏÊÊÊëׯ߰aƒ²²²´ÙBFûz[·n­¨¨pwwÉ‘ÊÊÊÅ‹c7\]»v½víZ=¤mÈ"²Ø×ûðრ‡ÃÉÈȰ±±‘¶9-€7nÌ›7¯¸¸XUUÕßßÿ?þ€Y<¨ Y|7Ö¯_Ïd2çÌ™’W/yyyK–,‰ŽŽF 6ìäɓݺu“¶Q ÓÈ\_/))iÈ!YYY°t+‡³ÿþ7bw&þþþëׯo=7@“!s}½Õ«W“$¹bÅ }Š1bÄÑ£Gá"` ™S=€‡òòò•+Wâ;á´µµwíÚµhÑ"i-èRÉ. # ÀÄÄäôéÓxH[PP’ DæÖpLTTÔêÕ«±wï±cÇ8pÀÂÂBÚF@kTOæøò勯¯oBBB¨{÷î—/_†³eЈÀW†ÈÏÏ÷õõ577OHHhß¾ýÑ£G322@ò q¾žLPVV¶k×®#GŽüüùSEEeÙ²e6lÐÕÕ•¶]Ð Õ“2L&sëÖ­{÷î­­­EùúúèëëKÛ.hµ€êI ’$ÃÃÃW®\YTT„1bÄ_ýÕ¯_?iÛ­˜×“çÒ¥Kݺuóññ)**²±±ùçŸâââ@ò ©¯G’d\\ÜÅ‹³²²²³³‹ŠŠZÊÞf}}} ++«I“&¹ºº6Å‘?~())‰8G’dTT”¿¿ÿëׯB:::þþþ+V¬_)@ÓÁf³ÿþûï«W¯~øð!;;ûû÷ïÒ¶H$,---,,zõêµfÍš¶mÛ6ZѯPc±X7nÔÔÔl´ú¤‡††ÆÆ¹o€n8………;w %qxx¸¹¹96¦S§NÁÁÁL&³jkkW¯^­¦¦&ÝW¯á1mÚ´¯_¿6Êc¦zOŸ>¥¶MlÞ¼ùÖ­[Ÿ?n)wªr8œ/_¾$&&=z”rÄdkkËs‰²Ä”••á‹xú÷ï/ÜŒ7nP£×:=z´Å]´8îß¿OýÊš˜˜;v,111//¯¥¼¿L&óåË—W¯^8q"ö ®¬¬¼{÷î†w\êT½“'OâaW‡þþûïV# ܾ}ÛÈÈÿn\¹r¥¥UWW;88P¿E•”Íf_»vº¢L[[{ÿþýпš={öào……Åýû÷¥mNCùñãǼyó°"9884°Ó XõN:…+X¸pakê•üüùsÚ´i!%%¥˜˜‰Ëa2™¿þú+w|ùòåÜ X,Ö¹sçºwïNõï:Ôšž$ Ë`É#bÕªU­éWöùóçíÛ·G¹»»×ÖÖJ\ŽÕ‹ŒŒÄ’wàÀX(»üñÇXø’’’$ÈÎápüüüxæÚ·oÿ ,+$$ÄÀÀ‡›™™‚ÞÍö8KDhh¨´mi|Þ¼y£§§‡š0a‚Ä…ðªÞ—/_ðÚž}ûfžLƒeËÔÔ´ªªJܼkÖ¬8áº}ûv†ÿ477¿téRK™CZïÞ½Ã7 Ÿ>}º9ëmÜEBá¼yó¦M›6¡K—.IV¯½1cÆÜºuËÓÓóÚµkßíF!55õîÝ»ùùùß¾}ûöí[^^Þ÷ïßõôôLLL ŒŒŒ‡ÚD[:X,V¿~ý^¾|¹nݺ;wŠžqß¾}¿ÿþ»À¨¾}û¾xñ!Ô¡C‡ 6,\¸ö£Í I’=š3gΩS§š¨–ššš7n¤¤¤äååæçç°Ùl==½:tìØÑÐÐÐÄÄÄÓÓ“šÑnt‚ƒƒçÎK£Ñ²²²tttÄÎÏ-W®\Aiii5Ö 17L&óîÝ»‹-244Ѷ¶mÛΚ5+**êçÏŸnOrr2AŠŠŠYYY"f "dŠŠŠãÇ‹‹ktS@‚ƒƒBúúú%%%^ø·oßG%ðªeï…‘‘ÑòåË݇3xð`„Ð’%K$Èþÿ©^ÿþýB"î>‹7nXYYQCKKë?þ8zôèõë×?~œ]UU•“““œœuüøñõë×soJìØ±cXX›Ín\«æÏŸš?¾(‰oÞ¼YïÕÚÛ·oo\ @tºv튺páBã[YYéïïOmú#bøðá—/_NHHÈÊʪ¨¨`³Ù_¿~}þüù­[·çÍ›‡¡{{û'Ož4®U¯_¿&BMM­¬¬Lܼÿ§zoÞ¼ÁzT]]݈Æ=þ|øðá¸ñfffþùgjjªˆ³]oÞ¼Ù¹s'µÚ»wïÆ]ƒÇMnÓ¦M½M~ô葆††pÉCYXXÀD RRRBíÛ·oÄE[‹LMU?>44´¢¢B”¼'))iíڵؕA^^^ÙÙÙeI’NNN¡'Nˆ›ñÿT¯lŠØñ‘;và®oÛ¶m>Ì`0$(„ÍfŸ;wŽÚf}zÇŽK–,ñôô>ÿÂð»„3f BÈÍÍM”æœ8q!4gÎQ€ 2a„PDD„(‰]]]B>>><á‡Béêê~üø‘?×éÓ§±–µk×îÚµk.\ ¤ /ïÖÖÖ:;;#„455oÞ¼‰Ý| „z÷î-°ã¶dÉ„‘‘Ï\Þ»wïTUUB=ª·-l6!¤   JÃ)þS½I“&!Ñε1™L<Æ#ÒÇÞÞ!´lÙ2þ\Ÿ?ÆcR++«”””òòr|Z¥sçθ?Ou£££333ñÆi]]Ý´´4þbp/g¡¸¸XII‰ ˆ‚‚‚z[Šòõõ­7%È&^^^!Qü§}ýúUAAAYY™çüFQQ‘––V]S=”äùùùÑéôØØXÜáÀ^#µµµ_¿~=yòdüæbeÌÊÊJII騱#B¨oß¾üÇE8NïÞ½B7nä‰Â³u"v÷pbíRüOõ°0Ÿ;w®Þ <@uéÒ…'o× Ñhü}ã‚‚ÜkUSSËÌÌ$I2//o}trrºÀÀ@Ò¶m[<¼×ÖÖf±XµµµãÆÃ? oß¾å)™Édâ=ÌÛ¶mã‰òðð@¢3¹xñ"BhÊ”)õ¦ÙdêÔ©H´Sø|îøñãyÂñYàðèÆXòŒŒŒðúÆ“'OðнP/œxÍš5$IæååážMŸ>}ø…éÉ“'A¨¨¨ðô.ß¿DÞR‚-ËȪ7sæL„5Ò>ëÇ#ÃíÚµC={–?‹››î…â)¼ÀúòåKüÛ‚Z½z5ÑÖÖÆ!jjjx9"&&æùóçxºÁÖÖ–ÿÁÅÇÇ¡ªªÊ³x²{÷nþY?`? žžžõ¦ÙÄ××!tìØ±õ±jÕ*„Ж-[¸³§¥¥¡¬¬üáÞ’ ©÷!4lØ0<®ºyó&µrˆ]ê^»vÚÝ5vìX&“ùóçÏÉ“'¿yóóÖ¯_Ïo9Vž1cÆð„ãa_AAA½-Â>¢JKKE\ÿ©ÞܹsBmëoÑàiÀ_ý…»»üë>—/_¦$«²½½=懪©©Í˜1ƒÊ•˜˜¨ªªª¤¤tëÖ-’$=z¤¦¦¦««›ššŠw!ð·w­yÎ!cuVUU­·Eø©;Vô§2ÅìÙ³‘8œ:uŠ;;Þõ&pÿ‡··7ÎB±?oœ UWWøð!•øøñã¡!C†TWW³X,J0<˪ÂùOõ.\(VG¥Š`±XÆÆÆHЪBuu5÷‘Œ9sæ „RSSq‚‡òø¸wï^Ï~ýú5Õõ322¾••• yjyõêAšššÜÛ‹¢¢¢ÄjÑèÑ£Ej S¬\¹RWdÚ¶m{÷î]*ï§OŸðL¿pà³½x誦¦Ö«W/„µµuee%NÀ¿‡900°²²’Ãáà—3|øpìéCàŽÂeË–!¾åDÑ[¤§§'–›¨ÿTN§îÕÛëׯ£:Þc×U666”ªAmÒ‹ÅÂCf;;;|Ù…‰‰ þéØÝÃç¹?3 ±Z*ùyg̘Á…»ëׯÇ{<B¦¦¦¢èËßÿÓS]Å;v(++Á¿7++ ORÕµ¿¯Ñ©çfHQ6lBèÈ‘#üQxAýàÁƒl6/3íÝ»WÄbñÝxfTQQ1** ïHìÓ§â7n „:vìØèÞ¨ S]]笨áEMM leffÖÔÔ899á•YKƧ0NNN555XöZðÔÿž={ÚÑh¨êaGêêêüñ***”””ž?¾lÙ2ƒ!®ç¾;vàîáÚµkñ15<±Ê¿}Ãáà.a]Ç9@Fptt|ðà´­øÜ]èÛ·/>åfkk{èСµk×ÖÔÔˆu¥*‹ÅÂ[[[ggçgÏžá^‹@)BÌh ªzAAAHÐB8I’Ïž=CuïÞû&Ø´i“å/[¶láÂ…xOä^¿~]`JTÇ:ȸû##Ú‡gßzÃ=pàBÈÕÕUâ»Ãjjj¦L™‚O|¶k×.99Õ±w9 ‚ÈÏÏ—¤bÒPÕûå—_PW“`içöj/—æÌÌLÊÉŠššž_xr;P°¶¶–¤Ð\p¯¡IWûØl6^–}ùò%ìÊ•+Ñÿv_à¹&q½4s8¼1ëf§Nð~:Οxüøñ¡   # R=:Žg(.ã.øÁáiQ‚ Äu¦ŠÇ­ÚÚÚøt->ª"Ðu"ƒÁÀãßÜÜ\ ÛMâCZÚ‡—h;uê$0OÃ!„´´´°ó]===±úfffbѤÖsqUàáôéÓ!www #"ùŒ«‹ŒŒ &“ikkKù˜æ†Á`àGÆápBBBB+W®ÄµEçòå˪ªª?&Ÿœc2™ü)•••GŒJJJ’ - -âã㜜œœœâã㛳Þôôt„~køÁý2mmíÊÊÊgÏžµiÓæþýûxg²ˆtëÖ-00!„7Ϻ¸¸à­yý-á{&ž?.v3ħAª‡ŽØØØŒÅ²Œ£LŸ>ûËaÆ]¾|?ë#GŽà#Àx{ ?øtÚ»wïÄ­¤Nók~±·J~ðû;cÆ mmm•›7oJpÓãœ9s¶lÙ‚êß¿ÿÕ«W‹‹‹ ‚À‡sy055UQQ)**¢ÓéâÖ". zŸZIIÉ;w&NœBDMMXUäååݾ};((èÏ?ÿ\¼xq^^U2?Øl´DšSûpÿ [·ncqߢ¶¶6!!áÒ¥KÇÏÍÍÅ>ND„$Éùóç+++¯^½:66¶²²’Åbéééáy*---ÉÿÂmZ2<ÆYêòôðýûw¼ùò3³}ûvWWWÑï ***Âë¿+W®$I’Ífãi¾º\4ã±­­­­øMiBšü_´Ršz¾ÏÔÔ!ÄïÔ“˜˜ˆ211Á§ŠŠŠÌÍÍ'L˜ úÔÞºuëBA„„„ÿ;$êââRWú‰'¢&¸Ù’Ÿ©^—.]ß:n†Šþç’ï:FM›6M”ë+**úôéC}öìÙƒý:uêTWöÒÒR„ºººL]ÏØÜï ÐZhRÕ«©©QPPPTT¬­­˜€Ífã‹ ?~Ìý2ŠèqýàÁƒèާoܸ'î÷Àlذ!äïï/a“DFrÕc0؇ >ðn¾Q£FEFFr; À}7á,]º!DöóL>Ý"|GžøÑ‹24?R×;Ì«W¯B;w’ûž9s&>ñijjŠUŒß#‡Ã¡ÓéL&¯pÚÙÙᣥØAº²²²ç(ø¦Gooï5L$W=<)`bb"$Í?ðc·Mjkk㕚öíÛ×uîõÝ»w®®®ÅÅÅÜW¯Íœ9‹ì»wï„ÔˆÇÅÆÆJÜ.hR¤®wì`M¸«¡>((((((`ï¡fffØÁ† ¦çp8>>>£Gf0™™™Ôõø"0TŸïÞ§OŸ"„zôèÑ †‰€äªD¸§cÏž=øWûMEéèèð;œÁäççã9Ô‘#GÖÖÖ~úôÉÀÀ!4}útì¨yñâÅ«çÿ¸]€L!u½Ã`ïp+V¬žlÍš5!kkkjwΜ9uÍ ­X±§ññña³ÙÉÉÉêêêø22mmíoß¾ ©«¤¤!¤©©)y«DCrÕÑ'ƒÁÀ[[ÆŒciiIyÙ秤¤ßÏ„ñööf³Ù¯^½š4iäÔë?÷ýû÷KØ*hb¤®w¼Là†nªªªÌÌÌð›nffF9×ãûñÅ>ß(=½sçÎõë×qÈÉ“'…×UVVÖ<ª'ùμü\ïNeeåsçÎ)++ß¼yÓÎÎ.""bðàÁS;v,33!„Â\¹råùóç=zôèÚµkPPAçÏŸ§<îÕ¶‡ÿVcA°Þ=xðÀÑѱ™«ÆïHmm­ðdׯ_WWW¿~ýzïÞ½©Í³üܹs!¤££ƒ·ãÅÇÇ3™L__ߪªª)S¦`×ÅBÀÆÜ×ÒÈH¬—±±±!gggQÇÇÇc‡6nnnuMg2 |ªãïï_UUåããƒRTT¼|ù²(a÷ÿx¥düõ–º÷|†ˆW>~ü÷× ôæÍiªªªð4B¨k׮߾}Û½{7–°)S¦ˆ²ß%77!Ô¡CñZ">’«ÞÎ3dÈÓ'''ã[8µ´´Î;'pj€N§ã«‚æÏŸ…d•””nÞ¼)b-¢_›RAêz‡Á ¦Ó¦M1ý“'OpÇEAAaóæÍÜ~Ë)þý÷_kkë:ìÝ»—r¿xñb½^âýÉ—•ÉU;’Ë%ÖçÏŸ©Þœ®®î¢E‹>|Èí¹àßÿíÝ»7^Ä@ 8P CˆºÀW£ tEÅÕ«WB'N=KYYÙ¢E‹ð®[„Ѐ¶mÛváÂ…‡¾~ý:&&&00pÉ’%ÔµsçÎbí¦ÈÈÈ@uïÞ]üÖˆ‡äª÷òåK$‘g§Ë—/ób#ÂÌÌŒº` £¡¡±eËq÷c§¬ø²!êïÁ࿜¬^RRR¦OŸ.Ѓ…­­íáÇÅrÐBþoçJ¿~ýÄ5I\”„˜.<\-//7£·····wFFFxxøýû÷³²²ÊËËsrrBmÚ´éÚµë Aƒ¦NjooOýªˆNEEBwŨ ]]]„зoßÄÍhggwöìÙàààìììüüü¼¼¼âââ.]º›››Ï›7ïLl µË¯é\õLMMÕÔÔ KKK%0ÔÖÖ–ÚÄáp²²²¬¬¬xº{âÂáp°óî0ðckkKÄË—/ †ðŽ›@”••ŽX±^°›)îs¨M„ä*£¤¤Ô»wo’$SSSj„‚B·nÝ(y¡>Ðét###q/ÓyC[[ÛÒÒ’Á`à©*Yp±Ó&¥ABƒýã[Edl vy€pœBáááÒ6!„¾ÿ«  Ð [¤Ÿå8n îIDATzx3Ý™3gÄõš×Dœþœûn<*™‹‹Ë‚ ðÍÅ‘‘‘ÇŽÃw£&V½OŸ>YXXää䘚š.^¼¸°°_Í,êêê¶mÛÖ{hU4õ6h ¥Ãý%¡>ïÛ·oêÔ©“ÅÄÄ888ðâèè-°L!b}?ñMRâ~Õ/]º$k­MMC·ÈòÉo¿ý–ýîðàææöõëW¼]óêÕ«¼¼<Éæ[ ALillŒ¿Ó¿þú+ö>‹ü|þü‡ógù÷ß7lØ a@ËT…àààåË—óozPPPXºté¡C‡¨Ã‡/Y²D²]è«…gIIIÁ.RRRh4vŠÉÏ·oß&L˜„·­òC£Íë­þy=*jóæÍ cçÎ È……EŸ>}|}}»wïneeÕ<Æp8œØØXrûòò   ðß±HáÂIE5jÍš5ùùù,ëßÿ‰‰5jŽJKKÓ×׌Œ¬©©ÉÈÈèÒ¥ µ!äëëUSS#\›© ácbb<<<¸£Þ¿o``pæÌ™ŠŠŠòòòàà`oooÊ0áµÏš5kÇŽ #99ÙÊÊêäÉ“B~ Ä2 ¤¼¼¢<–ŠŠŠ„„„aÆùùù‘øpQœ²²òÏŸ?¦ñòò:räõçíÛ·©ìÂí#I2##_·Fþ¯·üéÓ'*vêÔ©‡âN¿ÿ~Ê0áµïÚµ‹;ã½{÷¨ŠDQ=ᆀԗW ûzÖÖÖ¤èª×§OŸß~û­¨¨ˆ?ANNõ'N×ÕÕ¥²·$I??¿3gÎPîÚµkùòåÜåýú•;}AAe˜ðÚ ¹3VUU©©©ñ4MbÃ@êÀË+žt:=..®GëÖ­#±êÁb±øs²X,üùãÇcÆŒQUUµ¶¶öõõˆˆàp88Šßé3Þdëf³ÙÂíûþý»©©)w/º¸¸¸}ûöåååøOEEEó˜L&Õ*áµ yõ>¸z ©/¯@&HOO777'±êÑh´ÜÜ\þD999Ü!555éééAAA ˜>}:ÄÞ„D¯›‡íÛ· ìŽàÂ.Ä­]ôW¯a uàå=;ƒÁPQQ!±êM:uçΛäãã#°Ð²²2ª·éíí*zÝ~ühnnŽ%¦NÊ=ø'IòàÁƒTÉâÖNòÿ ‰kHxyEÏž––fjjJbÕËÎÎîØ±ã¦M›Þ¿_[[[[[ûþý{ÿ:P“ááá,«¸¸xÓ¦Mîîî8êíÛ·&&&W®\¡Óét:ýÞ½{nnnBêææâÅ‹cÇŽåîî~åÊ’$ß¿oddVYYYQQqæÌîe qk§ñ‚ÕÕ—À0:ðò „'{UUÕýû÷{ôè±ÿ~«I’ùùù‹/¶´´TUUUUUµ´´\¼xq~~>•íÁƒzzzJJJfff+V¬À«!˜ôôt77·6mÚ¨««;88ÄÄÄ1›Þ½{W`Ô;wìííñç´´4ggguuu===??¿?~¨ªªJV;Ñ©S'¼ßGbÃ@êÀË+0%7jjj}ûö Á±-ÒçÊãÇ/^Ì} -Yxyeè®ÆÿäÉ“ÚÚZ:~÷îÝ3f¬X±BÚFP?2øò¶Œ¾Þõë×÷ìÙóòåKEEÅ=z,]ºtÚ´iÒ6 €ú‘Á—·e¨@cÑ2F¸¨ò¨ò¨ò¨ò¨ò¨òÅÿ`ôML‹IEND®B`‚kamailio-4.0.4/doc/sip/figures/redirect.dia0000644000000000000000000000245212223032460017264 0ustar rootroot‹íZÑnÛ6}ïWÊË8´HŠÕÄ)’b l°¦{5h‰¶¹Ê’AÑqœ‡}û®dÕql9‘TméâHèŠG—ä9—‡”ÏßÝÎbëFêL¥ÉÀÆÈ±-™„i¤’ÉÀþ|ýó)·ß]¼9”x ?-f´H²ü¿=5fþ¶ß_.—(^e¤Åj2Ùÿ[ıèÃM}ûb»u$ŒX_Æh5Zi%b&öH„_&:]$QÙ"LãT[7"Ø'ãâc÷/ÎûÚVCÍÅ\ê Êlžf bf5߉í¶Ë—¡ "ÉäâäÒ=Y?²¼Pëùf&ôD%%”–"^wƒ N©Û‹Q'(q'(º• ç©6Z(S"Ò4–"Yƒ½u¡²PÄ0{ áºceLZ•ÅXÄYeýtª÷”‰VÑ"n…v[-Ud¦ÃÛö[¬ÚܨLb¹ÉA%¦%ªB«±]¨Hf‡Fw;¸ÛrZFûR¿?•W¿²´Åb%u vu_Ö¬rLJ¦¯ïMGÉДY”f™ê/Ö©ui]ËXΧi"íûú ÕYEûwç@Ç öµÛót3è‘®=w9Ìh´aOhD2‰å’ƒv† bÔç=LsxPº5†©N6e¸užTÁõ=¦»ˆÇo†4•j255Îe¢=Œ<Ö…$Œ8Èñðqs=ÒãÈåÔq;QEãlE„zÜíB ±À ð«0:F¥•”·æÀ·Úê¬îXÙFv C[¢äZc1Sñ ðE’ÙVfVùb"\ßý>]hü­Ùµ¬r¯=È•Òæ(ÀÜ:”×`±Û¨Øy8ŧ. ˜ÒI2“›Q“Ébö ¶§n©|Ü-¦Õ‘¸Ù…5TáŸàçvÁ 0í¤06LôQ¿@xà¹ùïµ,þ'~á°-:\É;(?¿ªJÐÆòœ¹n7€(a=qpÎ` l†ýÚè#y7,Ò{2×Ý AAÁs«bKׇ°W†D7±zU4ê0Ÿšl¯:_Ÿ°s©–à’h(´N—{¨´1Ä0–ɤ¢4XyËɨÚT¾Ôa‡ÝÆæˆ÷|äwÂ`‚¨Ç€ó¨ãg9¶€/…ë®Kê3$I†0|©¼•ò>‘9м‰úF#•AOB¹7M§Pw Þý•†•4=‹¨C¬ßÒÁp’x|<‚h6¯ß¿ú›/S=ü¦ÕÅ›È=zc¸*kamailio-4.0.4/doc/sip/figures/ua.dia0000644000000000000000000000361012223032460016065 0ustar rootroot‹í\[oÚH}ﯰèk:ÌÅöØ¥¤J¢­Tiµ»RÛ•ö ˜ol“„—þö‹ ñÌ¥GjÚb|üÙß™3ß™‹?||œ&Î=Ë‹8Kû`Çaé(Çé¤ßùöõÓ» óñò͇q½ç&y4uøi!þ×ïÜ•åì}·ûðð’E•Y’x Öý%IÔå_êv.×ÏGe¤>ˆÊ2‡ó’9i4eýÎ0}ŸäÙ<WgŒ²$Ëû(éwÞÞÊŸN÷òC÷Ù¹õP³hÆò%Êt–1?V.fÇ6Ï¿«C?’N.ß^¹oÕ%«]¿œFù$N+¨œE‰º ŒqÓ»ZAI¬ äVPâb0Ëò2â²BfY¢T•ùœ5…*FQÂó±j p—eVÅm”µatŸÑ©ÙU&y<ÞAĵC›g=Äãònðh~s `apñ0aËâ´4DX4B0z¶óxÌŠ]Owýàæ™wÕÑîŽÐW‡_Š«[+mI´`yv½’5§z&ÓÕw³á¿lTVQ)£tåcçs=vVÂÌe9÷;ÂwÄQxÓzºßY¶|Ú@ï¹M³&p†Ã%oF<žIÂ*¨ D@èõ|€8$m É6Œ²<]Ê®yxK’{‹ÚI æŽÅ“»²FÑÂÆ8Å]ö0Øê·4ÔD%?¾²Ç²† HŸ ”?jV¸ Àz ‚ €4-ÅÝÔ7ÙµC[Zv¾]ó·«›7óm–>]FüÓ¹¦q²à׈ҢãåB4Pþ”Õ·o²ysæ6¼½¤j¬šv2'+§š* ÊŸ¦(¼ƒ¤S¶|Z,O4‘ðÆ„¯W>l |Xr3 <”çÂ@‘ÈÁ1ÔIµ£mÕpÉb †ây‡€ÚaG…ˆüqUÄT²7·*Œ_ÎEµsxŠÒèê’‡Rm‰i ¤6Jñê,µ!ô½Úhn«Kzxô +%z<áñüõDÐBŽ#Ž7g!Ž&IОšAj"Ûš™Ajìˉ1€ í“­y“hÏqZf—.šìÁbÙžkk Gó€±\å±Ü9áѤ‘9Wç2s­™ÇÓ, Löøb¼Æÿ]1´äSÅ$§B®BGl×çÒôòøºùÿ{œ²:þkO=ùjãŠK­4±â;‰ˆHØC®h¡œ] Hô¤é€¥cç‹A¢Àk®†%rÄ>’ a`Íòn„½U C5Ûà67Ùóüž ÆqÁïfĶèùŠ]HZ'¼Ï ‡5¦ö#Œk¼³}LôwéùÀ“Ã\Ä­¬P€.ÿ-ö¡Hg€¹ióé1:‡ëΦg0Êä¯ß;t—o`þzöË7ÿ£}ª íbkamailio-4.0.4/doc/sip/figures/message.dia0000644000000000000000000000304612223032460017107 0ustar rootroot‹í[]s£6}ß_ÁWG–„„Ð:ÉNÚÙö¡ílg²ûœÁ6±i1xoâ—þö^ 7ˆÈÉÖ!3ù²àpŹ:œ{e_|z˜%Î÷(/â,½t ®¥£l§“K÷Û×_Î÷ÓÕ‡‹q~„ïIÎ8#-Ô—î´,çûýûû{”,‹°Ìr”Ä TDýÂ$ ûpPß½Z?{–aõBX–y<\”‘“†³èÒ†£¿'y¶HÇõ£,Érç{˜\ºgwúËí_]ô7ÎÝ5çQþ„2›gE cår¾5¶}žúY0’N®Î®ÙYuÉú…F×/ga>‰Ó*¤šE¥´é,†VP+(¹”¸¸gy™‡qY# ³,‰Â´+óEÔª… ð±i p—e¶/Š»0)ö†ÑßH§fW™äñø@"® mŸuËéíCûÉUËößã"&ÑS qZ¶DX6Bhuoñ8*ÝÝõÁí3§õhÿ@è«áçâêï•¶$\Fy öÓJÖœúžÔ™^› ÿŠFeõM¦ã0;çÎïq¹+e]ŽÇ—î|`JkëqÂólu»QÀ{ ë¼)q i8|J„4I¢G0Ád@zI ¿Ù€ ‰1‘=ÂǸ±Œ²4½Ò±Žó¹ wÇÔåXã ¥‹ÙhÞ8Uh0i¸éÅö ÎØQôedø®³U/¯2f ¤¿¢¹åÄ”òOQØ <@XH©ñ¨”ÔˆbŒ…¦œcb‹ò wǸ¾ž}Ê_Jmµ={ëQJŸô("ðÛSÜpßøcOX]ÞþÕWìyÇY‰ÛgC„·ažg÷5Bp„g q›D餜î< 1 ̱ôÓ¶)ÔKÎk¡¼Ç±•ožÏ{ QVçÆ Ðn1ßÖ#Þ—ŠÜ`õt)gr_£‡rOÊ1Ó”óà I¾Ó`à3„à JµÉÆØ¥šÔ~“¸6´S_èro«üãóÍÍõ¯Ÿ[T„wYúx%õ§sÎâd — ÓÂuŠrYÛŠê蟳EC•Úp†Ó(žLË—$–uÁ¾§xÇú«) Ôu“t=ݯ՚Ãm*‡—&?7O~©RXJ~ †«äÇ*ùAß$ùAé/¿½›Ü7$ñÿû¼†onn=›Å¦×#Š {^Ö;XYRÓè_¯Ì¦LÄíÕ€1!tÕOÁÊö N¥¢Si‹™ˆw¡xkb<Sb0R³´cÖ9Ö…‡Jÿ“)\ãULIn¨UÏÒ´ð.EÆóyE~h›Ž&÷dÏSYé ;»«¾©×’O„*ÿÌfWs+ê]žô"öÔ’n>¯®Åùr#CŒÛê„B¤äWX‰G<¢Ü<á( ºïIYçæokÚ°y’Žž7û% ´tr;ë ‚£Hêe´¡; ¬ë÷y ´ ò4í=ocï ñz¾rx¶ æu¯_¦ ‰¯ÔIX5÷[QïZ8@…ad´:CbA‡Í{ïX¹GàÊgv I…µƒ¨”˜+%†d‘ÝæÓ‘ÍH &OS‰E[%l+±ØPâà8J<«Ä¢SâWWâ …c¬¸Ö”¸Âõ«÷Â@²ÙyâãK±1•§)ŲE—NKV`¯KGt‘è.ü%f=i½K·õî[èºt¯+Å·x'"VTùÒÖ{5œÞq÷‘¯tXB:v:|ü÷"šòx’2LI‹Ï`µí@ô³'ÃXé0%Ä|¢…Øâg¶Ã>°ŸBô(ëtøuØxËŽ)²U–šž¬z>¢z¯„t½‰£ q"OS‰½¶ÛÖ§¶”¸Ú¶&dcßZmšXTâ=aØ·VGtJüºJÌZ4'×9cɯq½eǵÓ·ñÅï­SlNæ/Çý§Ño~Âþêÿ¨9°Dkamailio-4.0.4/doc/sip/figures/ua.eps0000644000000000000000000012305212223032460016122 0ustar rootroot%!PS-Adobe-2.0 EPSF-2.0 %%Title: ua.dia %%Creator: Dia v0.91 %%CreationDate: Tue Jul 15 15:00:18 2003 %%For: janakj %%Orientation: Portrait %%Magnification: 1.0000 %%BoundingBox: 0 0 885 634 %%BeginSetup %%EndSetup %%EndComments %%BeginProlog [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def /cp {closepath} bind def /c {curveto} bind def /f {fill} bind def /a {arc} bind def /ef {eofill} bind def /ex {exch} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth pop} bind def /tr {translate} bind def /ellipsedict 8 dict def ellipsedict /mtrx matrix put /ellipse { ellipsedict begin /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc savematrix setmatrix end } def /mergeprocs { dup length 3 -1 roll dup length dup 5 1 roll 3 -1 roll add array cvx dup 3 -1 roll 0 exch putinterval dup 4 2 roll putinterval } bind def /dpi_x 300 def /dpi_y 300 def /conicto { /to_y exch def /to_x exch def /conic_cntrl_y exch def /conic_cntrl_x exch def currentpoint /p0_y exch def /p0_x exch def /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def /p2_x p1_x to_x p0_x sub 1 3 div mul add def /p2_y p1_y to_y p0_y sub 1 3 div mul add def p1_x p1_y p2_x p2_y to_x to_y curveto } bind def /start_ol { gsave 1.1 dpi_x div dup scale} bind def /end_ol { closepath fill grestore } bind def 28.346000 -28.346000 scale -1.925000 -27.985000 translate %%EndProlog 0.100000 slw [] 0 sd [] 0 sd 0 slj 0.000000 0.000000 0.000000 srgb n 3.050000 14.000000 m 3.050000 16.950000 l 6.100000 16.950000 l 6.100000 14.000000 l cp s gsave 3.750000 15.800000 translate 0.035278 -0.035278 scale start_ol 3136 4160 moveto 3136 1341 lineto 3136 801 2735 471 2074 471 curveto 1769 471 1521 543 1323 680 curveto 1120 834 1024 1038 1024 1341 curveto 1024 4160 lineto 512 4160 lineto 512 1328 lineto 512 509 1106 0 2074 0 curveto 3032 0 3648 520 3648 1328 curveto 3648 4160 lineto 3136 4160 lineto end_ol grestore gsave 4.300333 15.800000 translate 0.035278 -0.035278 scale start_ol 2676 1280 moveto 3099 0 lineto 3686 0 lineto 2241 4160 lineto 1564 4160 lineto 96 0 lineto 655 0 lineto 1089 1280 lineto 2676 1280 lineto 2529 1728 moveto 1219 1728 lineto 1897 3597 lineto 2529 1728 lineto end_ol grestore gsave 4.808333 15.800000 translate 0.035278 -0.035278 scale start_ol 3709 2852 moveto 3546 3731 3029 4160 2129 4160 curveto 1578 4160 1133 3991 830 3665 curveto 458 3272 256 2706 256 2064 curveto 256 1410 464 849 852 463 curveto 1167 147 1572 0 2107 0 curveto 3108 0 3670 525 3794 1581 curveto 3252 1581 lineto 3207 1313 3150 1131 3066 975 curveto 2896 653 2546 471 2106 471 curveto 1287 471 768 1093 768 2069 curveto 768 3072 1265 3689 2061 3689 curveto 2394 3689 2704 3592 2874 3442 curveto 3026 3308 3111 3142 3173 2852 curveto 3709 2852 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj n 3.025000 17.985000 m 3.025000 20.935000 l 6.075000 20.935000 l 6.075000 17.985000 l cp s gsave 3.725000 19.785000 translate 0.035278 -0.035278 scale start_ol 3136 4160 moveto 3136 1341 lineto 3136 801 2735 471 2074 471 curveto 1769 471 1521 543 1323 680 curveto 1120 834 1024 1038 1024 1341 curveto 1024 4160 lineto 512 4160 lineto 512 1328 lineto 512 509 1106 0 2074 0 curveto 3032 0 3648 520 3648 1328 curveto 3648 4160 lineto 3136 4160 lineto end_ol grestore gsave 4.275333 19.785000 translate 0.035278 -0.035278 scale start_ol 2676 1280 moveto 3099 0 lineto 3686 0 lineto 2241 4160 lineto 1564 4160 lineto 96 0 lineto 655 0 lineto 1089 1280 lineto 2676 1280 lineto 2529 1728 moveto 1219 1728 lineto 1897 3597 lineto 2529 1728 lineto end_ol grestore gsave 4.783333 19.785000 translate 0.035278 -0.035278 scale start_ol 3520 2918 moveto 3520 3704 2935 4160 1931 4160 curveto 975 4160 384 3716 384 3002 curveto 384 2521 653 2218 1203 2083 curveto 2240 1823 lineto 2767 1693 3008 1493 3008 1185 curveto 3008 974 2895 758 2729 639 curveto 2573 530 2325 471 2008 471 curveto 1584 471 1294 573 1106 798 curveto 960 970 896 1158 896 1399 curveto 384 1399 lineto 384 1031 455 790 614 571 curveto 888 198 1349 0 1957 0 curveto 2434 0 2824 108 3081 303 curveto 3350 514 3520 866 3520 1207 curveto 3520 1694 3199 2051 2632 2197 curveto 1584 2462 lineto 1079 2592 896 2743 896 3046 curveto 896 3447 1295 3712 1897 3712 curveto 2609 3712 3008 3428 3008 2918 curveto 3520 2918 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj n 29.025000 8.035000 m 29.025000 10.985000 l 32.075000 10.985000 l 32.075000 8.035000 l cp s gsave 29.725000 9.835000 translate 0.035278 -0.035278 scale start_ol 3136 4160 moveto 3136 1341 lineto 3136 801 2735 471 2074 471 curveto 1769 471 1521 543 1323 680 curveto 1120 834 1024 1038 1024 1341 curveto 1024 4160 lineto 512 4160 lineto 512 1328 lineto 512 509 1106 0 2074 0 curveto 3032 0 3648 520 3648 1328 curveto 3648 4160 lineto 3136 4160 lineto end_ol grestore gsave 30.275333 9.835000 translate 0.035278 -0.035278 scale start_ol 2676 1280 moveto 3099 0 lineto 3686 0 lineto 2241 4160 lineto 1564 4160 lineto 96 0 lineto 655 0 lineto 1089 1280 lineto 2676 1280 lineto 2529 1728 moveto 1219 1728 lineto 1897 3597 lineto 2529 1728 lineto end_ol grestore gsave 30.783333 9.835000 translate 0.035278 -0.035278 scale start_ol 3709 2852 moveto 3546 3731 3029 4160 2129 4160 curveto 1578 4160 1133 3991 830 3665 curveto 458 3272 256 2706 256 2064 curveto 256 1410 464 849 852 463 curveto 1167 147 1572 0 2107 0 curveto 3108 0 3670 525 3794 1581 curveto 3252 1581 lineto 3207 1313 3150 1131 3066 975 curveto 2896 653 2546 471 2106 471 curveto 1287 471 768 1093 768 2069 curveto 768 3072 1265 3689 2061 3689 curveto 2394 3689 2704 3592 2874 3442 curveto 3026 3308 3111 3142 3173 2852 curveto 3709 2852 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj n 29.000000 12.020000 m 29.000000 14.970000 l 32.050000 14.970000 l 32.050000 12.020000 l cp s gsave 29.700000 13.820000 translate 0.035278 -0.035278 scale start_ol 3136 4160 moveto 3136 1341 lineto 3136 801 2735 471 2074 471 curveto 1769 471 1521 543 1323 680 curveto 1120 834 1024 1038 1024 1341 curveto 1024 4160 lineto 512 4160 lineto 512 1328 lineto 512 509 1106 0 2074 0 curveto 3032 0 3648 520 3648 1328 curveto 3648 4160 lineto 3136 4160 lineto end_ol grestore gsave 30.250333 13.820000 translate 0.035278 -0.035278 scale start_ol 2676 1280 moveto 3099 0 lineto 3686 0 lineto 2241 4160 lineto 1564 4160 lineto 96 0 lineto 655 0 lineto 1089 1280 lineto 2676 1280 lineto 2529 1728 moveto 1219 1728 lineto 1897 3597 lineto 2529 1728 lineto end_ol grestore gsave 30.758333 13.820000 translate 0.035278 -0.035278 scale start_ol 3520 2918 moveto 3520 3704 2935 4160 1931 4160 curveto 975 4160 384 3716 384 3002 curveto 384 2521 653 2218 1203 2083 curveto 2240 1823 lineto 2767 1693 3008 1493 3008 1185 curveto 3008 974 2895 758 2729 639 curveto 2573 530 2325 471 2008 471 curveto 1584 471 1294 573 1106 798 curveto 960 970 896 1158 896 1399 curveto 384 1399 lineto 384 1031 455 790 614 571 curveto 888 198 1349 0 1957 0 curveto 2434 0 2824 108 3081 303 curveto 3350 514 3520 866 3520 1207 curveto 3520 1694 3199 2051 2632 2197 curveto 1584 2462 lineto 1079 2592 896 2743 896 3046 curveto 896 3447 1295 3712 1897 3712 curveto 2609 3712 3008 3428 3008 2918 curveto 3520 2918 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj n 28.025000 7.035000 m 28.025000 15.985000 l 33.075000 15.985000 l 33.075000 7.035000 l cp s 0.100000 slw [] 0 sd [] 0 sd 0 slj n 29.025000 24.035000 m 29.025000 26.985000 l 32.075000 26.985000 l 32.075000 24.035000 l cp s gsave 29.725000 25.835000 translate 0.035278 -0.035278 scale start_ol 3136 4160 moveto 3136 1341 lineto 3136 801 2735 471 2074 471 curveto 1769 471 1521 543 1323 680 curveto 1120 834 1024 1038 1024 1341 curveto 1024 4160 lineto 512 4160 lineto 512 1328 lineto 512 509 1106 0 2074 0 curveto 3032 0 3648 520 3648 1328 curveto 3648 4160 lineto 3136 4160 lineto end_ol grestore gsave 30.275333 25.835000 translate 0.035278 -0.035278 scale start_ol 2676 1280 moveto 3099 0 lineto 3686 0 lineto 2241 4160 lineto 1564 4160 lineto 96 0 lineto 655 0 lineto 1089 1280 lineto 2676 1280 lineto 2529 1728 moveto 1219 1728 lineto 1897 3597 lineto 2529 1728 lineto end_ol grestore gsave 30.783333 25.835000 translate 0.035278 -0.035278 scale start_ol 3709 2852 moveto 3546 3731 3029 4160 2129 4160 curveto 1578 4160 1133 3991 830 3665 curveto 458 3272 256 2706 256 2064 curveto 256 1410 464 849 852 463 curveto 1167 147 1572 0 2107 0 curveto 3108 0 3670 525 3794 1581 curveto 3252 1581 lineto 3207 1313 3150 1131 3066 975 curveto 2896 653 2546 471 2106 471 curveto 1287 471 768 1093 768 2069 curveto 768 3072 1265 3689 2061 3689 curveto 2394 3689 2704 3592 2874 3442 curveto 3026 3308 3111 3142 3173 2852 curveto 3709 2852 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj n 29.000000 20.070000 m 29.000000 23.020000 l 32.050000 23.020000 l 32.050000 20.070000 l cp s gsave 29.700000 21.870000 translate 0.035278 -0.035278 scale start_ol 3136 4160 moveto 3136 1341 lineto 3136 801 2735 471 2074 471 curveto 1769 471 1521 543 1323 680 curveto 1120 834 1024 1038 1024 1341 curveto 1024 4160 lineto 512 4160 lineto 512 1328 lineto 512 509 1106 0 2074 0 curveto 3032 0 3648 520 3648 1328 curveto 3648 4160 lineto 3136 4160 lineto end_ol grestore gsave 30.250333 21.870000 translate 0.035278 -0.035278 scale start_ol 2676 1280 moveto 3099 0 lineto 3686 0 lineto 2241 4160 lineto 1564 4160 lineto 96 0 lineto 655 0 lineto 1089 1280 lineto 2676 1280 lineto 2529 1728 moveto 1219 1728 lineto 1897 3597 lineto 2529 1728 lineto end_ol grestore gsave 30.758333 21.870000 translate 0.035278 -0.035278 scale start_ol 3520 2918 moveto 3520 3704 2935 4160 1931 4160 curveto 975 4160 384 3716 384 3002 curveto 384 2521 653 2218 1203 2083 curveto 2240 1823 lineto 2767 1693 3008 1493 3008 1185 curveto 3008 974 2895 758 2729 639 curveto 2573 530 2325 471 2008 471 curveto 1584 471 1294 573 1106 798 curveto 960 970 896 1158 896 1399 curveto 384 1399 lineto 384 1031 455 790 614 571 curveto 888 198 1349 0 1957 0 curveto 2434 0 2824 108 3081 303 curveto 3350 514 3520 866 3520 1207 curveto 3520 1694 3199 2051 2632 2197 curveto 1584 2462 lineto 1079 2592 896 2743 896 3046 curveto 896 3447 1295 3712 1897 3712 curveto 2609 3712 3008 3428 3008 2918 curveto 3520 2918 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj n 28.025000 18.985000 m 28.025000 27.935000 l 33.075000 27.935000 l 33.075000 18.985000 l cp s 0.100000 slw [] 0 sd [] 0 sd 0 slj n 17.950000 14.035000 m 17.950000 16.985000 l 21.000000 16.985000 l 21.000000 14.035000 l cp s gsave 18.650000 15.835000 translate 0.035278 -0.035278 scale start_ol 3136 4160 moveto 3136 1341 lineto 3136 801 2735 471 2074 471 curveto 1769 471 1521 543 1323 680 curveto 1120 834 1024 1038 1024 1341 curveto 1024 4160 lineto 512 4160 lineto 512 1328 lineto 512 509 1106 0 2074 0 curveto 3032 0 3648 520 3648 1328 curveto 3648 4160 lineto 3136 4160 lineto end_ol grestore gsave 19.200333 15.835000 translate 0.035278 -0.035278 scale start_ol 2676 1280 moveto 3099 0 lineto 3686 0 lineto 2241 4160 lineto 1564 4160 lineto 96 0 lineto 655 0 lineto 1089 1280 lineto 2676 1280 lineto 2529 1728 moveto 1219 1728 lineto 1897 3597 lineto 2529 1728 lineto end_ol grestore gsave 19.708333 15.835000 translate 0.035278 -0.035278 scale start_ol 3709 2852 moveto 3546 3731 3029 4160 2129 4160 curveto 1578 4160 1133 3991 830 3665 curveto 458 3272 256 2706 256 2064 curveto 256 1410 464 849 852 463 curveto 1167 147 1572 0 2107 0 curveto 3108 0 3670 525 3794 1581 curveto 3252 1581 lineto 3207 1313 3150 1131 3066 975 curveto 2896 653 2546 471 2106 471 curveto 1287 471 768 1093 768 2069 curveto 768 3072 1265 3689 2061 3689 curveto 2394 3689 2704 3592 2874 3442 curveto 3026 3308 3111 3142 3173 2852 curveto 3709 2852 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj n 13.925000 16.070000 m 13.925000 19.020000 l 16.975000 19.020000 l 16.975000 16.070000 l cp s gsave 14.625000 17.870000 translate 0.035278 -0.035278 scale start_ol 3136 4160 moveto 3136 1341 lineto 3136 801 2735 471 2074 471 curveto 1769 471 1521 543 1323 680 curveto 1120 834 1024 1038 1024 1341 curveto 1024 4160 lineto 512 4160 lineto 512 1328 lineto 512 509 1106 0 2074 0 curveto 3032 0 3648 520 3648 1328 curveto 3648 4160 lineto 3136 4160 lineto end_ol grestore gsave 15.175333 17.870000 translate 0.035278 -0.035278 scale start_ol 2676 1280 moveto 3099 0 lineto 3686 0 lineto 2241 4160 lineto 1564 4160 lineto 96 0 lineto 655 0 lineto 1089 1280 lineto 2676 1280 lineto 2529 1728 moveto 1219 1728 lineto 1897 3597 lineto 2529 1728 lineto end_ol grestore gsave 15.683333 17.870000 translate 0.035278 -0.035278 scale start_ol 3520 2918 moveto 3520 3704 2935 4160 1931 4160 curveto 975 4160 384 3716 384 3002 curveto 384 2521 653 2218 1203 2083 curveto 2240 1823 lineto 2767 1693 3008 1493 3008 1185 curveto 3008 974 2895 758 2729 639 curveto 2573 530 2325 471 2008 471 curveto 1584 471 1294 573 1106 798 curveto 960 970 896 1158 896 1399 curveto 384 1399 lineto 384 1031 455 790 614 571 curveto 888 198 1349 0 1957 0 curveto 2434 0 2824 108 3081 303 curveto 3350 514 3520 866 3520 1207 curveto 3520 1694 3199 2051 2632 2197 curveto 1584 2462 lineto 1079 2592 896 2743 896 3046 curveto 896 3447 1295 3712 1897 3712 curveto 2609 3712 3008 3428 3008 2918 curveto 3520 2918 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj n 17.950000 18.035000 m 17.950000 20.985000 l 21.000000 20.985000 l 21.000000 18.035000 l cp s gsave 18.650000 19.835000 translate 0.035278 -0.035278 scale start_ol 3136 4160 moveto 3136 1341 lineto 3136 801 2735 471 2074 471 curveto 1769 471 1521 543 1323 680 curveto 1120 834 1024 1038 1024 1341 curveto 1024 4160 lineto 512 4160 lineto 512 1328 lineto 512 509 1106 0 2074 0 curveto 3032 0 3648 520 3648 1328 curveto 3648 4160 lineto 3136 4160 lineto end_ol grestore gsave 19.200333 19.835000 translate 0.035278 -0.035278 scale start_ol 2676 1280 moveto 3099 0 lineto 3686 0 lineto 2241 4160 lineto 1564 4160 lineto 96 0 lineto 655 0 lineto 1089 1280 lineto 2676 1280 lineto 2529 1728 moveto 1219 1728 lineto 1897 3597 lineto 2529 1728 lineto end_ol grestore gsave 19.708333 19.835000 translate 0.035278 -0.035278 scale start_ol 3709 2852 moveto 3546 3731 3029 4160 2129 4160 curveto 1578 4160 1133 3991 830 3665 curveto 458 3272 256 2706 256 2064 curveto 256 1410 464 849 852 463 curveto 1167 147 1572 0 2107 0 curveto 3108 0 3670 525 3794 1581 curveto 3252 1581 lineto 3207 1313 3150 1131 3066 975 curveto 2896 653 2546 471 2106 471 curveto 1287 471 768 1093 768 2069 curveto 768 3072 1265 3689 2061 3689 curveto 2394 3689 2704 3592 2874 3442 curveto 3026 3308 3111 3142 3173 2852 curveto 3709 2852 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj n 13.050000 13.050000 m 13.050000 22.000000 l 22.000000 22.000000 l 22.000000 13.050000 l cp s gsave 13.950000 12.200000 translate 0.035278 -0.035278 scale start_ol 3520 2918 moveto 3520 3704 2935 4160 1931 4160 curveto 975 4160 384 3716 384 3002 curveto 384 2521 653 2218 1203 2083 curveto 2240 1823 lineto 2767 1693 3008 1493 3008 1185 curveto 3008 974 2895 758 2729 639 curveto 2573 530 2325 471 2008 471 curveto 1584 471 1294 573 1106 798 curveto 960 970 896 1158 896 1399 curveto 384 1399 lineto 384 1031 455 790 614 571 curveto 888 198 1349 0 1957 0 curveto 2434 0 2824 108 3081 303 curveto 3350 514 3520 866 3520 1207 curveto 3520 1694 3199 2051 2632 2197 curveto 1584 2462 lineto 1079 2592 896 2743 896 3046 curveto 896 3447 1295 3712 1897 3712 curveto 2609 3712 3008 3428 3008 2918 curveto 3520 2918 lineto end_ol grestore gsave 14.458000 12.200000 translate 0.035278 -0.035278 scale start_ol 1509 3008 moveto 1024 3008 lineto 1024 3835 lineto 512 3835 lineto 512 3008 lineto 111 3008 lineto 111 2618 lineto 512 2618 lineto 512 468 lineto 512 169 724 0 1108 0 curveto 1226 0 1344 11 1509 40 curveto 1509 435 lineto 1447 419 1374 419 1283 419 curveto 1080 419 1024 473 1024 674 curveto 1024 2618 lineto 1509 2618 lineto 1509 3008 lineto end_ol grestore gsave 14.669667 12.200000 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 15.093000 12.200000 translate 0.035278 -0.035278 scale start_ol 1509 3008 moveto 1024 3008 lineto 1024 3835 lineto 512 3835 lineto 512 3008 lineto 111 3008 lineto 111 2618 lineto 512 2618 lineto 512 468 lineto 512 169 724 0 1108 0 curveto 1226 0 1344 11 1509 40 curveto 1509 435 lineto 1447 419 1374 419 1283 419 curveto 1080 419 1024 473 1024 674 curveto 1024 2618 lineto 1509 2618 lineto 1509 3008 lineto end_ol grestore gsave 15.304667 12.200000 translate 0.035278 -0.035278 scale start_ol 2896 1344 moveto 2896 1780 2862 2042 2777 2255 curveto 2585 2724 2134 3008 1581 3008 curveto 756 3008 226 2399 226 1463 curveto 226 558 739 0 1569 0 curveto 2247 0 2715 362 2834 968 curveto 2360 968 lineto 2230 622 1964 442 1586 442 curveto 1287 442 1033 562 875 783 curveto 762 933 723 1083 717 1344 curveto 2896 1344 lineto 728 1728 moveto 768 2236 1101 2566 1575 2566 curveto 2038 2566 2393 2210 2393 1759 curveto 2393 1749 2393 1739 2388 1728 curveto 728 1728 lineto end_ol grestore gsave 15.728000 12.200000 translate 0.035278 -0.035278 scale start_ol 1515 3008 moveto 1024 3008 lineto 1024 3453 lineto 1024 3643 1137 3741 1352 3741 curveto 1391 3741 1408 3741 1515 3741 curveto 1515 4131 lineto 1403 4154 1338 4160 1238 4160 curveto 783 4160 512 3914 512 3495 curveto 512 3008 lineto 117 3008 lineto 117 2618 lineto 512 2618 lineto 512 0 lineto 1024 0 lineto 1024 2618 lineto 1515 2618 lineto 1515 3008 lineto end_ol grestore gsave 15.939667 12.200000 translate 0.035278 -0.035278 scale start_ol 2752 0 moveto 2752 3008 lineto 2240 3008 lineto 2240 1386 lineto 2240 801 1940 419 1474 419 curveto 1121 419 896 638 896 982 curveto 896 3008 lineto 384 3008 lineto 384 786 lineto 384 308 737 0 1290 0 curveto 1708 0 1974 153 2240 545 curveto 2240 0 lineto 2752 0 lineto end_ol grestore gsave 16.363000 12.200000 translate 0.035278 -0.035278 scale start_ol 896 4160 moveto 384 4160 lineto 384 0 lineto 896 0 lineto 896 4160 lineto end_ol grestore gsave 16.532333 12.200000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 16.744000 12.200000 translate 0.035278 -0.035278 scale start_ol 1024 1920 moveto 2988 1920 lineto 2988 2368 lineto 1024 2368 lineto 1024 3689 lineto 3259 3689 lineto 3259 4160 lineto 512 4160 lineto 512 0 lineto 1024 0 lineto 1024 1920 lineto end_ol grestore gsave 17.209667 12.200000 translate 0.035278 -0.035278 scale start_ol 1530 3008 moveto 697 3008 192 2446 192 1504 curveto 192 562 691 0 1536 0 curveto 2370 0 2880 562 2880 1483 curveto 2880 2451 2387 3008 1530 3008 curveto 1536 2566 moveto 2057 2566 2368 2165 2368 1488 curveto 2368 848 2046 442 1536 442 curveto 1020 442 704 843 704 1504 curveto 704 2160 1020 2566 1536 2566 curveto end_ol grestore gsave 17.633000 12.200000 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1561 lineto 896 1992 1003 2273 1229 2440 curveto 1376 2549 1517 2583 1844 2589 curveto 1844 3008 lineto 1763 3008 1723 3008 1659 3008 curveto 1347 3008 1110 2849 832 2463 curveto 832 3008 lineto 384 3008 lineto end_ol grestore gsave 17.887000 12.200000 translate 0.035278 -0.035278 scale start_ol 832 4160 moveto 320 4160 lineto 320 0 lineto 832 0 lineto 832 1164 lineto 1289 1621 lineto 2288 0 lineto 2870 0 lineto 1662 1957 lineto 2689 2990 lineto 2085 2990 lineto 832 1723 lineto 832 4160 lineto end_ol grestore gsave 18.268000 12.200000 translate 0.035278 -0.035278 scale start_ol 896 2987 moveto 384 2987 lineto 384 0 lineto 896 0 lineto 896 2987 lineto 896 4160 moveto 384 4160 lineto 384 3557 lineto 896 3557 lineto 896 4160 lineto end_ol grestore gsave 18.437333 12.200000 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1606 lineto 896 2200 1211 2589 1696 2589 curveto 2067 2589 2304 2367 2304 2017 curveto 2304 0 lineto 2816 0 lineto 2816 2210 lineto 2816 2695 2437 3008 1847 3008 curveto 1392 3008 1100 2837 832 2422 curveto 832 3008 lineto 384 3008 lineto end_ol grestore gsave 18.860667 12.200000 translate 0.035278 -0.035278 scale start_ol 2304 3008 moveto 2304 2493 lineto 2072 2844 1791 3008 1422 3008 curveto 688 3008 192 2387 192 1477 curveto 192 1017 322 642 564 375 curveto 783 139 1099 0 1409 0 curveto 1780 0 2039 158 2304 531 curveto 2304 355 lineto 2304 -108 2254 -388 2137 -577 curveto 2015 -779 1776 -896 1493 -896 curveto 1282 -896 1093 -824 965 -695 curveto 859 -588 815 -487 787 -265 curveto 292 -265 lineto 342 -901 770 -1280 1453 -1280 curveto 1886 -1280 2258 -1136 2447 -893 curveto 2669 -615 2752 -234 2752 477 curveto 2752 3008 lineto 2304 3008 lineto 1510 2566 moveto 2010 2566 2304 2170 2304 1488 curveto 2304 838 2004 442 1515 442 curveto 1010 442 704 843 704 1504 curveto 704 2160 1015 2566 1510 2566 curveto end_ol grestore gsave 19.284000 12.200000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.495667 12.200000 translate 0.035278 -0.035278 scale start_ol 1024 1792 moveto 2333 1792 lineto 2665 1792 2927 1888 3153 2085 curveto 3409 2311 3520 2576 3520 2953 curveto 3520 3726 3057 4160 2233 4160 curveto 512 4160 lineto 512 0 lineto 1024 0 lineto 1024 1792 lineto 1024 2240 moveto 1024 3689 lineto 2133 3689 lineto 2641 3689 2944 3417 2944 2965 curveto 2944 2512 2641 2240 2133 2240 curveto 1024 2240 lineto end_ol grestore gsave 20.003667 12.200000 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1561 lineto 896 1992 1003 2273 1229 2440 curveto 1376 2549 1517 2583 1844 2589 curveto 1844 3008 lineto 1763 3008 1723 3008 1659 3008 curveto 1347 3008 1110 2849 832 2463 curveto 832 3008 lineto 384 3008 lineto end_ol grestore gsave 20.257667 12.200000 translate 0.035278 -0.035278 scale start_ol 1530 3008 moveto 697 3008 192 2446 192 1504 curveto 192 562 691 0 1536 0 curveto 2370 0 2880 562 2880 1483 curveto 2880 2451 2387 3008 1530 3008 curveto 1536 2566 moveto 2057 2566 2368 2165 2368 1488 curveto 2368 848 2046 442 1536 442 curveto 1020 442 704 843 704 1504 curveto 704 2160 1020 2566 1536 2566 curveto end_ol grestore gsave 20.681000 12.200000 translate 0.035278 -0.035278 scale start_ol 1648 1556 moveto 2642 3008 lineto 2111 3008 lineto 1400 1917 lineto 689 3008 lineto 152 3008 lineto 1140 1533 lineto 96 0 lineto 632 0 lineto 1383 1154 lineto 2122 0 lineto 2670 0 lineto 1648 1556 lineto end_ol grestore gsave 21.062000 12.200000 translate 0.035278 -0.035278 scale start_ol 2190 3008 moveto 1372 634 lineto 615 3008 lineto 113 3008 lineto 1112 -52 lineto 931 -535 lineto 858 -751 751 -832 553 -832 curveto 485 -832 406 -820 305 -797 curveto 305 -1209 lineto 401 -1258 497 -1280 621 -1280 curveto 773 -1280 937 -1228 1061 -1136 curveto 1208 -1026 1293 -899 1383 -656 curveto 2698 3008 lineto 2190 3008 lineto end_ol grestore gsave 3.600000 12.300000 translate 0.035278 -0.035278 scale start_ol 3709 2852 moveto 3546 3731 3029 4160 2129 4160 curveto 1578 4160 1133 3991 830 3665 curveto 458 3272 256 2706 256 2064 curveto 256 1410 464 849 852 463 curveto 1167 147 1572 0 2107 0 curveto 3108 0 3670 525 3794 1581 curveto 3252 1581 lineto 3207 1313 3150 1131 3066 975 curveto 2896 653 2546 471 2106 471 curveto 1287 471 768 1093 768 2069 curveto 768 3072 1265 3689 2061 3689 curveto 2394 3689 2704 3592 2874 3442 curveto 3026 3308 3111 3142 3173 2852 curveto 3709 2852 lineto end_ol grestore gsave 4.150333 12.300000 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 4.573667 12.300000 translate 0.035278 -0.035278 scale start_ol 896 4160 moveto 384 4160 lineto 384 0 lineto 896 0 lineto 896 4160 lineto end_ol grestore gsave 4.743000 12.300000 translate 0.035278 -0.035278 scale start_ol 896 4160 moveto 384 4160 lineto 384 0 lineto 896 0 lineto 896 4160 lineto end_ol grestore gsave 4.912333 12.300000 translate 0.035278 -0.035278 scale start_ol 2896 1344 moveto 2896 1780 2862 2042 2777 2255 curveto 2585 2724 2134 3008 1581 3008 curveto 756 3008 226 2399 226 1463 curveto 226 558 739 0 1569 0 curveto 2247 0 2715 362 2834 968 curveto 2360 968 lineto 2230 622 1964 442 1586 442 curveto 1287 442 1033 562 875 783 curveto 762 933 723 1083 717 1344 curveto 2896 1344 lineto 728 1728 moveto 768 2236 1101 2566 1575 2566 curveto 2038 2566 2393 2210 2393 1759 curveto 2393 1749 2393 1739 2388 1728 curveto 728 1728 lineto end_ol grestore gsave 5.335667 12.300000 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1561 lineto 896 1992 1003 2273 1229 2440 curveto 1376 2549 1517 2583 1844 2589 curveto 1844 3008 lineto 1763 3008 1723 3008 1659 3008 curveto 1347 3008 1110 2849 832 2463 curveto 832 3008 lineto 384 3008 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj n 1.975000 12.985000 m 1.975000 21.935000 l 7.025000 21.935000 l 7.025000 12.985000 l cp s gsave 29.250000 6.450000 translate 0.035278 -0.035278 scale start_ol 3709 2852 moveto 3546 3731 3029 4160 2129 4160 curveto 1578 4160 1133 3991 830 3665 curveto 458 3272 256 2706 256 2064 curveto 256 1410 464 849 852 463 curveto 1167 147 1572 0 2107 0 curveto 3108 0 3670 525 3794 1581 curveto 3252 1581 lineto 3207 1313 3150 1131 3066 975 curveto 2896 653 2546 471 2106 471 curveto 1287 471 768 1093 768 2069 curveto 768 3072 1265 3689 2061 3689 curveto 2394 3689 2704 3592 2874 3442 curveto 3026 3308 3111 3142 3173 2852 curveto 3709 2852 lineto end_ol grestore gsave 29.800333 6.450000 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 30.223667 6.450000 translate 0.035278 -0.035278 scale start_ol 896 4160 moveto 384 4160 lineto 384 0 lineto 896 0 lineto 896 4160 lineto end_ol grestore gsave 30.393000 6.450000 translate 0.035278 -0.035278 scale start_ol 896 4160 moveto 384 4160 lineto 384 0 lineto 896 0 lineto 896 4160 lineto end_ol grestore gsave 30.562333 6.450000 translate 0.035278 -0.035278 scale start_ol 2896 1344 moveto 2896 1780 2862 2042 2777 2255 curveto 2585 2724 2134 3008 1581 3008 curveto 756 3008 226 2399 226 1463 curveto 226 558 739 0 1569 0 curveto 2247 0 2715 362 2834 968 curveto 2360 968 lineto 2230 622 1964 442 1586 442 curveto 1287 442 1033 562 875 783 curveto 762 933 723 1083 717 1344 curveto 2896 1344 lineto 728 1728 moveto 768 2236 1101 2566 1575 2566 curveto 2038 2566 2393 2210 2393 1759 curveto 2393 1749 2393 1739 2388 1728 curveto 728 1728 lineto end_ol grestore gsave 30.985667 6.450000 translate 0.035278 -0.035278 scale start_ol 2896 1344 moveto 2896 1780 2862 2042 2777 2255 curveto 2585 2724 2134 3008 1581 3008 curveto 756 3008 226 2399 226 1463 curveto 226 558 739 0 1569 0 curveto 2247 0 2715 362 2834 968 curveto 2360 968 lineto 2230 622 1964 442 1586 442 curveto 1287 442 1033 562 875 783 curveto 762 933 723 1083 717 1344 curveto 2896 1344 lineto 728 1728 moveto 768 2236 1101 2566 1575 2566 curveto 2038 2566 2393 2210 2393 1759 curveto 2393 1749 2393 1739 2388 1728 curveto 728 1728 lineto end_ol grestore gsave 31.409000 6.450000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 31.620667 6.450000 translate 0.035278 -0.035278 scale start_ol 2676 1280 moveto 3099 0 lineto 3686 0 lineto 2241 4160 lineto 1564 4160 lineto 96 0 lineto 655 0 lineto 1089 1280 lineto 2676 1280 lineto 2529 1728 moveto 1219 1728 lineto 1897 3597 lineto 2529 1728 lineto end_ol grestore gsave 29.400000 18.500000 translate 0.035278 -0.035278 scale start_ol 3709 2852 moveto 3546 3731 3029 4160 2129 4160 curveto 1578 4160 1133 3991 830 3665 curveto 458 3272 256 2706 256 2064 curveto 256 1410 464 849 852 463 curveto 1167 147 1572 0 2107 0 curveto 3108 0 3670 525 3794 1581 curveto 3252 1581 lineto 3207 1313 3150 1131 3066 975 curveto 2896 653 2546 471 2106 471 curveto 1287 471 768 1093 768 2069 curveto 768 3072 1265 3689 2061 3689 curveto 2394 3689 2704 3592 2874 3442 curveto 3026 3308 3111 3142 3173 2852 curveto 3709 2852 lineto end_ol grestore gsave 29.950333 18.500000 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 30.373667 18.500000 translate 0.035278 -0.035278 scale start_ol 896 4160 moveto 384 4160 lineto 384 0 lineto 896 0 lineto 896 4160 lineto end_ol grestore gsave 30.543000 18.500000 translate 0.035278 -0.035278 scale start_ol 896 4160 moveto 384 4160 lineto 384 0 lineto 896 0 lineto 896 4160 lineto end_ol grestore gsave 30.712333 18.500000 translate 0.035278 -0.035278 scale start_ol 2896 1344 moveto 2896 1780 2862 2042 2777 2255 curveto 2585 2724 2134 3008 1581 3008 curveto 756 3008 226 2399 226 1463 curveto 226 558 739 0 1569 0 curveto 2247 0 2715 362 2834 968 curveto 2360 968 lineto 2230 622 1964 442 1586 442 curveto 1287 442 1033 562 875 783 curveto 762 933 723 1083 717 1344 curveto 2896 1344 lineto 728 1728 moveto 768 2236 1101 2566 1575 2566 curveto 2038 2566 2393 2210 2393 1759 curveto 2393 1749 2393 1739 2388 1728 curveto 728 1728 lineto end_ol grestore gsave 31.135667 18.500000 translate 0.035278 -0.035278 scale start_ol 2896 1344 moveto 2896 1780 2862 2042 2777 2255 curveto 2585 2724 2134 3008 1581 3008 curveto 756 3008 226 2399 226 1463 curveto 226 558 739 0 1569 0 curveto 2247 0 2715 362 2834 968 curveto 2360 968 lineto 2230 622 1964 442 1586 442 curveto 1287 442 1033 562 875 783 curveto 762 933 723 1083 717 1344 curveto 2896 1344 lineto 728 1728 moveto 768 2236 1101 2566 1575 2566 curveto 2038 2566 2393 2210 2393 1759 curveto 2393 1749 2393 1739 2388 1728 curveto 728 1728 lineto end_ol grestore gsave 31.559000 18.500000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 31.770667 18.500000 translate 0.035278 -0.035278 scale start_ol 448 0 moveto 2306 0 lineto 2696 0 2984 109 3204 349 curveto 3407 567 3520 864 3520 1191 curveto 3520 1695 3294 1998 2770 2204 curveto 3135 2380 3328 2687 3328 3108 curveto 3328 3409 3216 3677 3002 3870 curveto 2788 4069 2507 4160 2113 4160 curveto 448 4160 lineto 448 0 lineto 960 2368 moveto 960 3689 lineto 1985 3689 lineto 2281 3689 2446 3649 2588 3541 curveto 2736 3427 2816 3256 2816 3029 curveto 2816 2806 2736 2630 2588 2516 curveto 2446 2408 2281 2368 1985 2368 curveto 960 2368 lineto 960 471 moveto 960 1920 lineto 2259 1920 lineto 2516 1920 2688 1856 2813 1718 curveto 2939 1585 3008 1400 3008 1193 curveto 3008 991 2939 806 2813 673 curveto 2688 535 2516 471 2259 471 curveto 960 471 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 6.100000 15.475000 m 13.043518 17.311816 l s 0 slj 1.000000 1.000000 1.000000 srgb n 12.941222 17.698514 m 13.816915 17.516407 l 13.145814 16.925117 l f 0.100000 slw [] 0 sd 0 slj 0.000000 0.000000 0.000000 srgb n 12.941222 17.698514 m 13.816915 17.516407 l 13.145814 16.925117 l cp s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 21.000000 15.510000 m 28.115812 13.717705 l s 0 slj 1.000000 1.000000 1.000000 srgb n 28.213511 14.105590 m 28.891583 13.522308 l 28.018114 13.329820 l f 0.100000 slw [] 0 sd 0 slj 0.000000 0.000000 0.000000 srgb n 28.213511 14.105590 m 28.891583 13.522308 l 28.018114 13.329820 l cp s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 21.000000 19.510000 m 28.116338 21.320218 l s 0 slj 1.000000 1.000000 1.000000 srgb n 28.017728 21.707873 m 28.891647 21.517438 l 28.214948 20.932564 l f 0.100000 slw [] 0 sd 0 slj 0.000000 0.000000 0.000000 srgb n 28.017728 21.707873 m 28.891647 21.517438 l 28.214948 20.932564 l cp s gsave 8.850000 15.800000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 9.019333 15.800000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 9.459600 15.800000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 2972 3392 lineto 2516 3392 lineto 1585 521 lineto 599 3392 lineto 138 3392 lineto 1346 0 lineto 1806 0 lineto end_ol grestore gsave 9.866000 15.800000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 10.035333 15.800000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 10.407867 15.800000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 23.700000 14.000000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 23.869333 14.000000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 24.309600 14.000000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 2972 3392 lineto 2516 3392 lineto 1585 521 lineto 599 3392 lineto 138 3392 lineto 1346 0 lineto 1806 0 lineto end_ol grestore gsave 24.716000 14.000000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 24.885333 14.000000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 25.257867 14.000000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 23.900000 19.800000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 24.069333 19.800000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 24.509600 19.800000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 2972 3392 lineto 2516 3392 lineto 1585 521 lineto 599 3392 lineto 138 3392 lineto 1346 0 lineto 1806 0 lineto end_ol grestore gsave 24.916000 19.800000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 25.085333 19.800000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 25.457867 19.800000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 27.756800 -16.310740 41.839965 41.839965 88.263057 119.972682 ellipse s 0 slj 1.000000 1.000000 1.000000 srgb n 7.061906 19.591920 m 6.170531 19.518084 l 6.646288 20.275485 l f 0.100000 slw [] 0 sd 0 slj 0.000000 0.000000 0.000000 srgb n 7.061906 19.591920 m 6.170531 19.518084 l 6.646288 20.275485 l cp s gsave 16.550000 25.222500 translate 0.035278 -0.035278 scale start_ol 384 0 moveto 1855 0 lineto 2163 0 2391 88 2566 282 curveto 2727 458 2816 698 2816 962 curveto 2816 1369 2632 1614 2204 1780 curveto 2478 1925 2624 2178 2624 2525 curveto 2624 2773 2537 2994 2370 3153 curveto 2204 3317 1985 3392 1679 3392 curveto 384 3392 lineto 384 0 lineto 768 1920 moveto 768 3011 lineto 1581 3011 lineto 1816 3011 1947 2978 2059 2889 curveto 2177 2795 2240 2654 2240 2466 curveto 2240 2282 2177 2136 2059 2042 curveto 1947 1953 1816 1920 1581 1920 curveto 768 1920 lineto 768 381 moveto 768 1536 lineto 1823 1536 lineto 2032 1536 2172 1485 2274 1375 curveto 2376 1269 2432 1122 2432 956 curveto 2432 795 2376 648 2274 542 curveto 2172 432 2032 381 1823 381 curveto 768 381 lineto end_ol grestore gsave 16.956400 25.222500 translate 0.035278 -0.035278 scale start_ol 1728 1331 moveto 2991 3392 lineto 2479 3392 lineto 1542 1740 lineto 579 3392 lineto 49 3392 lineto 1344 1331 lineto 1344 0 lineto 1728 0 lineto 1728 1331 lineto end_ol grestore gsave 17.362800 25.222500 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore showpage kamailio-4.0.4/doc/sip/figures/transaction.png0000644000000000000000000002714312223032460020043 0ustar rootroot‰PNG  IHDR1gAÐéÕsBITÛáOà pHYsÐй‹çŸ IDATxœíÝy\gþð'@..ƒr.Ä ‘ZÔÑjWwU¶ÖµuÛZ±ÕêÏJ­õ%«­«[i»…âM‹G»k•²º¶*h(AÀŠÐB0$H2¿?w6›„c&3I¾ï¿&3Ï|‡‡3™Lf8A k81]à˜“I‡£5X†Æ<Êdaaa\\ŸÏŠŠÊÍÍe¶&  †ÆÑ8!„ÊËË—.]úÞ{ïI¥Ò‚‚‚’’¦«ÀÐ8"‚ æÎ{èÐ!BGCCüyóÜÝÝù|þïÿûÖÖVÜŽ—ÒœP*•o¿ý¶¯¯¯P(|þù绺ºÈ>øàƒgggÝþÁ€èšþÚãœB—/_ž5k–n\çÌ™³jÕª¶¶¶¶¶¶#F¬Y³¦¿`oß¾½¢¢¢ªªªµµ•Ïç§§§“?ºzõjUU•R©¤ø‰c oh `AÎÎÎJ¥Òpve2™¯¯/žF:ÿŒÅbñµk×ðtKKË!CÈîÝ»Gù?ÇAßÐôׇ‚ðöönkkÓýYYYÙï~÷;777œ^‡óhwqqqvvvvvvrrÒ;'0}CÓ_;`œBhÒ¤Iº»Ð ¬X±¢¹¹Y­Vwvvý_]ÐÔÔ¤T*U*Ajµšš¸Ã£oh`ÈXË !”žž¾~ýúsçÎõööÞºu륗^Â?ëîîæñxB¡°©©éÕW_5ÐËk¯½öÊ+¯Ô××+•Êëׯ/\¸Ðµ;ú††Œ½ðîòìÙ³cÆŒáñx‘‘‘䉾üüüˆˆ—°°°O>ùé‘*•jûöíb±˜ËåŽ;öË/¿Ôš˜¦¡é¯0ŽCÀõ®° \ï »@&`È$와]\t›’““Íî.’¶KhÉ’%/¾ø"uµêéÉdii©ÕË&°d€’’’¨+ÐBO& ìëðhØ2Ëðïßð…††ÒQ iŸOâo¯ÃGšlcdëà와] “° dvLÀ.IØ2 »@&`È$와] “° dvLÀ.IØ2 »@&`È$와] “°‹fßOLš4IëG999Ï>û¬îRÏ>ûlNN^–£Þvú·8;̤&77·'Nh¶,^¼øÛo¿mjjÒlljjúöÛo/^Œ_’O8ÒšF: ³Æ6cç™Üµk׆ úúúÈ@°|ùò={öhΖ™™ùÚk¯ «€6;¼—$~ 89ñòË/;öõ×_'[~ûí·#FÔÖÖŠD"„PgggTTÔµk×|||ÈeµºÒû’lbŒ€v¾ŸDmݺõoûÛÇÉooï dggã—{÷î}ê©§|||Œé ÞOºÙÿ~!´eË…Bñ—¿ü…lihh˜:uê­[·8ΰaÃΟ?‰Ú1Â~XCdR.—;¶¤¤$88˜,~Á‚üãy<Þ—_~Iž‚LÆéy^ˆýquuݰaÃÆ5×®]ûÊ+¯ðùüÌÌL¦ c‰9sæŒ?þÏþspp0ÓµÇØO"„Ôjõ„ ***4‹Ÿ2eŠR©¼|ù²î²ý½ÔZ ¦ŽÑµk×F…âñxkÖ¬Ù°aƒ»»;õØa&œ©cDDIIɾ}ûŽ;FDttô·ß~kä/@û?ïjë222ºººèëŸÃáL›6íÈ‘#õõõ1117nܘ1cFGG}k†Á~’í8ŽH$ZµjÕ›o¾‰?Pp~dîµ¶¶&%%ÕÖÖN˜0áüùóft,™d;òM¬‘É´pŒš››“’’^y啜œó:–€L²Ö‰¥“iùÕÕÕEGGs¹Ü†††ÀÀ@³ûæ÷“6F"‘lÞ¼9,,,##C"‘бŠaÆ͟?_¡P|ðÁtô ƒý$Û¸‚Oï>“’1ªªªŠ÷ðð¸}û¶———%]SÁ~҆ѷÏ7nÜôéÓ¥Ré™3g(ìÜëx’““)¯˜ '333ï3©êö‰'ž(**ª¨¨X´hU}c˜“ÉÒÒRªË–’H$»wïvq¡ìbÉñãÇ#„¾ûî;ª:F2çýdII mõmÆ•h¾±¤ê=GG‡···@ xøð!…Q‚sŸ´7”Œ‘J¥ …*•J.—Ã÷B¬Ì&÷“œÿÐj'bݺu^^^ƒ^¿~=ùwÙ_»n‡š¬ªÍa¡»wïöõõùûûC ­Ï&3I„Þý@NNÎÅ‹kjj®]»VZZºÿ~ÃíZâ>5§ ×@Ŧ°¸2‰0…‹ÐG·’ÄÄÄ¢¢"<]TT4iÒ$ÃíúD}ðÁ!!!!Ÿû÷ïãöÞÞ^__ß¶¶6Í9³²²Äb1—Ë‹‹ûñÇq»\._¼x±««ë!CvîÜiµß%ctèÐ!„Ð /¼@IIÀ$6¹ŸìÏõë×ñÝ»Bñññׯ_7ÜnØÕ«W«ªª‚xþùç³³³qcqqñ„ |}}5ç,**ºxñ¢D"IIIyùå—qãÆ;::ššš~úé§o¾ùÆò­³¦††„PXXÓ…8$“lÆ"ôÑ­ÄÉÉI¥Rái•Jåììl¸Ý@Ÿ¡{÷îá醆†ÞÞ^‚ –-[vüøq­9º•dee%&&Þ»wïîÝ»?þøÞ½{ ·èS·ó¤¤¤‰'–––ž“|¹fÍš9sæÜ¿¿­­mΜ9Vû½Y>Fr¹œÃáðx<¥RIUUÀx6™Éþþ­¨Õêµk׊D"‘H”žž®V« ·ëíYk‚túôi±XL.;`&e2YjjªP(ôóóÛ±c—Ë5ƒMaùÕÔÔ „† FUIÀ$ôfR*•fdd˜XKýýïçwÌ[öçŸ £¶žþXžIülæ'Ÿ|’ª’€Ièz?‰’‘‘AÓ*¬©³³ó£>Z±b…IK­\¹²½½½¥¥å­·ÞJII¡©6ÊÁ fQ?_‰D’™™¹gωDByçŒàp8ÎÎÎ}ôQ`` I FEEÅÄÄÈåò¹sçnÙ²…¦ò('x˜Ee&í/aîet+V¬0u×ʰŸd5™´×4:¦»wï"„†Êt!ÊÒLBíO{{;‚Ûº2ÇüLBíÎäàÁƒ™.ÄA™“ISÓ˜œœlÆZ#”Jegg§³³ó Aƒ˜®ÅA™sŸƒ¾ñÆðø^63û¼Ôýû÷ýüüð×Ó¨- ÉÌ{˜´«,))1¿@`"|Tbv&oÞ¼5lذ›7oRZ0–E÷ã12™fÿ}3Xx?œÉáÇ×ÖÖRZ0–E×ñˆD¢M›6566fddˆD"ªj rvvF©T*¦ q\\[É´'øQíä·@õQv½+$Ó>À~’q_ƒÉ´u\.!¤P(˜.ÄqÑxÏeò PGG‡™ÕÓYxŽG¥R¹»»+Їº»»SZ0 í÷AW*•ø- °Ëïƒ>zôèêêêÊÊÊqãÆQW0í÷ã@Úœ¨¨(„Ð7˜.ÄAÙÕ=²%p&áóI¦@&¶±cÇ"„ ˜.ÄAÁsµìåcÔÓÓØÑÑñã?Ž=šºÒ€Q`? ´ ‚^x!tàÀ#Ñû`2cOÆ4©P(„Ba}}=Bèw¿ûݾ}û´føþûïW¬Xáåå¥Udè±téR„ÐgŸ}&“ÉŒ™_ó¶kº/YÅjÿ&®\¹2xðàÈÈÈîîî«W¯&%%iÍššêëë{ñâE­v8) ôˆ‹‹›:ujiiéþð‡óçÏã ìMÿ8.\¸0eÊ„Pyy¹··÷°aôfè÷YR&ÝyÒŒE€•Q5F÷îÝ B½þú릠9ýùçŸ'$$¸¹¹999áÆ–––´´4¡Pèëë»lÙ²®®.ræ¼¼¼¸¸8>Ÿïéé9þü¶¶6ü£šššY³fyzzòx¼qãÆ=z”\E½qêÔ©øøx>ŸŠŸG¤÷_³àìì숈.—©y¿|µi©®®6Ôc„Þ@á•——ãç4¯X±¢¯¯Ïø4§GŒqùòeüT2,88øÐ¡CÝÝÝwïÞ]²dÉŠ+È™G}ñâE…BÑÚÚúÒK/¥¤¤àEGGüñÇ …âÊ•+O?ýô€½9s&88øüùó===µµµ .Ô-O«%77W,_ºtI¡P”••‰Åâ¼¼¼kÓ«··—Ïç_¿~ ˆ)S¦ìÛ·ÏÈß™´?ÔŽÑ矎߀9ò‹/¾ h¸Íé«W¯˜Y*•úûû“3×ÔÔ?joowssÃÓàÎ;®Z³·ÄÄÄÓ§O.O«%>>¾  €l?{ölBB€µéõý÷ß»¹¹©T*µZíáádÚȤ£vŒ~ýõ×W^yE(ânËÊÊŒ)@sZëA@­­­K—. Â_@A‘Ç´!­G¹]­^½ÚÏÏoùòå¼}û¶1½ …B™Lf¸<­@ —ËÉv™L&¬M¯}ûöMœ8‘ ˆ7np¹\…Ba`fȤ£pŒ<èéé‰;ܰaÃ3Ï–ìM"‘lݺuìØ±´O+ËǨ¦¦ÆÕÕ!´sçNó Ð;ÕÕÕ͘1C ‚1cÆ>|ؘL$%%ñù|Ù³g߸qcÀÞ‚8yòd\\Ç Ý¿?n>¾ß‹'Buvvîܹ1BKQQÑÅ‹%IJJÊË/¿Œ7nÜØÑÑÑÔÔôÓO?}óÍ7zû4{AFÈd²S§Nq8œ]»v1]‹£2)Áf,BÝ2š››ÉGA………Ý»w·;99‘Ÿª©T*ggg½½‘µÖ‚zðàž–Éd...x:((¨¾¾O×ÕÕ!}ûI“´œ…c”——‡Ò{4¬Ã~ö“K—.]²dIGGGGGÇ /¼ðâ‹/âvwwwò1 R©ÔÃÃCïâA¨Õ꺺ºˆˆ½—G‘Ï™ruu%oµØÒÒŠ§É ªdÄ—_~‰Z¸p!Ó…80“lÆ"4Ñ-ƒÏçk¾ŸäóùxÚÔ÷“·oßJ¥ÄÿîîôÎ8à~Ò¤-gáÅÄÄ þ?IV`?ûÉØØØ;vH$‰DòÞ{ï9·/^¼øÝwßýõ×_ïÝ»·aÃrÿÙŸ¡C‡&&&9rĘ•>÷Üs«W¯~ðàÁýû÷׬Yc|µf/H·ææf„Ppp0Ó…80“lÆ"”ë¯þŸþyÚ´iB¡P(N›6<]®V«×®]+‰D"QzzzŸOj¾,,,Œ'ŒØÝÉd²ÔÔT¡Pèçç·cÇ.—«5ƒ© ZÎ’1êììD¹»»SU 0Üûƒ27nܘ={vCCƒÕÔË’1ª©©‰ŠŠ‚›Ö1ˆÞc×®®®Í›7Óº Æ­\¹²½½½¥¥å­·ÞJII±Â‚ô‘Ëå¡þNƒë +“‰dóæÍ!!!4­‚%¢¢¢bbb† æéé¹eË+,Hüýîîn¦ qhÔ»ê>”Žu­É’c×[·nEFF†‡‡ßºu‹êº€±¨¼Io,„÷“===LâШÉ$¤Ñ>àLâw•€)–»“F8vµ&KŽ]ÕjµP(ìëë“Ëå€êÒ€QÌ?ǃÏâ„……eddÀîÑ>899qçΦkq\滚z¤šœœlÆZ#†ÚØØøË/¿èÞX‡9™¿««‹¼‘1°&Н­Ó›LȤ5YþÝÐÐÐÛ·o߸q?DXÅ× ÃѬÀ_7|31@Z¾É´i±±±ÞR2‡ÆïOâdÞ¿Ÿ¾U:À~’Y´ßÇÅmcð~2ɸ÷‡½±|Œº»»=<<œœœºººx<u¥£ØÏ}ëU„Baxxx__ßÍ›7™®ÅA&pš‡AI >ÍSSSÃt!Ž2 ôÀW ÔÖÖ2]ˆ#‚L= “ ‚ó®ö†’1êìì‰D®®®]]]¸C`5°Ÿz 4Èßß_.—ÃM@¬2 ôǯðqˆõA&~ÇGð–’ I œæa dè‡÷“pìj}I ì'™Ÿ…تƨ¯¯ÏÍÍM¥RÉd2¸'º5Ùç~òܹs3gÎ …ÞÞÞ©©©mmm¸ ˆuëÖyyy zH*•zxxn×äååõÛo¿‘/ûí7///òåÞ½{Ÿ}öY;û0d’v›ÉãÇ/Z´èĉñññdclllEEž®¬¬Äߦ7Юiâĉä˳gÏNœ8‘|¹téÒ÷Þ{oúôé•••”o S „êììdºcÒ‘®‹0b÷îÝC‡Õ=Ý’••¥÷üjíš.]ºäïï_\\¬P(Š‹‹ýýý/_¾ŒDþNŽ;æíí}ñâEÚ¶l`ŽÑ§Ÿ~ŠzõÕW)é ‰ÞLJ¥ÒŒŒ K¢€î¿©TJ„Z­^»v­H$‰DéééjµÏß_»–üüüØØX.—sêÔ)ÍÕ‘Ó'Nœðòò¢sã@a&333B+W®¤¤7`$º2ÙÑÑAÞݬ€™(Ìäûï¿Z·n%½#QÞÕÔ'«ÖêééAÁ…uVFe&!vŸ‹ …LâX¨É$¤Ñ.ýüóÏ¡ˆˆ¦ q,–fÒhÇ~øá„ÐØ±c™.ı˜ÿ]-ãÓhÒ*€…¨ú®Öƒ|}}=<<$‰““Ý~ŽÍBæì'MÝ7&''›±À,¼“=z4ÒÊÌÉäÉ“'wíÚE^Œ6 ÒÒR3Ö˜uúôi„PBBÓ…83]MÚU–””˜_ 0>*±ðص³³3$$¤««ëÇ5jE¥£Xtï#“ ï'­‰’÷“|ðÁš5k¦OŸþïÿ›¢º€±(¸πɄLZ“å™T*•aaaÍÍÍùùùsæÌ¡®4`Êî‘e ™Ik²<“Ë–-ËÉÉ ¯««ƒ<ÖGñ}ëô&2iMfrçÎééé®®®ÅÅÅ&L ´4`Zî%©•LȤ5Y’ÉÓ§O§¤¤ „¾øâ‹ùóçS\0’IW¬›´ùÕ“V,dưÑÛÛ»aÃggg„Ð_ÿúW: F¢ýžËJ¥ÒÅÅÎïúÃ*¦Ž‘J¥:vìØÖ­[kkkœœ¶oßžžžNg`pt{cê]½z¿o ÿç?ÿ W·22ioÌ£%K–L:uÑ¢EpDÃI{cdëàÓ'Ø2 »@&`È$와] “° dvLÀ.IØ2 »@&`‡ÈdLLÌðáõ ãââø|~TTTnn.nÄ‹bÕÕÕÑÑÑÖ«„#dòÊ•+J¥’Çã•••‘åååøÉÊR©´  @÷n—ß|óÍŸþô§Ï>û̺Åàß IKK äñx ÙÙÙ¸qÞ¼yO=õÔ’%K´fæp8A|õÕWëÖ­ËÏω‰±z½–²Å1šì<“ …"((¨²²’ËåŽ=úîÝ»|>!äëë{ýúu???­ù9Nvvöž={ Åb1%[ÊæÆh±óc×Ó§O=Z,Ž7.??·wttx{{ë]$--mïÞ½6H`ì<“‡&P—,Yrøða<-‰ÚÛÛõ.²wïÞgŸ}öÚµkV*-&ÝQËŒEÔÚÚªu3 —––‚ æÎ{èÐ!ÝEðÖåææúùùUTTX»b*ØÖ]ö¼ŸÌËË{á…4·vñâÅGŽA¥§§¯_¿þܹs½½½·nÝz饗4\´hч~øä“O^ºt‰¡Ú3)Áf, 1cÆ”””h¶”––Ž3OŸ={v̘1</22’Ügjn݉'¼¼¼¬U,elkŒ€.;?ïjV¯^½uëVwww#ç‡1²uI¶ãp8"‘hÕªUo¾ù¦H$2f~cdË “lG^îgd2aŒld’í4/ÁEF$ÆÈÖA&ÙN+“˜dÂÙ:È$ÛéÍ$¦7™0F¶2Év2‰i%ÆÈÖ™“É©S§ÒUÐQZZjÌld2½¼¼dÒ–™ó%#ÿJ€5I$’Ý»wóa `9sö“ºßôINNpÍÃW8vµuð~’í ¿ŸÔ=Ícdë࠶ʤ‹{€ LÚH£}ƒLÚH£#€LÚH£ã€L²¤ÑÑÀyW¶“H$&¥ÆÈÖA&í Œ‘­³çûñ`‹ “° dvLÀ.IØ2 »@&`È$와] “° dvLÀ.IØ2 »@&`È$와] “° dvLÀ.IØ2 »@&`È$와] “° dvLÀ.IØ2 »@&`È$와] “° dvLÀ.IØ2 »@&`È$와]%“œÿàóù“'Oþé§Ÿ¦OŸžŸŸOÎpêÔ©'žxŸ‚ÇÜ úIDATBsNsU‡D˜ÂŒEX‚,[&“½ÿþû±±±ååå ä ãÇ¿råŠæœ6ÊvÇ`޲Ÿ$¹ºº¦¥¥Õ××O˜0!00°°°!ôõ×_?öØcLW€Ã»’äryVVVBBBhÛ¶mÛ·oGmß¾}Û¶mL—B¹0]€õï ÝÝÝ/\¸€9rdhhè;K;'Füç€+à˜ô‡ÿXmño”Ãy´¥ÝÝÝ999_}õUii)B¨±±1<<¼¡¡!,,LkNe»c0‡Ë$BH&“y{{÷ôôèþH÷¥Í±Ý1˜Ã½ŸìîîþôÓOÃÃÙ.ýîý¤‹‹Ë˜1crssœ“û`MŽ’I¹Òú$0ËáŽ]`9È$와] “° dvLÀ.IØ2éè–-[öÕW_1]ø/G¹ÞÕq˜4F—.]š÷ÜsT—ŒB屫D"Ù¼ysXXXFFH`Cœ_{í5„З_~Ét-Ž‹šý¤î¾Qì'­ÉÂÏ«êêê†.‹›šš¨, ÍÒLN#™´& 3)“ÉÜÝÝ…B¡\.§´.`,ó3iL1Ȥ5Y˜IµZíââÂápúúúœœà£2˜sïãÓˆ%''›±Àˆß~û ˆÁƒC ™bN&Ož<¹k×®®®.#ç‡Ï»lÈ;wBþþþLâ¸ÌÉä‹/¾˜’’²gÏžÌÌLcv•%%%f¬˜Ç£’ªª*„И1c(*˜ÌÌûÖ‰D¢ŒŒŒ7ß|Ó˜dN:Õ¼µëÙ7nÓ…8.‹Þ3àd666nÚ´I$QU`d’q”]ÇcàÄœwµ&KλáææÖÓÓ#‘H<==©. …âkëô&2iM–d²¡¡!"""88ŸéŒ ø|·H$Ú´iScccFFÍÚœÚÚZ„PTTÓ…84Z>ƒ‚dÚ(È$Ðø¹0Næýû÷é[ Îdtt4Ó…84Ú¯Õpqq”ÇÙ|ÀÇLJéB\?þK  „ Ó…84[Êä¹sçfΜ) ½½½SSSÛÚÚp;AëÖ­óòòßÓÓsþüùmmmøG555³fÍòôôäñxãÆ;zô(¹Šþz#âÔ©Sñññ|>?44Ÿ5Ð{\­YpvvvDD—ËŒŒÔ<Ú4P›–êêj3ŽäÛÚÚ<<<­kÀ_±&f3¹{÷î¡C‡êžnÉÊÊÒ{~µ¿vM—.]ò÷÷/..V(ÅÅÅþþþ—/_Æ?"·ôرcÞÞÞ/^¤m˨DÕÕ××'%%áÞòòòz{{,@szĈ—/_Ö\688øÐ¡CÝÝÝwïÞ]²dÉŠ+È™G}ñâE…BÑÚÚúÒK/‘çÛ¢££?þøãÎÎN…BqåÊ•§Ÿ~zÀÞΜ9|þüùžžžÚÚÚ… ê–§Õ’››+‹/]º¤P(ÊÊÊÄbq^^Þ€µéÕÛÛËçó¯_¿NÄ”)SöíÛgø—–žžþòË/?Z—áYu«g0“ºÿu¤R)AjµzíÚµ"‘H$¥§§“‡ýµkÉÏÏår¹111šûÍ-=qâ„——G ÇH­VoÚ´‰Çãá>=<<ðÙ€hNþ I*•úûû“3kþÃmoowssÃÓàÎ;®Z³·ÄÄÄÓ§O.O«%>>¾  €l?{ölBB€µéõý÷ß»¹¹©T*µZíááQUUe`æ¼¼¼¨¨¨ŽŽŽGë20«.Æ]Á€¨#¥R¹ÿþÄÄD‡ãââããã#—Ë)@sZóA­­­K—. rvvÆ¥’Ç´!­ÿ›dW«W¯öóó[¾|ùÁƒoß¾mLoB¡P&“.O«E hn L&֦׾}û&NœHÄ7¸\®B¡èoΣGŠÅâ[·ný·gýê‚L²Ÿåc¤R©:Œ»©©©ÍÍÍ?üðƒ‘èÆ’““W®\yóæÍîîn‚ z{{õ¾¯Óm©¬¬Ü¹sç3Ï<ãååµmÛ¶{£<“öC¶÷G÷dáBBBjkkÿ§½ýö2É~ŽÑÇq'ãÇ?zô¨‘o#5 Ð;q¹\œìܹsFf’„Ÿ/4`o'NßÃÃcöìÙ7nܰ7‚ Nž<ÇãñBCC÷ïß9LÞ!Ek½YYYäg!9996ÄÀ/ùàÁƒøƒ·††2œº‹ë§yèzþ$`ŠÙcTQQ‘  ëëëi( Å–¾«huôèQ„Ðo¼ddYUUåããóÃ?ÑV\ƒPIIIUUÕðá몪 Œƒý¤½1oŒöìÙ³dÉ///zŠ&€LÚ#[Ç®° dvLÀ.IØ2 »@&`Ó®w…»~°Œ‘­ûgè…ƈñIEND®B`‚kamailio-4.0.4/doc/sip/figures/dialog.dia0000644000000000000000000000327112223032460016722 0ustar rootroot‹í\[oÛ6~ï¯0œW—o¢Ô4)Ún† +°eödÐ6ãh“%CRšºûí#)ÅJl)i6Ž/j¸¦ytÄï;<7Êoß}™Ç½Ï"Ë£4¹èCàõ{"™¤Ó(™]ôÿ¼úéuÐwùêí4âoä¿YÆç=9#ÉÕÿ.ú7E±x3ÞÝÝx™ó"Í@Ý‚\ ÿãq̇òKÃþåÃÙS^ðò^Y4¾-D/ásqÑóÉ¿³,½M¦ÕŒI§Yï3/úg×ú¯?¼|;|4·YÔ‚/D¶’2_¤y$ÇŠåbml}žz­†r9’Ì.ÏÞ“³ò’Õ®_Ìy6‹’JT&x\ÞB¨ë]ŒH‰HÉœH‰òÑ"ÍŠŒGE%iœ¦±àI)¬ÈnEWQù„Ç …`W×QQ¤MZ\ó8oTcøˆNÝ®2Ë¢i  ­Ïº‹¦ÅÍè‹ýÍ•–ö>Gy4ŽÅJ‡(),%,;I°ZÛÛh*ò¶Õ}8¸>󦶨^K¯aãÖó¥È*aêm­W­IÅôò»éø1)*­ÿ(x2åÙ´÷º÷k”ˆ~½3Ë}9š^ô?y-·$ÅHÛº¿áEZ/·?`]1SBÆãk&R›Y,*9„tàË—s葯]%OÒ$‰dª{BËÍézäv>Ytg[¹ðV @SvB+v½–›¿D…%„î,!¨Â C3›ÝÐ4[ê6âY–ÞU„WŠÀÆ"F±HfÅ͆òº¯W-K{´®¢¶¥¶°oÝq z+rAèŽ]+-7Y×]û¿¶ç±ávÈ/Tó »åná>ñëùøEmøEò‹Ôü¢nùE[øEOüúüº_Š~ùÆ¡W|2Q7$ÓÒÏ‘'EJ¹ag¹…º£æ”ñÁÐFµAÖJC?ÿö×ÏW?Z”‡®ÓäþBêmïšÏ£x)¯Â“¼ßË‹¥JåZ—ßþ˜Þf‘ȺÞàˆf7Å6¤r„ .ß5”ò<ý×U £Y2«« γ©#lË|fÁ|ˆuDG™+ò+!‘L!ðJé!Û… @Ïë]eKùþxÌÀ΃´„ÀÆTäéÌ@•…ʈiwâ`àõ~—ïŽËÌp’‚nŠÔmC7hEé8ÉkõuµgEƒÀp 5ÌäZ!ÖhØeaAê"v·uaq‹Ë´¬H[atjíìÔU:.¶ÛQ,«±í”§vu)2>‡B@y„@»{÷ö&7Ę›òûüurrÎï÷;—/¿{s_? ‚ ˜–ÆÔºLÐ¥9bñk×®mnnÖ͈ÎßΉ¥ZÏh”‰àààÀÀ@6,³ð† ‚ zzz¢¢¢¶mÛ¦›½GEb¸|ùßÿþW©Tòx¼«W¯Ì) ''§·Þz çË™™™ììlWWW;;»ÄÄD™L†ÇLNN&''ÛÛÛ»»»>|˜LdãÒ¥K¡¡¡<ÏÏϯ°°ü §drðÑ£G}||¸\.B¨³³3&&ÆÁÁÁÆÆF$I$Ú°9t©êRÃh¾i|úé§ÉÉÉIIIŸ~ú)ÙÉ´f .Ž?îççÇãñ^|ñÅ[·n©Ý €–ŒŽŽ:thÕªU¡ƒÖ××744 X[[ïÛ·ÉÉÉîêêºuëVmm­ª‘¤¤¤ÜÜ\©TZ[[ûã?¢ŸR)NÉä°ººº††¥R‰ŠŽŽNOO—H$‰dÅŠ™™™´a?3ABö¸¸¸”””ÈåòîîîÔÔTÜ™——·víÚŒ¥¤¤¼ñÆ䬄„„ÁÁAB¹\¾xñâ®®®ÞÞÞÅ‹ËårÜ¿wïÞèèh‰D200 ‹I¿\ÄÆÆ>xð@&“8pॗ^¢ hUUžžžAøúú666âýýýnnn¸íååÕÞÞŽÛmmmTÁá†ÏÑ£G»»»i.hûúúÔ#“É–,YB6‡.ÕºdZ€ß%%%«W¯ÆíuëÖ}ñŚ׬ÁÅÐй.—«v+šÁ›kvv¶­­M(–––Áår9‡Ã±´|ò«æp8J¥·§§§UuYWWíââ²téÒ¯¾úŠö­ÚW¯^ …öööøƒôõãAXXXÌÌÌs”J¥¥¥¥—L Ð ±X|êÔ)ÜþüóÏ7mÚ¤yÍZºPÝF€6P7Www·‡‡‡T*]ºtéƒT{zzjΗ$åååîîîj¿¥}ôöö...žUµ‰‚ðòòº{÷.9§¥¥ÅÛÛ[ƒK¦0‰c``€vÄÉårûûû5¬YK KÝ m®Í›7Ÿ8qâý÷ßß°aC[[ÛôôtcccBBþ6333::zppP"‘DGG«nóßÿþ÷MMMSSSeeeä™Ïç·´´0y\¼xñ¹sçäryGGG||¼z]fee‰D¢{÷î)•Ê{÷îýæ7¿ÉÎÎÖà’iLâ8räHrr2µgçÎGŽѰf-]i[Ð m3~óÍ7aaa333ôõõµ²²ZµjUII þV&“mß¾ÝÖÖÖÕÕ5??ßÊÊŠfäÌ™3AAA\.744ôÛo¿Åï½÷Þ¢E‹˜Gii©¿¿?—Ë]¶lÙ±cÇÔërjj*++ËËË‹ÃáxyyýíoS(\2-€I—+W®¬ªª¢öTWW¯\¹RÚµtA~¤m€%îܹ³lÙ282¡?¤ÁÖ <-{öìyôèÑÇÅbñ_þòx4þuÈ?ÿùÏ?îïïë­·bbbŒ †   ààà€€GGǼ¼<x´ Œ}ŸÛÇœ——711ñÛßþöĉxw ˜9Æ×%`²Ü¹sçòåËºÍ  …:»VsÁ0—/_Þ½{·nsÓÒÒŒ©Ëg¹$ ©ÚÄ NKKSûN¥’Ⳉ¡g>±bD×€Á7ñìÚµ‹%ûÏš/ fiâTªa0aüóDÀ|„í¤ºLÐ%  /st èäKÀ]ê ¦‡¦-T §Ü¸qcÓ¦MNNN¶¶¶¯¼òÊùóçÉ)¸ÑÛÛ»uëV@`ccùõ×_«5H}2NÕ‘^žæ^`˜‘.óCÓ´“º¸³©©iÆ qqqÝÝÝÃÃß}öÍ`|||```{{ûèèh^^õWâçw‹jp¤!0“…õ“Óz4­ªªªÍ›7k¶ÿþÎÎNCf °/Íë~"òÎÓÓó‡~PíÇàÍýøñcÚÚ_âË/¿üûßÿ199¹qãÆÃ‡{zzjÍS`f‹yåK⧇¦ýýýoß¾Mí§‚;‡††¨T zyy­aLNNNNNÎéÓ§ÇÆÆ EMMê±X\SS#—ˇ†† žýÐP›ÀLÈ—zƒ ~hšP·eÉ1×®][³f½½½P(¼páÍTYY™P(´¶¶vtt‹Åä;ÔzdrĘ)ƒO™íرƒ%û,>ß÷_.`>ÿüó;vlß¾ýßÿþ7öÍt?˜8 K@àú8`Ž€.]€| ˜# K#c°wÂë×äKÑÐÐÀR – élÕ<]>¡¬¬ ˜§ø<1äKýÐÒÒ‚ºÿ¾¥¥e__B¨¥¥% ¸xñ"Ö¥\.ßµk—“““““SZZš\.Wµ6==½wïÞ%K–‚#GŽàNZ™¦R/SSS¯½öY†´ùŒ¥aÈSiµµoL sÑ¥H$ª®®F•••Y[[—••!„ªªª6n܈’H$­­­ø•:o¿ýv___[[[kkkWW×;ï¼£j-77·±±±¡¡¡££ãþýûd?µD†r8ƒƒƒ]]]7oÞ¬¬¬$ç2GUTT\¹redd$&&&551ÈÁh(ƒQ-·£p}\?”——ÇÇDZqãÆ¬¬,±XLD\\Ü¥K—‚(**ŠÅ#===[[[q»¥¥ÅÓÓSÕš··7µRý¼LŒ†r8mmm¸}÷î]r=ciµ›šV7ÔÖ¾yZŠ‹‹Bx“²¹èrbbbÙ²e2™L  ;;;Ëd2??¿ÉÉI‚ ¶lÙRTT„GÒJ·: Âáp¦§§i´Å>m9œg, C~œ³4ŽÚÚ7OËÙ³gBqqqºMŸsÙÛÚÚ}üñÇaaa|>?,,ìÿøGpp°ÍôôtEEÞ¡#„ÜÜÜÈç":::ÜÜÜT­yxxttthöèááÑÕÕ¥T*qm¤ÙÙY²ŸjÎñOËÖ­[ßxãžž\‡PÙÕ¾ôÒK¥¥¥ƒƒƒ'Nœøãÿ¨›¶1]"„D"Q~~þºuëBëׯÏÏωD¡ÚÚÚ   WWW<,!!!33—nIOOOHHP5•œœ¼gÏžžžž‘‘‘ôôtµî^ýõ]»vµ··+•ʦ¦¦ÄÄDÜŸ˜˜˜‘‘AÚŸs<|>Иœœäñx¶¶¶]]]jß^™ÐÜܬP(fgggff4{aŽ/õFkk+BèúõëAܸq!„ó222òòòÈa“““©©©ŽŽŽŽŽŽ©©©xGOC¡P¤§§;;; ‚?üwÒËTêerr2%%—†9tè9ëKÃ9K㨭}CDTT­œ¾øâ „ÐÖ­[µÿ´˜‘.™hhh0vÆÿ½´TgII Bˆü±¨wÌh?ÎDkkë /¼`ì(L…êêêÕ«W¯^½ŸV3 K@ sª®÷Fȹt ÌZuB¾LçNÖß³Àî¿2sª«««««£¢¢Ö¬Yƒ _æëù’½)@¿h³g‹ŠŠÚ¿TT~E-{\ózŸ 3¤" ãt ÌZEÂû/£aàIt ¨aNEB¾ Šs$Ð%ð„§R$äKÀ@TUU;„ÿçÕ]€ëã€9ºtò%`Ž€.]€| ˜# K@ _æèÐÈ—€9ºtò%`Ž€.]€|i¢I}V‹ ˆ¬¬,@àììœMþÍ,~Ž“““‘BžO€.u¤¨¨ˆöêÔÂÂÂ+W®477766VWWŸäK]ÈÎÎ~óÍ7ƒ‚‚¨MMM¿üå/q;,,¬©©‰6«´´4$$ÄËËË@Q² ܯnrÔÕÕݼy¿Š—Êøø8~/BÈÁÁA*•Ò¼ÿþû§N2@„ È—OMFFÆÉ“'9­Ñ¢Eããã¸-•J¨ß^ºtÉÍÍ-88Ø@Q² ü79®^½JÖ#’‡„„Ô××ãöõë×CBB¨³Þ}÷]j©(@3 Ë§†zxŽ(9#))éwÞyøða__ßÛo¿’’BNùþûï-,,"""Œ0Àñå¼a÷îÝ÷îÝ[±bnÿá ¿z÷Ýw³²²ŒÚüÂ=ɳ~Š 0UUU¯¾úêêÕ«©%.õˆ íÇñi`˜Ä~¼ººúÀÆ-Ì< üø ¨ÅhºEÎk`¾EsbP]‚" $_‚"§ƒ¥û”Èÿ¤ªª*£¿ä` ¡PÈ’xLèü%ü–ôNüü¾`í³&{ñzäòåË¡ÈÈH–ì(_âjë°O´Ä ûqPç‚aÞ ê\,@]b@€Œü{Ô9OY°ù’ ¨ a÷¹a¢¢¢@—ó³È—@t èäKÀ]ºù0G@—€.@¾ÌÐ%  /st èäKÀ]ºù0G@—€.@¾ÌÐ%  /õ YÏÁÁaíÚµÍÍÍ´~rÊ76mÚääädkkûÊ+¯œ?žœ‚½½½[·n666‘‘‘_ýµZƒdµŽ˜3OÌN—è§W'ôôôDEEmÛ¶ÖO‚;›šš6lØ×ÝÝ=<<\PPðÙgŸÑ ÆÇǶ··ŽŽæååáÒ>T;4›jiÌ:|z†’\.çñxªýTâââŽ;¦Ù”••ÕÄÄ„658b Ì4¹qãBhåÊ•,Ù7Ç|‰=tèЪU«4«ªªÚ¼y³æ1aaaû÷ïïìì4d`Æò¥ž¡®ÝÓÓ³³³SµŸ9‡ÃQ*•´1äGÜèééÙ¹s§‡‡ŸÏOLLìíí¥yd €fMm`¦ÉÍ›7B¿øÅ/X²oŽù’ ˆÙÙÙ¶¶6ÿÛ·oSû©àNggç¡¡!êUƒ^^^'Ožìëë»s王‹K\\Üœ¨:Ò˜ ùRÏPCêîîöððJ¥óa_lllaa¡Z j§Œ[YY1yÔàˆ)0ÓäÖ­[¡çŸž%ûæ˜/I|||ÂÃÃOŸ>­aLNNNNNÎéÓ§ÇÆÆ EMMê±X\SS#—ˇ†† žýÐP›ÀŒ äK=C é›o¾ #˜û‚¸víÚš5kìíííìì„Bá… h¦ÊÊÊ„B¡µµµ£££X,noo×à‘ÉS`¦ >Ì eÉ>Ôát¡©©)444$$¤±±‘ ûf½LÐ%  p}0G@—€.@¾ÌÐ¥A¡Þ>7¯A¾4( ÆŽB 4L߯tù3ÊÊÊÄb±±£˜£Ÿ†|©OZZZB÷ïß·´´ìëëCµ´´à/^TÕåôôôÞ½{—,Y"Ž9‚;-,,Ž=êããÃårB333ÙÙÙ®®®vvv‰‰‰2™ ›ššzíµ×ìííÝÝÝ>LÚdoaaqüøq???÷â‹/âËÐÔÝÉa¸ÑÙÙãàà`cc#‰$ -øK—.…††òxÃÆûp}\Ÿ”——ÇÇDZqãÆ¬¬,±XLD\\Ü¥K—‚(**ŠUåíí}÷î]Z'B¨¯¯üèëëÛØØˆÛýýýnnn¸íååÕÖÖ†ÛwïÞ%7Óx„ÐÐÐnËd2.—KöÓ¼«Æ)“É–,YBàããsôèÑîînÆ¢x-ú5Kb^ºœ˜˜X¶l™L&ÃÃÃÎÎÎ2™ÌÏÏorr’ ˆ-[¶©Îâp8ÓÓÓ´NÚÒ¸\.‡Ãáp8–––8sÉÛŠ§§§ÉYLã™ôÇÔõêU¡PhooO¦1Ú€ºººèèh—¥K—~õÕWZm&-hmmEèË óÒ%A6l8tèÐÚµk ‚X·n]AAH$"B¡PðùüÕ)Lù’úqéÒ¥??_$!„jkkƒ‚‚\]]U§$''ïÙ³§§§gdd$==]­Ù×_}×®]íííJ¥²©©)11÷'&&fdd J$ê\¦ñLðù|,k“““<ÏÖÖ¶««+--Mu@BBBss³B¡˜™™ÑìE{àøRÏàôëׯ?=Ô‡óYFFF^^žÚ) …"==ÝÙÙY |øá‡¸“¶´™™™ƒúúúZYY­Zµª¤¤÷ONN¦¤¤ØÚÚººº:tˆœÅ41äÅ÷Þ{oÑ¢EH%–––úûûs¹ÜeË–QŸ&gΜ âr¹¡¡¡ß~û­NÛL mmm¡åË—ëË ¸ÿò gÏž}á…ŒÈüàÞ½{Ë—/÷÷÷ooogþÙíÇ™hmm5sQfff>|øÐØQ<t <¡¸¸ØÏÏo÷îÝÝÝÝs†ë=€áP(………Zª“=@—mÔ ù0ÆÍ K@Lê„| ÃçNÖÏ_†……±dÐ/===sãñx)))ÉÉɾ¾¾]]]lÃeÃ(•ëׯ³íÐ j¯Áª¢P(Μ9ƒ/>±뺬¯¯gÛ ´¹QŸÏç§§§gddŒ9r„½-뺄ýø|Aó3C¤"ù|>BhllŒÕ`X×%0ß¡)ÃöïqÐ%ÀˆZEÐ% †9 ù0(FÌ‘T@—Àø|~ZZš–Š„| ˆúúz;;;cGñ¸ <á©D ×Çst èäKÀ]ºù0G@—€.@¾ÌÐ%  /st èäKÀ]ºù0E@—Fà»ï¾[¿~½­­íâÅ‹·oßN!"++K 8;;ggg”wϪí§Q^^þüóÏ[[[‡„„àRê_·oß~î¹çX[Ùü¥÷½¦ú¾`mX¿~}yyùøøøÀÀÀŸþô§W_}÷?~<<<¼¯¯¯··÷׿þõ'Ÿ|¢¹ŸÊÕ«W=<<*++åryEE…‡‡Çµk×ðWäVª©©ñ÷÷¯««c‰ÏÊ£GBÎÎÎ,Ù]ÎL&³¶¶ÆíðððŠŠ Ü®¨¨ˆˆˆÐÜOåw¿ûÝ¿þõ/òãÉ“'cbbpo¥sçÎ-_¾¼¹¹™uèÐ¥‘9þ|TTn;::ŽŽŽâöÈÈŸÏ×ÜOÅÅÅ…Z좿¿ŸZkçøñãÏ=÷\WWK«Ð;?F –ìƒ.5Q__ïïïO:±´´œ™™Áí™™‡£¹Ÿ µAJ¥’Z3ÊÒÒòòåË,­‚ ØÖ%üîa¤ªªjË–-gÏž]¾|9îY´hÑøø8nK¥RÍýTÞ÷a=z$ÈŸ|òI|||cc# aø=nΞ=»cÇŽsçÎQßcB¾néúõë!!!šû©¼üòËåååäÇ‹/¾üòËäÇ;w¬Y³^3ö–ò01Ÿ÷ã|ðêOþóŸjw3õSùá‡ÜÝÝ+++§¦¦*++ÝÝÝÿóŸÿà¯È­T\\¼xñâ+W®°¶2½122‚rrrbÉ>èR ªÿ½R©” ˆÙÙÙ¿þõ¯|>ŸÏçïÛ·ovvgê§QZZbee|áª;²}îÜ9öÚôÛº4•ºRR©ôƒ>xóÍ7]\\XŠÐ#cccNNNøDö|922’››ëããsàÀ…Baìp“À˜ïÛ9zôèG}„w À_æææj¯ÈmÛ¶I¥R¶CžÙÙYv°t^”üOòððÐ>777vV °‚Kâaý¼úÔÔÔ©S§<¨Mý¶ï¿ÿÞ¸¯O´D.—GFFÚÙÙÉd26ìèzB¡ÐF½½½žžž,Åè‘ÉÉI;;;[[Û‰‰ 6ìèzÇKKKkmm=qℯ¯¯aœóƒ^‡u.àý— N`NŒvߨs^³ó%P' S¹ÿT*•VVV,Åè…BammÍãñ¦¦¦Ø°oüû/I,,,@”Æ„t Ì#øñ%¨t èäKÀ]ºù0G@—€.@¾ÌÐ%  /s„ÅçÎØûgŒµV+öA=€nÁž:ÿjLŸ´•³›ÉIEND®B`‚kamailio-4.0.4/doc/sip/figures/registrar.eps0000644000000000000000000034610712223032460017527 0ustar rootroot%!PS-Adobe-2.0 EPSF-2.0 %%Title: registrar.dia %%Creator: Dia v0.91 %%CreationDate: Tue Jul 15 14:59:52 2003 %%For: janakj %%Orientation: Portrait %%Magnification: 1.0000 %%BoundingBox: 0 0 1000 536 %%BeginSetup %%EndSetup %%EndComments %%BeginProlog [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def /cp {closepath} bind def /c {curveto} bind def /f {fill} bind def /a {arc} bind def /ef {eofill} bind def /ex {exch} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth pop} bind def /tr {translate} bind def /ellipsedict 8 dict def ellipsedict /mtrx matrix put /ellipse { ellipsedict begin /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc savematrix setmatrix end } def /mergeprocs { dup length 3 -1 roll dup length dup 5 1 roll 3 -1 roll add array cvx dup 3 -1 roll 0 exch putinterval dup 4 2 roll putinterval } bind def /dpi_x 300 def /dpi_y 300 def /conicto { /to_y exch def /to_x exch def /conic_cntrl_y exch def /conic_cntrl_x exch def currentpoint /p0_y exch def /p0_x exch def /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def /p2_x p1_x to_x p0_x sub 1 3 div mul add def /p2_y p1_y to_y p0_y sub 1 3 div mul add def p1_x p1_y p2_x p2_y to_x to_y curveto } bind def /start_ol { gsave 1.1 dpi_x div dup scale} bind def /end_ol { closepath fill grestore } bind def 28.346000 -28.346000 scale -2.750000 -23.600000 translate %%EndProlog 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0.080000 slw 0 slc 0 slj [] 0 sd 0.701961 0.701961 0.701961 srgb n 17.499044 14.949999 m 17.499044 21.355507 l 20.244262 21.355507 l 20.244262 14.949999 l f 0.000000 0.000000 0.000000 srgb n 17.499044 14.949999 m 17.499044 21.355507 l 20.244262 21.355507 l 20.244262 14.949999 l cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 17.773566 15.334330 m 17.773566 16.066388 l 19.969740 16.066388 l 19.969740 15.334330 l cp s 0 slc 0 slj [] 0 sd n 17.773566 16.066388 m 17.773566 16.798446 l 19.969740 16.798446 l 19.969740 16.066388 l cp s 0 slc 0 slj [] 0 sd n 17.773566 16.798446 m 17.773566 17.530504 l 19.969740 17.530504 l 19.969740 16.798446 l cp s 0 slc 0 slj [] 0 sd n 17.773566 17.530504 m 17.773566 18.262562 l 19.969740 18.262562 l 19.969740 17.530504 l cp s 0 slc 0 slj [] 0 sd n 17.773566 18.408974 m 17.773566 18.848208 l 19.146175 18.848208 l 19.146175 18.408974 l cp s 0 slc 0 slj [] 0 sd 0.000000 1.000000 0.000000 srgb n 19.832479 18.482179 0.096083 0.096083 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 19.832479 18.482179 0.096083 0.096083 0 360 ellipse cp s 0 slc 0 slj [] 0 sd 1.000000 1.000000 0.000000 srgb n 19.832479 18.775003 0.096083 0.096083 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 19.832479 18.775003 0.096083 0.096083 0 360 ellipse cp s 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 19.283435 18.555385 m 19.283435 18.848208 l 19.612862 18.848208 l 19.612862 18.555385 l f 0.000000 0.000000 0.000000 srgb n 19.283435 18.555385 m 19.283435 18.848208 l 19.612862 18.848208 l 19.612862 18.555385 l cp s 0 slc 0 slj [] 0 sd n 17.956580 19.433855 m 17.956580 21.035232 l s 0 slc 0 slj [] 0 sd n 18.414116 19.433855 m 18.414116 21.035232 l s 0 slc 0 slj [] 0 sd n 18.871653 19.433855 m 18.871653 21.035232 l s 0 slc 0 slj [] 0 sd n 19.329189 19.433855 m 19.329189 21.035232 l s 0 slc 0 slj [] 0 sd n 19.786725 19.433855 m 19.786725 21.035232 l s 0 slc 0 slj [] 0 sd n 20.244262 19.433855 m 20.244262 21.035232 l s 0 slc 0 slj [] 0 sd 0.600000 0.600000 0.600000 srgb n 16.950000 21.904551 m 17.499044 20.806464 l 17.499044 21.355507 l 20.244262 21.355507 l 20.244262 20.806464 l 20.976320 21.904551 l f 0.000000 0.000000 0.000000 srgb n 16.950000 21.904551 m 17.499044 20.806464 l 17.499044 21.355507 l 20.244262 21.355507 l 20.244262 20.806464 l 20.976320 21.904551 l cp s 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 4.118005 16.949999 m 4.444369 16.949999 6.402557 16.949999 6.728922 16.949999 c 7.055287 16.949999 7.414288 17.276363 7.381651 17.929093 c 7.055287 17.929093 7.055287 17.929093 6.728922 17.929093 c 6.745240 17.309000 5.749828 17.602728 5.423463 17.602728 c 5.097099 17.602728 4.118005 17.292682 4.118005 17.929093 c 3.791640 17.929093 3.791640 17.929093 3.465275 17.929093 c 3.400002 17.276363 3.791640 16.949999 4.118005 16.949999 c f 0.000000 0.000000 0.000000 srgb n 4.118005 16.949999 m 4.444369 16.949999 6.402557 16.949999 6.728922 16.949999 c 7.055287 16.949999 7.414288 17.276363 7.381651 17.929093 c 7.055287 17.929093 7.055287 17.929093 6.728922 17.929093 c 6.745240 17.309000 5.749828 17.602728 5.423463 17.602728 c 5.097099 17.602728 4.118005 17.292682 4.118005 17.929093 c 3.791640 17.929093 3.791640 17.929093 3.465275 17.929093 c 3.400002 17.276363 3.791640 16.949999 4.118005 16.949999 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 4.118005 16.949999 m 4.444369 16.949999 6.402557 16.949999 6.728922 16.949999 c 7.055287 16.949999 7.414288 17.276363 7.381651 17.929093 c 7.055287 17.929093 7.055287 17.929093 6.728922 17.929093 c 6.745240 17.309000 5.749828 17.602728 5.423463 17.602728 c 5.097099 17.602728 4.118005 17.292682 4.118005 17.929093 c 3.791640 17.929093 3.791640 17.929093 3.465275 17.929093 c 3.400002 17.276363 3.791640 16.949999 4.118005 16.949999 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 5.423463 17.276363 m 6.728922 17.276363 6.728922 19.234551 6.402557 19.234551 c 6.076193 19.234551 4.770734 19.234551 4.444369 19.234551 c 4.118005 19.234551 4.118005 17.276363 5.423463 17.276363 c f 0.000000 0.000000 0.000000 srgb n 5.423463 17.276363 m 6.728922 17.276363 6.728922 19.234551 6.402557 19.234551 c 6.076193 19.234551 4.770734 19.234551 4.444369 19.234551 c 4.118005 19.234551 4.118005 17.276363 5.423463 17.276363 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 5.423463 17.276363 m 6.728922 17.276363 6.728922 19.234551 6.402557 19.234551 c 6.076193 19.234551 4.770734 19.234551 4.444369 19.234551 c 4.118005 19.234551 4.118005 17.276363 5.423463 17.276363 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 5.423463 18.255457 0.652729 0.652729 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 5.423463 18.255457 0.652729 0.652729 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 5.423463 18.255457 0.652729 0.652729 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 5.031826 18.541026 0.081591 0.089750 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 5.031826 18.541026 0.081591 0.089750 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 5.031826 18.541026 0.081591 0.089750 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 4.899648 18.230980 0.081591 0.089750 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 4.899648 18.230980 0.081591 0.089750 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 4.899648 18.230980 0.081591 0.089750 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 4.997557 17.937252 0.081591 0.089750 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 4.997557 17.937252 0.081591 0.089750 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 4.997557 17.937252 0.081591 0.089750 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 5.291286 17.790388 0.081591 0.089750 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 5.291286 17.790388 0.081591 0.089750 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 5.291286 17.790388 0.081591 0.089750 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 5.585014 17.806706 0.081591 0.089750 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 5.585014 17.806706 0.081591 0.089750 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 5.585014 17.806706 0.081591 0.089750 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 5.813469 18.002525 0.081591 0.089750 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 5.813469 18.002525 0.081591 0.089750 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 5.813469 18.002525 0.081591 0.089750 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 5.911378 18.230980 0.081591 0.089750 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 5.911378 18.230980 0.081591 0.089750 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 5.911378 18.230980 0.081591 0.089750 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 5.829787 18.508390 0.081591 0.089750 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 5.829787 18.508390 0.081591 0.089750 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 5.829787 18.508390 0.081591 0.089750 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 5.601332 18.687891 0.081591 0.089750 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 5.601332 18.687891 0.081591 0.089750 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 5.601332 18.687891 0.081591 0.089750 0 360 ellipse cp s 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 17.050000 6.731617 m 17.050000 9.621323 l 20.325000 9.621323 l 20.325000 6.731617 l f 0 slc 0 slj [] 0 sd n 18.687500 9.621323 1.637500 0.481618 0 360 ellipse f 0 slc 0 slj [] 0 sd n 18.687500 6.731617 1.637500 0.481618 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 18.687500 6.731617 1.637500 0.481618 0 360 ellipse cp s 0 slc 0 slj [] 0 sd n 20.325000 6.731617 m 20.325000 9.621323 l 20.325000 9.887313 19.591866 10.102940 18.687500 10.102940 c 17.783134 10.102940 17.050000 9.887313 17.050000 9.621323 c 17.050000 6.731617 l s 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 8.000000 17.400000 m 10.550000 16.150000 13.750000 16.150000 16.059756 17.096621 c s 0 slj n 15.908067 17.466743 m 16.800000 17.400000 l 16.211445 16.726499 l f 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 16.700000 18.550000 m 13.650000 19.450000 11.450000 19.500000 8.817492 18.725733 c s 0 slj n 8.930359 18.341987 m 8.050000 18.500000 l 8.704626 19.109479 l f gsave 10.550000 20.650000 translate 0.035278 -0.035278 scale start_ol 1330 2071 moveto 1399 2071 lineto 1634 2077 lineto 2287 2077 2624 1820 2624 1324 curveto 2624 804 2265 495 1664 495 curveto 1036 495 729 775 690 1376 curveto 132 1376 lineto 157 1034 221 811 328 619 curveto 557 210 1000 0 1615 0 curveto 2540 0 3136 517 3136 1312 curveto 3136 1847 2914 2144 2375 2316 curveto 2754 2471 2944 2768 2944 3191 curveto 2944 3917 2442 4352 1604 4352 curveto 717 4352 246 3883 227 2976 curveto 785 2976 lineto 791 3227 817 3367 886 3495 curveto 1013 3723 1292 3863 1640 3863 curveto 2134 3863 2432 3595 2432 3157 curveto 2432 2865 2318 2690 2071 2597 curveto 1919 2538 1723 2515 1330 2509 curveto 1330 2071 lineto end_ol grestore gsave 11.015667 20.650000 translate 0.035278 -0.035278 scale start_ol 1216 661 moveto 576 661 lineto 576 0 lineto 1216 0 lineto 1216 661 lineto end_ol grestore gsave 11.252733 20.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 11.489800 20.650000 translate 0.035278 -0.035278 scale start_ol 3200 553 moveto 863 553 lineto 919 907 1116 1133 1654 1445 curveto 2273 1775 lineto 2885 2104 3200 2550 3200 3082 curveto 3200 3442 3051 3778 2790 4010 curveto 2529 4242 2206 4352 1790 4352 curveto 1231 4352 815 4155 573 3785 curveto 418 3551 349 3280 337 2837 curveto 895 2837 lineto 914 3128 952 3304 1028 3444 curveto 1174 3705 1465 3863 1801 3863 curveto 2308 3863 2688 3515 2688 3050 curveto 2688 2708 2479 2414 2080 2194 curveto 1497 1876 lineto 559 1363 287 954 236 0 curveto 3200 0 lineto 3200 553 lineto end_ol grestore gsave 11.955467 20.650000 translate 0.035278 -0.035278 scale start_ol 1696 4352 moveto 1286 4352 914 4180 684 3888 curveto 399 3520 256 2955 256 2176 curveto 256 755 753 0 1696 0 curveto 2627 0 3136 755 3136 2140 curveto 3136 2961 2999 3508 2708 3888 curveto 2478 4186 2112 4352 1696 4352 curveto 1696 3857 moveto 2317 3857 2624 3296 2624 2187 curveto 2624 1013 2323 464 1683 464 curveto 1075 464 768 1036 768 2169 curveto 768 3302 1075 3857 1696 3857 curveto end_ol grestore gsave 12.421133 20.650000 translate 0.035278 -0.035278 scale start_ol 1696 4352 moveto 1286 4352 914 4180 684 3888 curveto 399 3520 256 2955 256 2176 curveto 256 755 753 0 1696 0 curveto 2627 0 3136 755 3136 2140 curveto 3136 2961 2999 3508 2708 3888 curveto 2478 4186 2112 4352 1696 4352 curveto 1696 3857 moveto 2317 3857 2624 3296 2624 2187 curveto 2624 1013 2323 464 1683 464 curveto 1075 464 768 1036 768 2169 curveto 768 3302 1075 3857 1696 3857 curveto end_ol grestore gsave 12.886800 20.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 13.123867 20.650000 translate 0.035278 -0.035278 scale start_ol 2458 4608 moveto 1147 4608 256 3679 256 2304 curveto 256 929 1147 0 2464 0 curveto 3016 0 3512 163 3882 464 curveto 4377 869 4672 1550 4672 2268 curveto 4672 3685 3800 4608 2458 4608 curveto 2458 4087 moveto 3453 4087 4096 3374 4096 2280 curveto 4096 1234 3434 521 2464 521 curveto 1487 521 832 1234 832 2304 curveto 832 3374 1487 4087 2458 4087 curveto end_ol grestore gsave 13.775800 20.650000 translate 0.035278 -0.035278 scale start_ol 1088 1612 moveto 1842 2364 lineto 3470 0 lineto 4167 0 lineto 2266 2731 lineto 4148 4608 lineto 3388 4608 lineto 1088 2276 lineto 1088 4608 lineto 512 4608 lineto 512 0 lineto 1088 0 lineto 1088 1612 lineto end_ol grestore gsave 9.650000 15.850000 translate 0.035278 -0.035278 scale start_ol 1664 3207 moveto 1664 0 lineto 2176 0 lineto 2176 4503 lineto 1839 4503 lineto 1645 3811 1520 3716 669 3608 curveto 669 3207 lineto 1664 3207 lineto end_ol grestore gsave 10.115667 15.850000 translate 0.035278 -0.035278 scale start_ol 1216 661 moveto 576 661 lineto 576 0 lineto 1216 0 lineto 1216 661 lineto end_ol grestore gsave 10.352733 15.850000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 10.589800 15.850000 translate 0.035278 -0.035278 scale start_ol 1152 1984 moveto 2731 1984 lineto 3277 1984 3520 1731 3520 1163 curveto 3520 752 lineto 3520 468 3557 190 3616 0 curveto 4160 0 lineto 4160 145 lineto 4160 297 4160 461 4160 1074 curveto 4160 1830 4024 2057 3461 2271 curveto 3959 2524 4160 2847 4160 3373 curveto 4160 4171 3653 4608 2734 4608 curveto 576 4608 lineto 576 0 lineto 1152 0 lineto 1152 1984 lineto 1152 2496 moveto 1152 4087 lineto 2600 4087 lineto 2934 4087 3127 4036 3275 3910 curveto 3436 3776 3520 3567 3520 3288 curveto 3520 2743 3237 2496 2600 2496 curveto 1152 2496 lineto end_ol grestore gsave 11.190933 15.850000 translate 0.035278 -0.035278 scale start_ol 1152 2112 moveto 3667 2112 lineto 3667 2624 lineto 1152 2624 lineto 1152 4087 lineto 3762 4087 lineto 3762 4608 lineto 576 4608 lineto 576 0 lineto 3876 0 lineto 3876 521 lineto 1152 521 lineto 1152 2112 lineto end_ol grestore gsave 11.749733 15.850000 translate 0.035278 -0.035278 scale start_ol 4480 2432 moveto 2547 2432 lineto 2547 1920 lineto 3968 1920 lineto 3968 1805 lineto 3968 1060 3354 521 2502 521 curveto 2029 521 1600 681 1325 961 curveto 1018 1270 832 1787 832 2322 curveto 832 3386 1485 4087 2471 4087 curveto 3181 4087 3693 3743 3821 3175 curveto 4418 3175 lineto 4253 4085 3541 4608 2480 4608 curveto 1914 4608 1457 4469 1095 4186 curveto 561 3764 256 3082 256 2292 curveto 256 941 1128 0 2383 0 curveto 3013 0 3516 228 3968 716 curveto 4112 0 lineto 4480 0 lineto 4480 2432 lineto end_ol grestore gsave 12.401667 15.850000 translate 0.035278 -0.035278 scale start_ol 1216 4608 moveto 640 4608 lineto 640 0 lineto 1216 0 lineto 1216 4608 lineto end_ol grestore gsave 12.638733 15.850000 translate 0.035278 -0.035278 scale start_ol 3904 3233 moveto 3904 4103 3259 4608 2153 4608 curveto 1100 4608 448 4117 448 3326 curveto 448 2792 744 2457 1347 2307 curveto 2485 2019 lineto 3064 1875 3328 1654 3328 1312 curveto 3328 1078 3205 839 3023 707 curveto 2853 587 2583 521 2238 521 curveto 1774 521 1458 634 1253 884 curveto 1094 1074 1024 1282 1024 1550 curveto 448 1550 lineto 448 1143 527 875 702 632 curveto 1004 219 1511 0 2182 0 curveto 2708 0 3137 120 3421 336 curveto 3717 570 3904 959 3904 1337 curveto 3904 1877 3552 2272 2929 2434 curveto 1779 2728 lineto 1225 2872 1024 3040 1024 3376 curveto 1024 3819 1459 4113 2116 4113 curveto 2893 4113 3328 3798 3328 3233 curveto 3904 3233 lineto end_ol grestore gsave 13.197533 15.850000 translate 0.035278 -0.035278 scale start_ol 2240 4087 moveto 3754 4087 lineto 3754 4608 lineto 143 4608 lineto 143 4087 lineto 1664 4087 lineto 1664 0 lineto 2240 0 lineto 2240 4087 lineto end_ol grestore gsave 13.705533 15.850000 translate 0.035278 -0.035278 scale start_ol 1152 2112 moveto 3667 2112 lineto 3667 2624 lineto 1152 2624 lineto 1152 4087 lineto 3762 4087 lineto 3762 4608 lineto 576 4608 lineto 576 0 lineto 3876 0 lineto 3876 521 lineto 1152 521 lineto 1152 2112 lineto end_ol grestore gsave 14.264333 15.850000 translate 0.035278 -0.035278 scale start_ol 1152 1984 moveto 2731 1984 lineto 3277 1984 3520 1731 3520 1163 curveto 3520 752 lineto 3520 468 3557 190 3616 0 curveto 4160 0 lineto 4160 145 lineto 4160 297 4160 461 4160 1074 curveto 4160 1830 4024 2057 3461 2271 curveto 3959 2524 4160 2847 4160 3373 curveto 4160 4171 3653 4608 2734 4608 curveto 576 4608 lineto 576 0 lineto 1152 0 lineto 1152 1984 lineto 1152 2496 moveto 1152 4087 lineto 2600 4087 lineto 2934 4087 3127 4036 3275 3910 curveto 3436 3776 3520 3567 3520 3288 curveto 3520 2743 3237 2496 2600 2496 curveto 1152 2496 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 18.800000 14.400000 m 18.800000 11.450000 l s 0 slj n 19.200000 11.450000 m 18.800000 10.650000 l 18.400000 11.450000 l f gsave 14.700000 13.000000 translate 0.035278 -0.035278 scale start_ol 3200 553 moveto 863 553 lineto 919 907 1116 1133 1654 1445 curveto 2273 1775 lineto 2885 2104 3200 2550 3200 3082 curveto 3200 3442 3051 3778 2790 4010 curveto 2529 4242 2206 4352 1790 4352 curveto 1231 4352 815 4155 573 3785 curveto 418 3551 349 3280 337 2837 curveto 895 2837 lineto 914 3128 952 3304 1028 3444 curveto 1174 3705 1465 3863 1801 3863 curveto 2308 3863 2688 3515 2688 3050 curveto 2688 2708 2479 2414 2080 2194 curveto 1497 1876 lineto 559 1363 287 954 236 0 curveto 3200 0 lineto 3200 553 lineto end_ol grestore gsave 15.165667 13.000000 translate 0.035278 -0.035278 scale start_ol 1216 661 moveto 576 661 lineto 576 0 lineto 1216 0 lineto 1216 661 lineto end_ol grestore gsave 15.402733 13.000000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 15.639800 13.000000 translate 0.035278 -0.035278 scale start_ol 3904 3233 moveto 3904 4103 3259 4608 2153 4608 curveto 1100 4608 448 4117 448 3326 curveto 448 2792 744 2457 1347 2307 curveto 2485 2019 lineto 3064 1875 3328 1654 3328 1312 curveto 3328 1078 3205 839 3023 707 curveto 2853 587 2583 521 2238 521 curveto 1774 521 1458 634 1253 884 curveto 1094 1074 1024 1282 1024 1550 curveto 448 1550 lineto 448 1143 527 875 702 632 curveto 1004 219 1511 0 2182 0 curveto 2708 0 3137 120 3421 336 curveto 3717 570 3904 959 3904 1337 curveto 3904 1877 3552 2272 2929 2434 curveto 1779 2728 lineto 1225 2872 1024 3040 1024 3376 curveto 1024 3819 1459 4113 2116 4113 curveto 2893 4113 3328 3798 3328 3233 curveto 3904 3233 lineto end_ol grestore gsave 16.198600 13.000000 translate 0.035278 -0.035278 scale start_ol 2240 4087 moveto 3754 4087 lineto 3754 4608 lineto 143 4608 lineto 143 4087 lineto 1664 4087 lineto 1664 0 lineto 2240 0 lineto 2240 4087 lineto end_ol grestore gsave 16.706600 13.000000 translate 0.035278 -0.035278 scale start_ol 2458 4608 moveto 1147 4608 256 3679 256 2304 curveto 256 929 1147 0 2464 0 curveto 3016 0 3512 163 3882 464 curveto 4377 869 4672 1550 4672 2268 curveto 4672 3685 3800 4608 2458 4608 curveto 2458 4087 moveto 3453 4087 4096 3374 4096 2280 curveto 4096 1234 3434 521 2464 521 curveto 1487 521 832 1234 832 2304 curveto 832 3374 1487 4087 2458 4087 curveto end_ol grestore gsave 17.358533 13.000000 translate 0.035278 -0.035278 scale start_ol 1152 1984 moveto 2731 1984 lineto 3277 1984 3520 1731 3520 1163 curveto 3520 752 lineto 3520 468 3557 190 3616 0 curveto 4160 0 lineto 4160 145 lineto 4160 297 4160 461 4160 1074 curveto 4160 1830 4024 2057 3461 2271 curveto 3959 2524 4160 2847 4160 3373 curveto 4160 4171 3653 4608 2734 4608 curveto 576 4608 lineto 576 0 lineto 1152 0 lineto 1152 1984 lineto 1152 2496 moveto 1152 4087 lineto 2600 4087 lineto 2934 4087 3127 4036 3275 3910 curveto 3436 3776 3520 3567 3520 3288 curveto 3520 2743 3237 2496 2600 2496 curveto 1152 2496 lineto end_ol grestore gsave 17.959667 13.000000 translate 0.035278 -0.035278 scale start_ol 1152 2112 moveto 3667 2112 lineto 3667 2624 lineto 1152 2624 lineto 1152 4087 lineto 3762 4087 lineto 3762 4608 lineto 576 4608 lineto 576 0 lineto 3876 0 lineto 3876 521 lineto 1152 521 lineto 1152 2112 lineto end_ol grestore gsave 15.400000 5.550000 translate 0.035278 -0.035278 scale start_ol 1088 4608 moveto 512 4608 lineto 512 0 lineto 3369 0 lineto 3369 521 lineto 1088 521 lineto 1088 4608 lineto end_ol grestore gsave 15.865667 5.550000 translate 0.035278 -0.035278 scale start_ol 1722 3328 moveto 809 3328 256 2706 256 1664 curveto 256 622 803 0 1728 0 curveto 2641 0 3200 622 3200 1640 curveto 3200 2712 2660 3328 1722 3328 curveto 1728 2839 moveto 2330 2839 2688 2395 2688 1647 curveto 2688 938 2317 489 1728 489 curveto 1133 489 768 933 768 1664 curveto 768 2390 1133 2839 1728 2839 curveto end_ol grestore gsave 16.331333 5.550000 translate 0.035278 -0.035278 scale start_ol 2941 2182 moveto 2916 2488 2847 2686 2722 2860 curveto 2497 3154 2104 3328 1648 3328 curveto 767 3328 192 2665 192 1634 curveto 192 634 754 0 1642 0 curveto 2423 0 2917 449 2979 1215 curveto 2446 1215 lineto 2357 731 2085 489 1635 489 curveto 1052 489 704 921 704 1635 curveto 704 2390 1046 2839 1623 2839 curveto 2066 2839 2345 2603 2408 2182 curveto 2941 2182 lineto end_ol grestore gsave 16.754667 5.550000 translate 0.035278 -0.035278 scale start_ol 3407 458 moveto 3350 464 3325 464 3293 464 curveto 3109 464 3008 544 3008 683 curveto 3008 2459 lineto 3008 3024 2546 3328 1669 3328 curveto 1153 3328 725 3195 487 2960 curveto 324 2797 256 2617 256 2303 curveto 901 2303 lineto 948 2672 1200 2839 1718 2839 curveto 2217 2839 2496 2675 2496 2382 curveto 2496 2254 lineto 2496 2049 2360 1961 1934 1914 curveto 1173 1826 1056 1803 850 1727 curveto 456 1580 256 1305 256 907 curveto 256 351 682 0 1366 0 curveto 1792 0 2134 145 2515 487 curveto 2552 152 2715 0 3051 0 curveto 3157 0 3239 13 3407 60 curveto 3407 458 lineto 2496 1127 moveto 2496 966 2443 868 2277 735 curveto 2051 556 1778 464 1453 464 curveto 1021 464 768 643 768 948 curveto 768 1265 1014 1427 1605 1502 curveto 2190 1571 2310 1594 2496 1669 curveto 2496 1127 lineto end_ol grestore gsave 17.220333 5.550000 translate 0.035278 -0.035278 scale start_ol 1569 3328 moveto 1024 3328 lineto 1024 4243 lineto 512 4243 lineto 512 3328 lineto 62 3328 lineto 62 2896 lineto 512 2896 lineto 512 518 lineto 512 187 737 0 1144 0 curveto 1269 0 1394 13 1569 44 curveto 1569 482 lineto 1499 464 1417 464 1316 464 curveto 1087 464 1024 524 1024 746 curveto 1024 2896 lineto 1569 2896 lineto 1569 3328 lineto end_ol grestore gsave 17.457400 5.550000 translate 0.035278 -0.035278 scale start_ol 960 3309 moveto 448 3309 lineto 448 0 lineto 960 0 lineto 960 3309 lineto 960 4608 moveto 448 4608 lineto 448 3941 lineto 960 3941 lineto 960 4608 lineto end_ol grestore gsave 17.643667 5.550000 translate 0.035278 -0.035278 scale start_ol 1722 3328 moveto 809 3328 256 2706 256 1664 curveto 256 622 803 0 1728 0 curveto 2641 0 3200 622 3200 1640 curveto 3200 2712 2660 3328 1722 3328 curveto 1728 2839 moveto 2330 2839 2688 2395 2688 1647 curveto 2688 938 2317 489 1728 489 curveto 1133 489 768 933 768 1664 curveto 768 2390 1133 2839 1728 2839 curveto end_ol grestore gsave 18.109333 5.550000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1776 lineto 960 2434 1318 2864 1869 2864 curveto 2291 2864 2560 2618 2560 2231 curveto 2560 0 lineto 3072 0 lineto 3072 2445 lineto 3072 2982 2668 3328 2041 3328 curveto 1556 3328 1246 3139 960 2680 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 18.575000 5.550000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 18.812067 5.550000 translate 0.035278 -0.035278 scale start_ol 576 0 moveto 2350 0 lineto 3511 0 4224 872 4224 2307 curveto 4224 3736 3517 4608 2350 4608 curveto 576 4608 lineto 576 0 lineto 1152 521 moveto 1152 4087 lineto 2247 4087 lineto 3164 4087 3648 3475 3648 2301 curveto 3648 1140 3164 521 2247 521 curveto 1152 521 lineto end_ol grestore gsave 19.413200 5.550000 translate 0.035278 -0.035278 scale start_ol 3407 458 moveto 3350 464 3325 464 3293 464 curveto 3109 464 3008 544 3008 683 curveto 3008 2459 lineto 3008 3024 2546 3328 1669 3328 curveto 1153 3328 725 3195 487 2960 curveto 324 2797 256 2617 256 2303 curveto 901 2303 lineto 948 2672 1200 2839 1718 2839 curveto 2217 2839 2496 2675 2496 2382 curveto 2496 2254 lineto 2496 2049 2360 1961 1934 1914 curveto 1173 1826 1056 1803 850 1727 curveto 456 1580 256 1305 256 907 curveto 256 351 682 0 1366 0 curveto 1792 0 2134 145 2515 487 curveto 2552 152 2715 0 3051 0 curveto 3157 0 3239 13 3407 60 curveto 3407 458 lineto 2496 1127 moveto 2496 966 2443 868 2277 735 curveto 2051 556 1778 464 1453 464 curveto 1021 464 768 643 768 948 curveto 768 1265 1014 1427 1605 1502 curveto 2190 1571 2310 1594 2496 1669 curveto 2496 1127 lineto end_ol grestore gsave 19.878867 5.550000 translate 0.035278 -0.035278 scale start_ol 1569 3328 moveto 1024 3328 lineto 1024 4243 lineto 512 4243 lineto 512 3328 lineto 62 3328 lineto 62 2896 lineto 512 2896 lineto 512 518 lineto 512 187 737 0 1144 0 curveto 1269 0 1394 13 1569 44 curveto 1569 482 lineto 1499 464 1417 464 1316 464 curveto 1087 464 1024 524 1024 746 curveto 1024 2896 lineto 1569 2896 lineto 1569 3328 lineto end_ol grestore gsave 20.115933 5.550000 translate 0.035278 -0.035278 scale start_ol 3407 458 moveto 3350 464 3325 464 3293 464 curveto 3109 464 3008 544 3008 683 curveto 3008 2459 lineto 3008 3024 2546 3328 1669 3328 curveto 1153 3328 725 3195 487 2960 curveto 324 2797 256 2617 256 2303 curveto 901 2303 lineto 948 2672 1200 2839 1718 2839 curveto 2217 2839 2496 2675 2496 2382 curveto 2496 2254 lineto 2496 2049 2360 1961 1934 1914 curveto 1173 1826 1056 1803 850 1727 curveto 456 1580 256 1305 256 907 curveto 256 351 682 0 1366 0 curveto 1792 0 2134 145 2515 487 curveto 2552 152 2715 0 3051 0 curveto 3157 0 3239 13 3407 60 curveto 3407 458 lineto 2496 1127 moveto 2496 966 2443 868 2277 735 curveto 2051 556 1778 464 1453 464 curveto 1021 464 768 643 768 948 curveto 768 1265 1014 1427 1605 1502 curveto 2190 1571 2310 1594 2496 1669 curveto 2496 1127 lineto end_ol grestore gsave 20.581600 5.550000 translate 0.035278 -0.035278 scale start_ol 320 4608 moveto 320 0 lineto 832 0 lineto 832 564 lineto 1079 182 1406 0 1857 0 curveto 2708 0 3264 669 3264 1700 curveto 3264 2706 2722 3328 1853 3328 curveto 1399 3328 1078 3158 832 2787 curveto 832 4608 lineto 320 4608 lineto 1770 2833 moveto 2367 2833 2752 2367 2752 1647 curveto 2752 961 2354 495 1770 495 curveto 1204 495 832 956 832 1664 curveto 832 2372 1204 2833 1770 2833 curveto end_ol grestore gsave 21.047267 5.550000 translate 0.035278 -0.035278 scale start_ol 3407 458 moveto 3350 464 3325 464 3293 464 curveto 3109 464 3008 544 3008 683 curveto 3008 2459 lineto 3008 3024 2546 3328 1669 3328 curveto 1153 3328 725 3195 487 2960 curveto 324 2797 256 2617 256 2303 curveto 901 2303 lineto 948 2672 1200 2839 1718 2839 curveto 2217 2839 2496 2675 2496 2382 curveto 2496 2254 lineto 2496 2049 2360 1961 1934 1914 curveto 1173 1826 1056 1803 850 1727 curveto 456 1580 256 1305 256 907 curveto 256 351 682 0 1366 0 curveto 1792 0 2134 145 2515 487 curveto 2552 152 2715 0 3051 0 curveto 3157 0 3239 13 3407 60 curveto 3407 458 lineto 2496 1127 moveto 2496 966 2443 868 2277 735 curveto 2051 556 1778 464 1453 464 curveto 1021 464 768 643 768 948 curveto 768 1265 1014 1427 1605 1502 curveto 2190 1571 2310 1594 2496 1669 curveto 2496 1127 lineto end_ol grestore gsave 21.512933 5.550000 translate 0.035278 -0.035278 scale start_ol 2880 2355 moveto 2880 2977 2434 3328 1639 3328 curveto 839 3328 320 2959 320 2391 curveto 320 1911 585 1683 1366 1508 curveto 1858 1396 lineto 2223 1314 2368 1191 2368 969 curveto 2368 682 2053 489 1584 489 curveto 1295 489 1050 564 915 691 curveto 832 777 793 864 761 1077 curveto 320 1077 lineto 320 349 733 0 1567 0 curveto 2369 0 2880 380 2880 972 curveto 2880 1428 2603 1680 1947 1826 curveto 1443 1938 lineto 1015 2031 832 2160 832 2377 curveto 832 2658 1141 2839 1629 2839 curveto 2110 2839 2368 2672 2368 2355 curveto 2880 2355 lineto end_ol grestore gsave 21.936267 5.550000 translate 0.035278 -0.035278 scale start_ol 3250 1472 moveto 3250 1959 3212 2251 3117 2488 curveto 2902 3012 2395 3328 1774 3328 curveto 849 3328 253 2650 253 1610 curveto 253 614 830 0 1761 0 curveto 2522 0 3048 397 3181 1062 curveto 2648 1062 lineto 2503 685 2205 489 1780 489 curveto 1445 489 1159 620 982 860 curveto 855 1024 811 1188 805 1472 curveto 3250 1472 lineto 817 1920 moveto 862 2477 1236 2839 1768 2839 curveto 2287 2839 2686 2448 2686 1954 curveto 2686 1943 2686 1932 2680 1920 curveto 817 1920 lineto end_ol grestore gsave 17.250000 23.050000 translate 0.035278 -0.035278 scale start_ol 1152 1984 moveto 2731 1984 lineto 3277 1984 3520 1731 3520 1163 curveto 3520 752 lineto 3520 468 3557 190 3616 0 curveto 4160 0 lineto 4160 145 lineto 4160 297 4160 461 4160 1074 curveto 4160 1830 4024 2057 3461 2271 curveto 3959 2524 4160 2847 4160 3373 curveto 4160 4171 3653 4608 2734 4608 curveto 576 4608 lineto 576 0 lineto 1152 0 lineto 1152 1984 lineto 1152 2496 moveto 1152 4087 lineto 2600 4087 lineto 2934 4087 3127 4036 3275 3910 curveto 3436 3776 3520 3567 3520 3288 curveto 3520 2743 3237 2496 2600 2496 curveto 1152 2496 lineto end_ol grestore gsave 17.851133 23.050000 translate 0.035278 -0.035278 scale start_ol 3250 1472 moveto 3250 1959 3212 2251 3117 2488 curveto 2902 3012 2395 3328 1774 3328 curveto 849 3328 253 2650 253 1610 curveto 253 614 830 0 1761 0 curveto 2522 0 3048 397 3181 1062 curveto 2648 1062 lineto 2503 685 2205 489 1780 489 curveto 1445 489 1159 620 982 860 curveto 855 1024 811 1188 805 1472 curveto 3250 1472 lineto 817 1920 moveto 862 2477 1236 2839 1768 2839 curveto 2287 2839 2686 2448 2686 1954 curveto 2686 1943 2686 1932 2680 1920 curveto 817 1920 lineto end_ol grestore gsave 18.316800 23.050000 translate 0.035278 -0.035278 scale start_ol 2624 3328 moveto 2624 2758 lineto 2357 3146 2033 3328 1608 3328 curveto 763 3328 192 2641 192 1634 curveto 192 1125 341 711 620 415 curveto 873 154 1236 0 1593 0 curveto 2021 0 2319 175 2624 587 curveto 2624 396 lineto 2624 -105 2564 -409 2424 -614 curveto 2277 -833 1991 -960 1651 -960 curveto 1397 -960 1171 -883 1017 -745 curveto 891 -630 837 -522 804 -284 curveto 292 -284 lineto 350 -988 844 -1408 1634 -1408 curveto 2135 -1408 2565 -1248 2783 -980 curveto 3040 -674 3136 -253 3136 532 curveto 3136 3328 lineto 2624 3328 lineto 1671 2839 moveto 2271 2839 2624 2401 2624 1647 curveto 2624 927 2264 489 1677 489 curveto 1071 489 704 933 704 1664 curveto 704 2390 1077 2839 1671 2839 curveto end_ol grestore gsave 18.782467 23.050000 translate 0.035278 -0.035278 scale start_ol 960 3309 moveto 448 3309 lineto 448 0 lineto 960 0 lineto 960 3309 lineto 960 4608 moveto 448 4608 lineto 448 3941 lineto 960 3941 lineto 960 4608 lineto end_ol grestore gsave 18.968733 23.050000 translate 0.035278 -0.035278 scale start_ol 2880 2355 moveto 2880 2977 2434 3328 1639 3328 curveto 839 3328 320 2959 320 2391 curveto 320 1911 585 1683 1366 1508 curveto 1858 1396 lineto 2223 1314 2368 1191 2368 969 curveto 2368 682 2053 489 1584 489 curveto 1295 489 1050 564 915 691 curveto 832 777 793 864 761 1077 curveto 320 1077 lineto 320 349 733 0 1567 0 curveto 2369 0 2880 380 2880 972 curveto 2880 1428 2603 1680 1947 1826 curveto 1443 1938 lineto 1015 2031 832 2160 832 2377 curveto 832 2658 1141 2839 1629 2839 curveto 2110 2839 2368 2672 2368 2355 curveto 2880 2355 lineto end_ol grestore gsave 19.392067 23.050000 translate 0.035278 -0.035278 scale start_ol 1569 3328 moveto 1024 3328 lineto 1024 4243 lineto 512 4243 lineto 512 3328 lineto 62 3328 lineto 62 2896 lineto 512 2896 lineto 512 518 lineto 512 187 737 0 1144 0 curveto 1269 0 1394 13 1569 44 curveto 1569 482 lineto 1499 464 1417 464 1316 464 curveto 1087 464 1024 524 1024 746 curveto 1024 2896 lineto 1569 2896 lineto 1569 3328 lineto end_ol grestore gsave 19.629133 23.050000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1728 lineto 960 2204 1080 2515 1334 2699 curveto 1498 2820 1657 2858 2024 2864 curveto 2024 3328 lineto 1939 3328 1896 3328 1829 3328 curveto 1501 3328 1252 3153 960 2725 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 19.908533 23.050000 translate 0.035278 -0.035278 scale start_ol 3407 458 moveto 3350 464 3325 464 3293 464 curveto 3109 464 3008 544 3008 683 curveto 3008 2459 lineto 3008 3024 2546 3328 1669 3328 curveto 1153 3328 725 3195 487 2960 curveto 324 2797 256 2617 256 2303 curveto 901 2303 lineto 948 2672 1200 2839 1718 2839 curveto 2217 2839 2496 2675 2496 2382 curveto 2496 2254 lineto 2496 2049 2360 1961 1934 1914 curveto 1173 1826 1056 1803 850 1727 curveto 456 1580 256 1305 256 907 curveto 256 351 682 0 1366 0 curveto 1792 0 2134 145 2515 487 curveto 2552 152 2715 0 3051 0 curveto 3157 0 3239 13 3407 60 curveto 3407 458 lineto 2496 1127 moveto 2496 966 2443 868 2277 735 curveto 2051 556 1778 464 1453 464 curveto 1021 464 768 643 768 948 curveto 768 1265 1014 1427 1605 1502 curveto 2190 1571 2310 1594 2496 1669 curveto 2496 1127 lineto end_ol grestore gsave 20.374200 23.050000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1728 lineto 960 2204 1080 2515 1334 2699 curveto 1498 2820 1657 2858 2024 2864 curveto 2024 3328 lineto 1939 3328 1896 3328 1829 3328 curveto 1501 3328 1252 3153 960 2725 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 3.050000 16.200000 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore gsave 3.354800 16.200000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 3.490267 16.200000 translate 0.035278 -0.035278 scale start_ol 256 -1024 moveto 640 -1024 lineto 640 362 lineto 837 111 1057 0 1363 0 curveto 1969 0 2368 472 2368 1194 curveto 2368 1956 1989 2432 1376 2432 curveto 1063 2432 812 2286 640 2003 curveto 640 2432 lineto 256 2432 lineto 256 -1024 lineto 1298 2070 moveto 1713 2070 1984 1729 1984 1203 curveto 1984 703 1709 362 1298 362 curveto 902 362 640 699 640 1216 curveto 640 1733 902 2070 1298 2070 curveto end_ol grestore gsave 3.828933 16.200000 translate 0.035278 -0.035278 scale start_ol 960 483 moveto 512 483 lineto 512 0 lineto 960 0 lineto 960 483 lineto 960 2432 moveto 512 2432 lineto 512 1949 lineto 960 1949 lineto 960 2432 lineto end_ol grestore gsave 3.998267 16.200000 translate 0.035278 -0.035278 scale start_ol 320 2443 moveto 320 -326 lineto 320 -563 242 -640 6 -640 curveto -8 -640 -8 -640 -86 -640 curveto -86 -1008 lineto -40 -1019 -17 -1024 43 -1024 curveto 478 -1024 704 -846 704 -515 curveto 704 2443 lineto 320 2443 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 4.133733 16.200000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 4.472400 16.200000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 4.811067 16.200000 translate 0.035278 -0.035278 scale start_ol 3044 2432 moveto 2942 2036 lineto 2828 2295 2637 2432 2386 2432 curveto 1776 2432 1216 1826 1216 1160 curveto 1216 683 1533 320 1955 320 curveto 2195 320 2390 447 2598 734 curveto 2639 460 2814 320 3103 320 curveto 3443 320 3723 450 3957 720 curveto 4210 1007 4352 1371 4352 1721 curveto 4352 2633 3463 3392 2390 3392 curveto 1842 3392 1261 3168 856 2803 curveto 404 2397 128 1785 128 1187 curveto 128 173 1062 -640 2231 -640 curveto 2573 -640 2943 -565 3276 -431 curveto 3146 -105 lineto 2766 -214 2501 -256 2251 -256 curveto 1268 -256 512 408 512 1270 curveto 512 2218 1356 3035 2335 3035 curveto 3221 3035 3968 2427 3968 1702 curveto 3968 1195 3583 704 3188 704 curveto 3063 704 2956 790 2956 894 curveto 2956 936 2975 1020 3012 1127 curveto 3429 2432 lineto 3044 2432 lineto 2348 2135 moveto 2608 2135 2798 1953 2784 1720 curveto 2775 1491 2649 1107 2533 937 curveto 2403 750 2231 640 2060 640 curveto 1804 640 1600 874 1600 1171 curveto 1600 1676 1958 2135 2348 2135 curveto end_ol grestore gsave 5.429133 16.200000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 5.564600 16.200000 translate 0.035278 -0.035278 scale start_ol 256 -1024 moveto 640 -1024 lineto 640 362 lineto 837 111 1057 0 1363 0 curveto 1969 0 2368 472 2368 1194 curveto 2368 1956 1989 2432 1376 2432 curveto 1063 2432 812 2286 640 2003 curveto 640 2432 lineto 256 2432 lineto 256 -1024 lineto 1298 2070 moveto 1713 2070 1984 1729 1984 1203 curveto 1984 703 1709 362 1298 362 curveto 902 362 640 699 640 1216 curveto 640 1733 902 2070 1298 2070 curveto end_ol grestore gsave 5.903267 16.200000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 6.072600 16.200000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 6.411267 16.200000 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 6.546733 16.200000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 6.716067 16.200000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 7.054733 16.200000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 7.257933 16.200000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 3.700000 20.300000 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 4.038667 20.300000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 4.208000 20.300000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 4.546667 20.300000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 4.716000 20.300000 translate 0.035278 -0.035278 scale start_ol 990 1523 moveto 1041 1523 lineto 1211 1527 lineto 1679 1527 1920 1338 1920 972 curveto 1920 590 1661 362 1227 362 curveto 775 362 553 568 525 1011 curveto 119 1011 lineto 137 759 184 596 262 455 curveto 428 155 751 0 1198 0 curveto 1871 0 2304 380 2304 966 curveto 2304 1359 2143 1577 1751 1704 curveto 2034 1818 2176 2036 2176 2347 curveto 2176 2881 1808 3200 1196 3200 curveto 547 3200 202 2856 188 2189 curveto 594 2189 lineto 599 2374 617 2477 668 2572 curveto 760 2740 963 2843 1216 2843 curveto 1575 2843 1792 2645 1792 2322 curveto 1792 2107 1709 1978 1529 1910 curveto 1419 1867 1276 1849 990 1845 curveto 990 1523 lineto end_ol grestore gsave 5.054667 20.300000 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 5.224000 20.300000 translate 0.035278 -0.035278 scale start_ol 1536 768 moveto 1536 0 lineto 1920 0 lineto 1920 768 lineto 2404 768 lineto 2404 1152 lineto 1920 1152 lineto 1920 3287 lineto 1636 3287 lineto 158 1217 lineto 158 768 lineto 1536 768 lineto 1536 1152 moveto 513 1152 lineto 1536 2591 lineto 1536 1152 lineto end_ol grestore gsave 5.562667 20.300000 translate 0.035278 -0.035278 scale start_ol 960 483 moveto 512 483 lineto 512 0 lineto 960 0 lineto 960 483 lineto 960 2432 moveto 512 2432 lineto 512 1949 lineto 960 1949 lineto 960 2432 lineto end_ol grestore gsave 5.732000 20.300000 translate 0.035278 -0.035278 scale start_ol 2146 3200 moveto 478 3200 lineto 233 1509 lineto 607 1509 lineto 796 1719 953 1792 1206 1792 curveto 1644 1792 1920 1514 1920 1064 curveto 1920 627 1648 362 1206 362 curveto 851 362 635 529 538 871 curveto 132 871 lineto 187 615 232 491 327 376 curveto 509 137 836 0 1200 0 curveto 1850 0 2304 462 2304 1128 curveto 2304 1750 1883 2176 1268 2176 curveto 1042 2176 861 2112 676 1964 curveto 805 2796 lineto 2146 2796 lineto 2146 3200 lineto end_ol grestore gsave 6.070667 20.300000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 6.409333 20.300000 translate 0.035278 -0.035278 scale start_ol 2240 2393 moveto 2163 2899 1821 3200 1335 3200 curveto 984 3200 669 3034 480 2758 curveto 282 2457 192 2077 192 1513 curveto 192 992 273 660 462 385 curveto 632 136 911 0 1261 0 curveto 1868 0 2304 441 2304 1055 curveto 2304 1638 1910 2048 1354 2048 curveto 1047 2048 806 1915 640 1660 curveto 640 2418 888 2838 1335 2838 curveto 1610 2838 1800 2676 1862 2393 curveto 2240 2393 lineto 1300 1664 moveto 1682 1664 1920 1415 1920 1013 curveto 1920 637 1651 362 1287 362 curveto 918 362 640 649 640 1034 curveto 640 1406 909 1664 1300 1664 curveto end_ol grestore gsave 6.748000 20.300000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore 1.000000 1.000000 1.000000 srgb n 2.850000 7.000000 m 2.850000 10.600000 l 14.000000 10.600000 l 14.000000 7.000000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slj 0.000000 0.000000 0.000000 srgb n 2.850000 7.000000 m 2.850000 10.600000 l 14.000000 10.600000 l 14.000000 7.000000 l cp s gsave 3.150000 7.800000 translate 0.035278 -0.035278 scale start_ol 1024 1792 moveto 2432 1792 lineto 2919 1792 3136 1564 3136 1050 curveto 3136 679 lineto 3136 422 3169 171 3222 0 curveto 3712 0 lineto 3712 131 lineto 3712 268 3712 416 3712 970 curveto 3712 1648 3590 1851 3083 2043 curveto 3532 2272 3712 2565 3712 3041 curveto 3712 3764 3259 4160 2439 4160 curveto 512 4160 lineto 512 0 lineto 1024 0 lineto 1024 1792 lineto 1024 2240 moveto 1024 3689 lineto 2315 3689 lineto 2614 3689 2786 3643 2918 3527 curveto 3061 3406 3136 3216 3136 2962 curveto 3136 2465 2883 2240 2315 2240 curveto 1024 2240 lineto end_ol grestore gsave 3.700333 7.800000 translate 0.035278 -0.035278 scale start_ol 2896 1344 moveto 2896 1780 2862 2042 2777 2255 curveto 2585 2724 2134 3008 1581 3008 curveto 756 3008 226 2399 226 1463 curveto 226 558 739 0 1569 0 curveto 2247 0 2715 362 2834 968 curveto 2360 968 lineto 2230 622 1964 442 1586 442 curveto 1287 442 1033 562 875 783 curveto 762 933 723 1083 717 1344 curveto 2896 1344 lineto 728 1728 moveto 768 2236 1101 2566 1575 2566 curveto 2038 2566 2393 2210 2393 1759 curveto 2393 1749 2393 1739 2388 1728 curveto 728 1728 lineto end_ol grestore gsave 4.123667 7.800000 translate 0.035278 -0.035278 scale start_ol 2697 1973 moveto 2674 2249 2612 2428 2498 2585 curveto 2293 2851 1934 3008 1519 3008 curveto 716 3008 192 2409 192 1477 curveto 192 573 704 0 1512 0 curveto 2224 0 2673 406 2730 1098 curveto 2256 1098 lineto 2177 661 1934 442 1534 442 curveto 1014 442 704 832 704 1478 curveto 704 2160 1009 2566 1522 2566 curveto 1917 2566 2166 2353 2222 1973 curveto 2697 1973 lineto end_ol grestore gsave 4.504667 7.800000 translate 0.035278 -0.035278 scale start_ol 1530 3008 moveto 697 3008 192 2446 192 1504 curveto 192 562 691 0 1536 0 curveto 2370 0 2880 562 2880 1483 curveto 2880 2451 2387 3008 1530 3008 curveto 1536 2566 moveto 2057 2566 2368 2165 2368 1488 curveto 2368 848 2046 442 1536 442 curveto 1020 442 704 843 704 1504 curveto 704 2160 1020 2566 1536 2566 curveto end_ol grestore gsave 4.928000 7.800000 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1561 lineto 896 1992 1003 2273 1229 2440 curveto 1376 2549 1517 2583 1844 2589 curveto 1844 3008 lineto 1763 3008 1723 3008 1659 3008 curveto 1347 3008 1110 2849 832 2463 curveto 832 3008 lineto 384 3008 lineto end_ol grestore gsave 5.182000 7.800000 translate 0.035278 -0.035278 scale start_ol 2816 4160 moveto 2304 4160 lineto 2304 2544 lineto 2107 2848 1791 3008 1396 3008 curveto 630 3008 128 2425 128 1531 curveto 128 583 607 0 1384 0 curveto 1781 0 2056 153 2304 521 curveto 2304 0 lineto 2816 0 lineto 2816 4160 lineto 1486 2560 moveto 1987 2560 2304 2144 2304 1494 curveto 2304 864 1981 448 1491 448 curveto 979 448 640 869 640 1504 curveto 640 2139 979 2560 1486 2560 curveto end_ol grestore gsave 5.605333 7.800000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 5.817000 7.800000 translate 0.035278 -0.035278 scale start_ol 896 2987 moveto 384 2987 lineto 384 0 lineto 896 0 lineto 896 2987 lineto 896 4160 moveto 384 4160 lineto 384 3557 lineto 896 3557 lineto 896 4160 lineto end_ol grestore gsave 5.986333 7.800000 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1606 lineto 896 2200 1211 2589 1696 2589 curveto 2067 2589 2304 2367 2304 2017 curveto 2304 0 lineto 2816 0 lineto 2816 2210 lineto 2816 2695 2437 3008 1847 3008 curveto 1392 3008 1100 2837 832 2422 curveto 832 3008 lineto 384 3008 lineto end_ol grestore gsave 6.409667 7.800000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 6.621333 7.800000 translate 0.035278 -0.035278 scale start_ol 960 4160 moveto 448 4160 lineto 448 0 lineto 2992 0 lineto 2992 471 lineto 960 471 lineto 960 4160 lineto end_ol grestore gsave 7.044667 7.800000 translate 0.035278 -0.035278 scale start_ol 1530 3008 moveto 697 3008 192 2446 192 1504 curveto 192 562 691 0 1536 0 curveto 2370 0 2880 562 2880 1483 curveto 2880 2451 2387 3008 1530 3008 curveto 1536 2566 moveto 2057 2566 2368 2165 2368 1488 curveto 2368 848 2046 442 1536 442 curveto 1020 442 704 843 704 1504 curveto 704 2160 1020 2566 1536 2566 curveto end_ol grestore gsave 7.468000 7.800000 translate 0.035278 -0.035278 scale start_ol 2697 1973 moveto 2674 2249 2612 2428 2498 2585 curveto 2293 2851 1934 3008 1519 3008 curveto 716 3008 192 2409 192 1477 curveto 192 573 704 0 1512 0 curveto 2224 0 2673 406 2730 1098 curveto 2256 1098 lineto 2177 661 1934 442 1534 442 curveto 1014 442 704 832 704 1478 curveto 704 2160 1009 2566 1522 2566 curveto 1917 2566 2166 2353 2222 1973 curveto 2697 1973 lineto end_ol grestore gsave 7.849000 7.800000 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 8.272333 7.800000 translate 0.035278 -0.035278 scale start_ol 1509 3008 moveto 1024 3008 lineto 1024 3835 lineto 512 3835 lineto 512 3008 lineto 111 3008 lineto 111 2618 lineto 512 2618 lineto 512 468 lineto 512 169 724 0 1108 0 curveto 1226 0 1344 11 1509 40 curveto 1509 435 lineto 1447 419 1374 419 1283 419 curveto 1080 419 1024 473 1024 674 curveto 1024 2618 lineto 1509 2618 lineto 1509 3008 lineto end_ol grestore gsave 8.484000 7.800000 translate 0.035278 -0.035278 scale start_ol 896 2987 moveto 384 2987 lineto 384 0 lineto 896 0 lineto 896 2987 lineto 896 4160 moveto 384 4160 lineto 384 3557 lineto 896 3557 lineto 896 4160 lineto end_ol grestore gsave 8.653333 7.800000 translate 0.035278 -0.035278 scale start_ol 1530 3008 moveto 697 3008 192 2446 192 1504 curveto 192 562 691 0 1536 0 curveto 2370 0 2880 562 2880 1483 curveto 2880 2451 2387 3008 1530 3008 curveto 1536 2566 moveto 2057 2566 2368 2165 2368 1488 curveto 2368 848 2046 442 1536 442 curveto 1020 442 704 843 704 1504 curveto 704 2160 1020 2566 1536 2566 curveto end_ol grestore gsave 9.076667 7.800000 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1606 lineto 896 2200 1211 2589 1696 2589 curveto 2067 2589 2304 2367 2304 2017 curveto 2304 0 lineto 2816 0 lineto 2816 2210 lineto 2816 2695 2437 3008 1847 3008 curveto 1392 3008 1100 2837 832 2422 curveto 832 3008 lineto 384 3008 lineto end_ol grestore gsave 9.500000 7.800000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 9.711667 7.800000 translate 0.035278 -0.035278 scale start_ol 512 0 moveto 2099 0 lineto 3138 0 3776 787 3776 2083 curveto 3776 3373 3144 4160 2099 4160 curveto 512 4160 lineto 512 0 lineto 1024 471 moveto 1024 3689 lineto 2007 3689 lineto 2830 3689 3264 3137 3264 2077 curveto 3264 1029 2830 471 2007 471 curveto 1024 471 lineto end_ol grestore gsave 10.262000 7.800000 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 10.685333 7.800000 translate 0.035278 -0.035278 scale start_ol 1509 3008 moveto 1024 3008 lineto 1024 3835 lineto 512 3835 lineto 512 3008 lineto 111 3008 lineto 111 2618 lineto 512 2618 lineto 512 468 lineto 512 169 724 0 1108 0 curveto 1226 0 1344 11 1509 40 curveto 1509 435 lineto 1447 419 1374 419 1283 419 curveto 1080 419 1024 473 1024 674 curveto 1024 2618 lineto 1509 2618 lineto 1509 3008 lineto end_ol grestore gsave 10.897000 7.800000 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 11.320333 7.800000 translate 0.035278 -0.035278 scale start_ol 320 4160 moveto 320 0 lineto 832 0 lineto 832 510 lineto 1046 164 1331 0 1722 0 curveto 2462 0 2944 605 2944 1536 curveto 2944 2446 2473 3008 1718 3008 curveto 1324 3008 1045 2854 832 2518 curveto 832 4160 lineto 320 4160 lineto 1613 2560 moveto 2111 2560 2432 2139 2432 1488 curveto 2432 869 2100 448 1613 448 curveto 1142 448 832 864 832 1504 curveto 832 2144 1142 2560 1613 2560 curveto end_ol grestore gsave 11.743667 7.800000 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 12.167000 7.800000 translate 0.035278 -0.035278 scale start_ol 2624 2129 moveto 2624 2691 2211 3008 1476 3008 curveto 736 3008 256 2675 256 2161 curveto 256 1728 496 1521 1204 1363 curveto 1649 1262 lineto 1981 1188 2112 1077 2112 876 curveto 2112 617 1835 442 1421 442 curveto 1167 442 952 510 833 624 curveto 759 702 725 780 697 973 curveto 256 973 lineto 256 315 638 0 1409 0 curveto 2152 0 2624 344 2624 878 curveto 2624 1291 2373 1518 1779 1651 curveto 1322 1751 lineto 934 1836 768 1952 768 2148 curveto 768 2402 1038 2566 1465 2566 curveto 1886 2566 2112 2415 2112 2129 curveto 2624 2129 lineto end_ol grestore gsave 12.548000 7.800000 translate 0.035278 -0.035278 scale start_ol 2896 1344 moveto 2896 1780 2862 2042 2777 2255 curveto 2585 2724 2134 3008 1581 3008 curveto 756 3008 226 2399 226 1463 curveto 226 558 739 0 1569 0 curveto 2247 0 2715 362 2834 968 curveto 2360 968 lineto 2230 622 1964 442 1586 442 curveto 1287 442 1033 562 875 783 curveto 762 933 723 1083 717 1344 curveto 2896 1344 lineto 728 1728 moveto 768 2236 1101 2566 1575 2566 curveto 2038 2566 2393 2210 2393 1759 curveto 2393 1749 2393 1739 2388 1728 curveto 728 1728 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 2.800000 8.150000 m 14.000000 8.150000 l s gsave 3.150000 9.100000 translate 0.035278 -0.035278 scale start_ol 2816 3712 moveto 2816 1197 lineto 2816 716 2452 421 1851 421 curveto 1574 421 1348 485 1168 608 curveto 983 745 896 927 896 1197 curveto 896 3712 lineto 448 3712 lineto 448 1185 lineto 448 454 981 0 1851 0 curveto 2711 0 3264 464 3264 1185 curveto 3264 3712 lineto 2816 3712 lineto end_ol grestore gsave 3.641067 9.100000 translate 0.035278 -0.035278 scale start_ol 2368 1902 moveto 2368 2405 2000 2688 1344 2688 curveto 684 2688 256 2390 256 1932 curveto 256 1544 471 1359 1106 1218 curveto 1505 1128 lineto 1802 1062 1920 962 1920 783 curveto 1920 551 1670 395 1296 395 curveto 1066 395 872 456 765 558 curveto 698 628 668 698 642 870 curveto 256 870 lineto 256 282 597 0 1284 0 curveto 1947 0 2368 307 2368 785 curveto 2368 1154 2143 1357 1610 1475 curveto 1201 1565 lineto 852 1641 704 1745 704 1919 curveto 704 2146 948 2293 1335 2293 curveto 1715 2293 1920 2158 1920 1902 curveto 2368 1902 lineto end_ol grestore gsave 3.988200 9.100000 translate 0.035278 -0.035278 scale start_ol 2606 1216 moveto 2606 1602 2576 1834 2500 2022 curveto 2327 2437 1920 2688 1422 2688 curveto 681 2688 203 2147 203 1315 curveto 203 501 666 0 1412 0 curveto 2022 0 2444 327 2550 874 curveto 2124 874 lineto 2007 559 1768 395 1428 395 curveto 1158 395 930 504 787 705 curveto 686 842 650 979 645 1216 curveto 2606 1216 lineto 655 1536 moveto 691 1995 991 2293 1417 2293 curveto 1834 2293 2154 1971 2154 1564 curveto 2154 1555 2154 1546 2149 1536 curveto 655 1536 lineto end_ol grestore gsave 4.369200 9.100000 translate 0.035278 -0.035278 scale start_ol 320 2688 moveto 320 0 lineto 768 0 lineto 768 1395 lineto 768 1780 864 2032 1068 2181 curveto 1200 2278 1327 2309 1621 2314 curveto 1621 2688 lineto 1548 2688 1511 2688 1453 2688 curveto 1170 2688 956 2546 704 2201 curveto 704 2688 lineto 320 2688 lineto end_ol grestore gsave 4.597800 9.100000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 4.792533 9.100000 translate 0.035278 -0.035278 scale start_ol 2368 1902 moveto 2368 2405 2000 2688 1344 2688 curveto 684 2688 256 2390 256 1932 curveto 256 1544 471 1359 1106 1218 curveto 1505 1128 lineto 1802 1062 1920 962 1920 783 curveto 1920 551 1670 395 1296 395 curveto 1066 395 872 456 765 558 curveto 698 628 668 698 642 870 curveto 256 870 lineto 256 282 597 0 1284 0 curveto 1947 0 2368 307 2368 785 curveto 2368 1154 2143 1357 1610 1475 curveto 1201 1565 lineto 852 1641 704 1745 704 1919 curveto 704 2146 948 2293 1335 2293 curveto 1715 2293 1920 2158 1920 1902 curveto 2368 1902 lineto end_ol grestore gsave 5.139667 9.100000 translate 0.035278 -0.035278 scale start_ol 768 2665 moveto 320 2665 lineto 320 0 lineto 768 0 lineto 768 2665 lineto 768 3712 moveto 320 3712 lineto 320 3173 lineto 768 3173 lineto 768 3712 lineto end_ol grestore gsave 5.292067 9.100000 translate 0.035278 -0.035278 scale start_ol 256 -1142 moveto 704 -1142 lineto 704 400 lineto 931 123 1183 0 1534 0 curveto 2229 0 2688 521 2688 1320 curveto 2688 2162 2239 2688 1512 2688 curveto 1142 2688 844 2527 640 2214 curveto 640 2688 lineto 256 2688 lineto 256 -1142 lineto 1457 2288 moveto 1931 2288 2240 1911 2240 1330 curveto 2240 777 1926 400 1457 400 curveto 1003 400 704 772 704 1344 curveto 704 1916 1003 2288 1457 2288 curveto end_ol grestore gsave 5.673067 9.100000 translate 0.035278 -0.035278 scale start_ol 1088 534 moveto 576 534 lineto 576 0 lineto 1088 0 lineto 1088 534 lineto 1088 2688 moveto 576 2688 lineto 576 2154 lineto 1088 2154 lineto 1088 2688 lineto end_ol grestore gsave 5.867800 9.100000 translate 0.035278 -0.035278 scale start_ol 384 2668 moveto 384 -361 lineto 384 -619 298 -704 39 -704 curveto 23 -704 23 -704 -63 -704 curveto -63 -1072 lineto -11 -1083 16 -1088 84 -1088 curveto 576 -1088 832 -896 832 -536 curveto 832 2668 lineto 384 2668 lineto 832 3712 moveto 384 3712 lineto 384 3173 lineto 832 3173 lineto 832 3712 lineto end_ol grestore gsave 6.020200 9.100000 translate 0.035278 -0.035278 scale start_ol 2752 370 moveto 2706 375 2686 375 2661 375 curveto 2513 375 2432 440 2432 552 curveto 2432 1986 lineto 2432 2443 2056 2688 1342 2688 curveto 922 2688 574 2581 380 2391 curveto 247 2259 192 2113 192 1860 curveto 743 1860 lineto 779 2158 976 2293 1379 2293 curveto 1767 2293 1984 2161 1984 1924 curveto 1984 1820 lineto 1984 1655 1876 1584 1535 1546 curveto 925 1475 832 1456 667 1395 curveto 352 1277 192 1054 192 733 curveto 192 284 533 0 1081 0 curveto 1421 0 1695 118 2000 394 curveto 2032 123 2168 0 2452 0 curveto 2542 0 2610 11 2752 48 curveto 2752 370 lineto 1984 910 moveto 1984 780 1943 701 1813 594 curveto 1638 449 1426 375 1172 375 curveto 836 375 640 519 640 766 curveto 640 1022 831 1152 1291 1213 curveto 1746 1269 1839 1287 1984 1348 curveto 1984 910 lineto end_ol grestore gsave 6.401200 9.100000 translate 0.035278 -0.035278 scale start_ol 384 2688 moveto 384 0 lineto 832 0 lineto 832 1434 lineto 832 1965 1104 2313 1523 2313 curveto 1844 2313 2048 2115 2048 1802 curveto 2048 0 lineto 2496 0 lineto 2496 1975 lineto 2496 2409 2166 2688 1652 2688 curveto 1256 2688 1002 2535 768 2164 curveto 768 2688 lineto 384 2688 lineto end_ol grestore gsave 6.782200 9.100000 translate 0.035278 -0.035278 scale start_ol 3392 2688 moveto 3279 2245 lineto 3151 2535 2937 2688 2656 2688 curveto 1972 2688 1344 2008 1344 1262 curveto 1344 727 1701 320 2174 320 curveto 2445 320 2664 448 2898 739 curveto 2944 461 3140 320 3464 320 curveto 3845 320 4159 464 4421 761 curveto 4704 1079 4864 1480 4864 1867 curveto 4864 2874 3881 3712 2694 3712 curveto 2087 3712 1445 3467 997 3067 curveto 498 2622 192 1952 192 1296 curveto 192 186 1217 -704 2502 -704 curveto 2877 -704 3284 -629 3649 -495 curveto 3505 -152 lineto 3091 -273 2804 -320 2532 -320 curveto 1462 -320 640 413 640 1366 curveto 640 2414 1562 3317 2632 3317 curveto 3600 3317 4416 2636 4416 1823 curveto 4416 1254 3989 704 3551 704 curveto 3412 704 3294 804 3294 924 curveto 3294 972 3315 1068 3356 1191 curveto 3819 2688 lineto 3392 2688 lineto 2620 2360 moveto 2908 2360 3119 2150 3104 1880 curveto 3094 1617 2955 1176 2826 982 curveto 2682 766 2492 640 2301 640 curveto 2018 640 1792 909 1792 1251 curveto 1792 1832 2188 2360 2620 2360 curveto end_ol grestore gsave 7.476467 9.100000 translate 0.035278 -0.035278 scale start_ol 768 2665 moveto 320 2665 lineto 320 0 lineto 768 0 lineto 768 2665 lineto 768 3712 moveto 320 3712 lineto 320 3173 lineto 768 3173 lineto 768 3712 lineto end_ol grestore gsave 7.628867 9.100000 translate 0.035278 -0.035278 scale start_ol 256 -1142 moveto 704 -1142 lineto 704 400 lineto 931 123 1183 0 1534 0 curveto 2229 0 2688 521 2688 1320 curveto 2688 2162 2239 2688 1512 2688 curveto 1142 2688 844 2527 640 2214 curveto 640 2688 lineto 256 2688 lineto 256 -1142 lineto 1457 2288 moveto 1931 2288 2240 1911 2240 1330 curveto 2240 777 1926 400 1457 400 curveto 1003 400 704 772 704 1344 curveto 704 1916 1003 2288 1457 2288 curveto end_ol grestore gsave 8.009867 9.100000 translate 0.035278 -0.035278 scale start_ol 1333 2688 moveto 896 2688 lineto 896 3427 lineto 448 3427 lineto 448 2688 lineto 87 2688 lineto 87 2339 lineto 448 2339 lineto 448 419 lineto 448 151 637 0 977 0 curveto 1082 0 1186 10 1333 36 curveto 1333 390 lineto 1277 375 1211 375 1130 375 curveto 947 375 896 423 896 602 curveto 896 2339 lineto 1333 2339 lineto 1333 2688 lineto end_ol grestore gsave 8.204600 9.100000 translate 0.035278 -0.035278 scale start_ol 2606 1216 moveto 2606 1602 2576 1834 2500 2022 curveto 2327 2437 1920 2688 1422 2688 curveto 681 2688 203 2147 203 1315 curveto 203 501 666 0 1412 0 curveto 2022 0 2444 327 2550 874 curveto 2124 874 lineto 2007 559 1768 395 1428 395 curveto 1158 395 930 504 787 705 curveto 686 842 650 979 645 1216 curveto 2606 1216 lineto 655 1536 moveto 691 1995 991 2293 1417 2293 curveto 1834 2293 2154 1971 2154 1564 curveto 2154 1555 2154 1546 2149 1536 curveto 655 1536 lineto end_ol grestore gsave 8.585600 9.100000 translate 0.035278 -0.035278 scale start_ol 768 3712 moveto 320 3712 lineto 320 0 lineto 768 0 lineto 768 3712 lineto end_ol grestore gsave 8.738000 9.100000 translate 0.035278 -0.035278 scale start_ol 960 534 moveto 448 534 lineto 448 0 lineto 960 0 lineto 960 534 lineto end_ol grestore gsave 8.932733 9.100000 translate 0.035278 -0.035278 scale start_ol 1403 2688 moveto 649 2688 192 2186 192 1344 curveto 192 502 644 0 1408 0 curveto 2162 0 2624 502 2624 1325 curveto 2624 2191 2178 2688 1403 2688 curveto 1408 2293 moveto 1889 2293 2176 1935 2176 1330 curveto 2176 758 1879 395 1408 395 curveto 932 395 640 753 640 1344 curveto 640 1930 932 2293 1408 2293 curveto end_ol grestore gsave 9.313733 9.100000 translate 0.035278 -0.035278 scale start_ol 320 2688 moveto 320 0 lineto 768 0 lineto 768 1395 lineto 768 1780 864 2032 1068 2181 curveto 1200 2278 1327 2309 1621 2314 curveto 1621 2688 lineto 1548 2688 1511 2688 1453 2688 curveto 1170 2688 956 2546 704 2201 curveto 704 2688 lineto 320 2688 lineto end_ol grestore gsave 9.542333 9.100000 translate 0.035278 -0.035278 scale start_ol 2112 2688 moveto 2112 2228 lineto 1894 2541 1630 2688 1283 2688 curveto 594 2688 128 2133 128 1320 curveto 128 909 250 574 477 335 curveto 684 124 980 0 1271 0 curveto 1620 0 1863 141 2112 474 curveto 2112 329 lineto 2112 -53 2064 -284 1952 -440 curveto 1835 -607 1605 -704 1333 -704 curveto 1131 -704 949 -648 827 -546 curveto 725 -462 683 -383 656 -208 curveto 216 -208 lineto 262 -759 659 -1088 1292 -1088 curveto 1693 -1088 2038 -961 2213 -747 curveto 2419 -503 2496 -167 2496 459 curveto 2496 2688 lineto 2112 2688 lineto 1349 2293 moveto 1829 2293 2112 1939 2112 1330 curveto 2112 749 1824 395 1355 395 curveto 869 395 576 753 576 1344 curveto 576 1930 875 2293 1349 2293 curveto end_ol grestore gsave 9.923333 9.100000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 10.118067 9.100000 translate 0.035278 -0.035278 scale start_ol 768 2665 moveto 320 2665 lineto 320 0 lineto 768 0 lineto 768 2665 lineto 768 3712 moveto 320 3712 lineto 320 3173 lineto 768 3173 lineto 768 3712 lineto end_ol grestore gsave 10.270467 9.100000 translate 0.035278 -0.035278 scale start_ol 2368 1902 moveto 2368 2405 2000 2688 1344 2688 curveto 684 2688 256 2390 256 1932 curveto 256 1544 471 1359 1106 1218 curveto 1505 1128 lineto 1802 1062 1920 962 1920 783 curveto 1920 551 1670 395 1296 395 curveto 1066 395 872 456 765 558 curveto 698 628 668 698 642 870 curveto 256 870 lineto 256 282 597 0 1284 0 curveto 1947 0 2368 307 2368 785 curveto 2368 1154 2143 1357 1610 1475 curveto 1201 1565 lineto 852 1641 704 1745 704 1919 curveto 704 2146 948 2293 1335 2293 curveto 1715 2293 1920 2158 1920 1902 curveto 2368 1902 lineto end_ol grestore gsave 10.617600 9.100000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 3.150000 10.000000 translate 0.035278 -0.035278 scale start_ol 320 2688 moveto 320 0 lineto 768 0 lineto 768 1395 lineto 768 1780 864 2032 1068 2181 curveto 1200 2278 1327 2309 1621 2314 curveto 1621 2688 lineto 1548 2688 1511 2688 1453 2688 curveto 1170 2688 956 2546 704 2201 curveto 704 2688 lineto 320 2688 lineto end_ol grestore gsave 3.378600 10.000000 translate 0.035278 -0.035278 scale start_ol 2606 1216 moveto 2606 1602 2576 1834 2500 2022 curveto 2327 2437 1920 2688 1422 2688 curveto 681 2688 203 2147 203 1315 curveto 203 501 666 0 1412 0 curveto 2022 0 2444 327 2550 874 curveto 2124 874 lineto 2007 559 1768 395 1428 395 curveto 1158 395 930 504 787 705 curveto 686 842 650 979 645 1216 curveto 2606 1216 lineto 655 1536 moveto 691 1995 991 2293 1417 2293 curveto 1834 2293 2154 1971 2154 1564 curveto 2154 1555 2154 1546 2149 1536 curveto 655 1536 lineto end_ol grestore gsave 3.759600 10.000000 translate 0.035278 -0.035278 scale start_ol 2752 370 moveto 2706 375 2686 375 2661 375 curveto 2513 375 2432 440 2432 552 curveto 2432 1986 lineto 2432 2443 2056 2688 1342 2688 curveto 922 2688 574 2581 380 2391 curveto 247 2259 192 2113 192 1860 curveto 743 1860 lineto 779 2158 976 2293 1379 2293 curveto 1767 2293 1984 2161 1984 1924 curveto 1984 1820 lineto 1984 1655 1876 1584 1535 1546 curveto 925 1475 832 1456 667 1395 curveto 352 1277 192 1054 192 733 curveto 192 284 533 0 1081 0 curveto 1421 0 1695 118 2000 394 curveto 2032 123 2168 0 2452 0 curveto 2542 0 2610 11 2752 48 curveto 2752 370 lineto 1984 910 moveto 1984 780 1943 701 1813 594 curveto 1638 449 1426 375 1172 375 curveto 836 375 640 519 640 766 curveto 640 1022 831 1152 1291 1213 curveto 1746 1269 1839 1287 1984 1348 curveto 1984 910 lineto end_ol grestore gsave 4.140600 10.000000 translate 0.035278 -0.035278 scale start_ol 2369 1763 moveto 2349 2010 2293 2170 2191 2310 curveto 2007 2548 1687 2688 1315 2688 curveto 597 2688 128 2152 128 1320 curveto 128 512 586 0 1310 0 curveto 1947 0 2349 362 2400 981 curveto 1973 981 lineto 1902 590 1683 395 1323 395 curveto 855 395 576 744 576 1321 curveto 576 1930 850 2293 1313 2293 curveto 1669 2293 1892 2102 1943 1763 curveto 2369 1763 lineto end_ol grestore gsave 4.487733 10.000000 translate 0.035278 -0.035278 scale start_ol 384 3712 moveto 384 0 lineto 832 0 lineto 832 1434 lineto 832 1965 1104 2313 1523 2313 curveto 1654 2313 1785 2268 1883 2194 curveto 1999 2110 2048 1986 2048 1802 curveto 2048 0 lineto 2496 0 lineto 2496 1975 lineto 2496 2414 2181 2688 1671 2688 curveto 1302 2688 1077 2571 832 2244 curveto 832 3712 lineto 384 3712 lineto end_ol grestore gsave 4.868733 10.000000 translate 0.035278 -0.035278 scale start_ol 2752 370 moveto 2706 375 2686 375 2661 375 curveto 2513 375 2432 440 2432 552 curveto 2432 1986 lineto 2432 2443 2056 2688 1342 2688 curveto 922 2688 574 2581 380 2391 curveto 247 2259 192 2113 192 1860 curveto 743 1860 lineto 779 2158 976 2293 1379 2293 curveto 1767 2293 1984 2161 1984 1924 curveto 1984 1820 lineto 1984 1655 1876 1584 1535 1546 curveto 925 1475 832 1456 667 1395 curveto 352 1277 192 1054 192 733 curveto 192 284 533 0 1081 0 curveto 1421 0 1695 118 2000 394 curveto 2032 123 2168 0 2452 0 curveto 2542 0 2610 11 2752 48 curveto 2752 370 lineto 1984 910 moveto 1984 780 1943 701 1813 594 curveto 1638 449 1426 375 1172 375 curveto 836 375 640 519 640 766 curveto 640 1022 831 1152 1291 1213 curveto 1746 1269 1839 1287 1984 1348 curveto 1984 910 lineto end_ol grestore gsave 5.249733 10.000000 translate 0.035278 -0.035278 scale start_ol 256 3712 moveto 256 0 lineto 704 0 lineto 704 456 lineto 905 147 1172 0 1540 0 curveto 2235 0 2688 540 2688 1373 curveto 2688 2186 2246 2688 1537 2688 curveto 1167 2688 904 2551 704 2251 curveto 704 3712 lineto 256 3712 lineto 1454 2288 moveto 1932 2288 2240 1911 2240 1330 curveto 2240 777 1921 400 1454 400 curveto 1002 400 704 772 704 1344 curveto 704 1916 1002 2288 1454 2288 curveto end_ol grestore gsave 5.630733 10.000000 translate 0.035278 -0.035278 scale start_ol 768 3712 moveto 320 3712 lineto 320 0 lineto 768 0 lineto 768 3712 lineto end_ol grestore gsave 5.783133 10.000000 translate 0.035278 -0.035278 scale start_ol 2606 1216 moveto 2606 1602 2576 1834 2500 2022 curveto 2327 2437 1920 2688 1422 2688 curveto 681 2688 203 2147 203 1315 curveto 203 501 666 0 1412 0 curveto 2022 0 2444 327 2550 874 curveto 2124 874 lineto 2007 559 1768 395 1428 395 curveto 1158 395 930 504 787 705 curveto 686 842 650 979 645 1216 curveto 2606 1216 lineto 655 1536 moveto 691 1995 991 2293 1417 2293 curveto 1834 2293 2154 1971 2154 1564 curveto 2154 1555 2154 1546 2149 1536 curveto 655 1536 lineto end_ol grestore gsave 6.164133 10.000000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 6.358867 10.000000 translate 0.035278 -0.035278 scale start_ol 2752 370 moveto 2706 375 2686 375 2661 375 curveto 2513 375 2432 440 2432 552 curveto 2432 1986 lineto 2432 2443 2056 2688 1342 2688 curveto 922 2688 574 2581 380 2391 curveto 247 2259 192 2113 192 1860 curveto 743 1860 lineto 779 2158 976 2293 1379 2293 curveto 1767 2293 1984 2161 1984 1924 curveto 1984 1820 lineto 1984 1655 1876 1584 1535 1546 curveto 925 1475 832 1456 667 1395 curveto 352 1277 192 1054 192 733 curveto 192 284 533 0 1081 0 curveto 1421 0 1695 118 2000 394 curveto 2032 123 2168 0 2452 0 curveto 2542 0 2610 11 2752 48 curveto 2752 370 lineto 1984 910 moveto 1984 780 1943 701 1813 594 curveto 1638 449 1426 375 1172 375 curveto 836 375 640 519 640 766 curveto 640 1022 831 1152 1291 1213 curveto 1746 1269 1839 1287 1984 1348 curveto 1984 910 lineto end_ol grestore gsave 6.739867 10.000000 translate 0.035278 -0.035278 scale start_ol 1333 2688 moveto 896 2688 lineto 896 3427 lineto 448 3427 lineto 448 2688 lineto 87 2688 lineto 87 2339 lineto 448 2339 lineto 448 419 lineto 448 151 637 0 977 0 curveto 1082 0 1186 10 1333 36 curveto 1333 390 lineto 1277 375 1211 375 1130 375 curveto 947 375 896 423 896 602 curveto 896 2339 lineto 1333 2339 lineto 1333 2688 lineto end_ol grestore gsave 6.934600 10.000000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 7.129333 10.000000 translate 0.035278 -0.035278 scale start_ol 2368 1902 moveto 2368 2405 2000 2688 1344 2688 curveto 684 2688 256 2390 256 1932 curveto 256 1544 471 1359 1106 1218 curveto 1505 1128 lineto 1802 1062 1920 962 1920 783 curveto 1920 551 1670 395 1296 395 curveto 1066 395 872 456 765 558 curveto 698 628 668 698 642 870 curveto 256 870 lineto 256 282 597 0 1284 0 curveto 1947 0 2368 307 2368 785 curveto 2368 1154 2143 1357 1610 1475 curveto 1201 1565 lineto 852 1641 704 1745 704 1919 curveto 704 2146 948 2293 1335 2293 curveto 1715 2293 1920 2158 1920 1902 curveto 2368 1902 lineto end_ol grestore gsave 7.476467 10.000000 translate 0.035278 -0.035278 scale start_ol 768 2665 moveto 320 2665 lineto 320 0 lineto 768 0 lineto 768 2665 lineto 768 3712 moveto 320 3712 lineto 320 3173 lineto 768 3173 lineto 768 3712 lineto end_ol grestore gsave 7.628867 10.000000 translate 0.035278 -0.035278 scale start_ol 256 -1142 moveto 704 -1142 lineto 704 400 lineto 931 123 1183 0 1534 0 curveto 2229 0 2688 521 2688 1320 curveto 2688 2162 2239 2688 1512 2688 curveto 1142 2688 844 2527 640 2214 curveto 640 2688 lineto 256 2688 lineto 256 -1142 lineto 1457 2288 moveto 1931 2288 2240 1911 2240 1330 curveto 2240 777 1926 400 1457 400 curveto 1003 400 704 772 704 1344 curveto 704 1916 1003 2288 1457 2288 curveto end_ol grestore gsave 8.009867 10.000000 translate 0.035278 -0.035278 scale start_ol 1088 534 moveto 576 534 lineto 576 0 lineto 1088 0 lineto 1088 534 lineto 1088 2688 moveto 576 2688 lineto 576 2154 lineto 1088 2154 lineto 1088 2688 lineto end_ol grestore gsave 8.204600 10.000000 translate 0.035278 -0.035278 scale start_ol 384 2668 moveto 384 -361 lineto 384 -619 298 -704 39 -704 curveto 23 -704 23 -704 -63 -704 curveto -63 -1072 lineto -11 -1083 16 -1088 84 -1088 curveto 576 -1088 832 -896 832 -536 curveto 832 2668 lineto 384 2668 lineto 832 3712 moveto 384 3712 lineto 384 3173 lineto 832 3173 lineto 832 3712 lineto end_ol grestore gsave 8.357000 10.000000 translate 0.035278 -0.035278 scale start_ol 2752 370 moveto 2706 375 2686 375 2661 375 curveto 2513 375 2432 440 2432 552 curveto 2432 1986 lineto 2432 2443 2056 2688 1342 2688 curveto 922 2688 574 2581 380 2391 curveto 247 2259 192 2113 192 1860 curveto 743 1860 lineto 779 2158 976 2293 1379 2293 curveto 1767 2293 1984 2161 1984 1924 curveto 1984 1820 lineto 1984 1655 1876 1584 1535 1546 curveto 925 1475 832 1456 667 1395 curveto 352 1277 192 1054 192 733 curveto 192 284 533 0 1081 0 curveto 1421 0 1695 118 2000 394 curveto 2032 123 2168 0 2452 0 curveto 2542 0 2610 11 2752 48 curveto 2752 370 lineto 1984 910 moveto 1984 780 1943 701 1813 594 curveto 1638 449 1426 375 1172 375 curveto 836 375 640 519 640 766 curveto 640 1022 831 1152 1291 1213 curveto 1746 1269 1839 1287 1984 1348 curveto 1984 910 lineto end_ol grestore gsave 8.738000 10.000000 translate 0.035278 -0.035278 scale start_ol 384 2688 moveto 384 0 lineto 832 0 lineto 832 1434 lineto 832 1965 1104 2313 1523 2313 curveto 1844 2313 2048 2115 2048 1802 curveto 2048 0 lineto 2496 0 lineto 2496 1975 lineto 2496 2409 2166 2688 1652 2688 curveto 1256 2688 1002 2535 768 2164 curveto 768 2688 lineto 384 2688 lineto end_ol grestore gsave 9.119000 10.000000 translate 0.035278 -0.035278 scale start_ol 3392 2688 moveto 3279 2245 lineto 3151 2535 2937 2688 2656 2688 curveto 1972 2688 1344 2008 1344 1262 curveto 1344 727 1701 320 2174 320 curveto 2445 320 2664 448 2898 739 curveto 2944 461 3140 320 3464 320 curveto 3845 320 4159 464 4421 761 curveto 4704 1079 4864 1480 4864 1867 curveto 4864 2874 3881 3712 2694 3712 curveto 2087 3712 1445 3467 997 3067 curveto 498 2622 192 1952 192 1296 curveto 192 186 1217 -704 2502 -704 curveto 2877 -704 3284 -629 3649 -495 curveto 3505 -152 lineto 3091 -273 2804 -320 2532 -320 curveto 1462 -320 640 413 640 1366 curveto 640 2414 1562 3317 2632 3317 curveto 3600 3317 4416 2636 4416 1823 curveto 4416 1254 3989 704 3551 704 curveto 3412 704 3294 804 3294 924 curveto 3294 972 3315 1068 3356 1191 curveto 3819 2688 lineto 3392 2688 lineto 2620 2360 moveto 2908 2360 3119 2150 3104 1880 curveto 3094 1617 2955 1176 2826 982 curveto 2682 766 2492 640 2301 640 curveto 2018 640 1792 909 1792 1251 curveto 1792 1832 2188 2360 2620 2360 curveto end_ol grestore gsave 9.813267 10.000000 translate 0.035278 -0.035278 scale start_ol 1344 2591 moveto 1344 0 lineto 1792 0 lineto 1792 3638 lineto 1497 3638 lineto 1339 3078 1238 3001 546 2914 curveto 546 2591 lineto 1344 2591 lineto end_ol grestore gsave 10.194267 10.000000 translate 0.035278 -0.035278 scale start_ol 960 534 moveto 448 534 lineto 448 0 lineto 960 0 lineto 960 534 lineto end_ol grestore gsave 10.389000 10.000000 translate 0.035278 -0.035278 scale start_ol 2560 446 moveto 649 446 lineto 695 733 856 916 1296 1168 curveto 1802 1434 lineto 2302 1701 2560 2062 2560 2492 curveto 2560 2784 2439 3055 2226 3243 curveto 2013 3431 1750 3520 1411 3520 curveto 956 3520 617 3361 419 3061 curveto 293 2872 237 2653 227 2294 curveto 674 2294 lineto 689 2530 720 2673 781 2786 curveto 898 2997 1131 3125 1401 3125 curveto 1807 3125 2112 2843 2112 2467 curveto 2112 2190 1944 1953 1624 1775 curveto 1157 1518 lineto 405 1103 187 771 146 0 curveto 2560 0 lineto 2560 446 lineto end_ol grestore gsave 10.770000 10.000000 translate 0.035278 -0.035278 scale start_ol 960 534 moveto 448 534 lineto 448 0 lineto 960 0 lineto 960 534 lineto end_ol grestore gsave 10.964733 10.000000 translate 0.035278 -0.035278 scale start_ol 1100 1675 moveto 1156 1675 lineto 1344 1680 lineto 1851 1680 2112 1472 2112 1071 curveto 2112 650 1829 400 1355 400 curveto 860 400 618 627 587 1113 curveto 140 1113 lineto 160 836 211 656 298 501 curveto 482 170 839 0 1335 0 curveto 2080 0 2560 418 2560 1061 curveto 2560 1494 2379 1734 1938 1873 curveto 2267 1998 2432 2239 2432 2581 curveto 2432 3168 2022 3520 1339 3520 curveto 616 3520 231 3141 216 2407 curveto 663 2407 lineto 668 2610 688 2723 744 2827 curveto 846 3012 1069 3125 1349 3125 curveto 1745 3125 1984 2908 1984 2553 curveto 1984 2317 1893 2175 1694 2100 curveto 1572 2053 1415 2034 1100 2029 curveto 1100 1675 lineto end_ol grestore gsave 11.345733 10.000000 translate 0.035278 -0.035278 scale start_ol 960 534 moveto 448 534 lineto 448 0 lineto 960 0 lineto 960 534 lineto end_ol grestore gsave 11.540467 10.000000 translate 0.035278 -0.035278 scale start_ol 1664 896 moveto 1664 0 lineto 2112 0 lineto 2112 896 lineto 2645 896 lineto 2645 1280 lineto 2112 1280 lineto 2112 3640 lineto 1781 3640 lineto 145 1352 lineto 145 896 lineto 1664 896 lineto 1664 1280 moveto 536 1280 lineto 1664 2870 lineto 1664 1280 lineto end_ol grestore gsave 11.921467 10.000000 translate 0.035278 -0.035278 scale start_ol 1088 534 moveto 576 534 lineto 576 0 lineto 1088 0 lineto 1088 534 lineto 1088 2688 moveto 576 2688 lineto 576 2154 lineto 1088 2154 lineto 1088 2688 lineto end_ol grestore gsave 12.116200 10.000000 translate 0.035278 -0.035278 scale start_ol 2440 3520 moveto 586 3520 lineto 317 1671 lineto 728 1671 lineto 936 1903 1109 1984 1388 1984 curveto 1871 1984 2176 1676 2176 1178 curveto 2176 694 1876 400 1389 400 curveto 997 400 759 585 652 964 curveto 205 964 lineto 266 680 316 543 423 416 curveto 625 152 989 0 1394 0 curveto 2118 0 2624 503 2624 1227 curveto 2624 1904 2154 2368 1466 2368 curveto 1214 2368 1011 2304 804 2156 curveto 947 3074 lineto 2440 3074 lineto 2440 3520 lineto end_ol grestore gsave 12.497200 10.000000 translate 0.035278 -0.035278 scale start_ol 1376 3520 moveto 1039 3520 733 3381 544 3145 curveto 309 2847 192 2390 192 1760 curveto 192 611 600 0 1376 0 curveto 2142 0 2560 611 2560 1731 curveto 2560 2395 2448 2837 2208 3145 curveto 2019 3385 1718 3520 1376 3520 curveto 1376 3120 moveto 1868 3120 2112 2666 2112 1769 curveto 2112 819 1874 375 1366 375 curveto 884 375 640 838 640 1755 curveto 640 2671 884 3120 1376 3120 curveto end_ol grestore gsave 12.878200 10.000000 translate 0.035278 -0.035278 scale start_ol 2549 2624 moveto 2461 3186 2067 3520 1508 3520 curveto 1104 3520 741 3337 524 3034 curveto 296 2703 192 2284 192 1664 curveto 192 1092 285 726 502 423 curveto 699 149 1020 0 1424 0 curveto 2122 0 2624 483 2624 1154 curveto 2624 1791 2169 2240 1528 2240 curveto 1174 2240 896 2107 704 1851 curveto 704 2667 989 3120 1503 3120 curveto 1819 3120 2038 2939 2109 2624 curveto 2549 2624 lineto 1463 1856 moveto 1902 1856 2176 1577 2176 1128 curveto 2176 707 1866 400 1448 400 curveto 1024 400 704 721 704 1152 curveto 704 1568 1014 1856 1463 1856 curveto end_ol grestore gsave 13.259200 10.000000 translate 0.035278 -0.035278 scale start_ol 1376 3520 moveto 1039 3520 733 3381 544 3145 curveto 309 2847 192 2390 192 1760 curveto 192 611 600 0 1376 0 curveto 2142 0 2560 611 2560 1731 curveto 2560 2395 2448 2837 2208 3145 curveto 2019 3385 1718 3520 1376 3520 curveto 1376 3120 moveto 1868 3120 2112 2666 2112 1769 curveto 2112 819 1874 375 1366 375 curveto 884 375 640 838 640 1755 curveto 640 2671 884 3120 1376 3120 curveto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 25.300000 9.100000 m 25.300000 19.100000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 30.230000 9.145000 m 30.300000 19.100000 l s gsave 23.800000 8.650000 translate 0.035278 -0.035278 scale start_ol 2560 3392 moveto 2560 1091 lineto 2560 651 2220 381 1659 381 curveto 1400 381 1190 439 1022 552 curveto 849 678 768 844 768 1091 curveto 768 3392 lineto 384 3392 lineto 384 1083 lineto 384 415 869 0 1659 0 curveto 2441 0 2944 424 2944 1083 curveto 2944 3392 lineto 2560 3392 lineto end_ol grestore gsave 24.240267 8.650000 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore gsave 24.545067 8.650000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 24.883733 8.650000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 25.086933 8.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 25.256267 8.650000 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 25.662667 8.650000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 26.001333 8.650000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 26.340000 8.650000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 26.678667 8.650000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 29.000000 8.650000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 29.440267 8.650000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 29.778933 8.650000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 30.117600 8.650000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 30.253067 8.650000 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore gsave 30.557867 8.650000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 30.727200 8.650000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 30.930400 8.650000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 31.269067 8.650000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 25.300000 11.100000 m 29.500000 11.100000 l s 0 slj n 29.500000 11.500000 m 30.300000 11.100000 l 29.500000 10.700000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 30.300000 15.100000 m 26.100000 15.100000 l s 0 slj n 26.100000 14.700000 m 25.300000 15.100000 l 26.100000 15.500000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 35.280000 9.095000 m 35.300000 19.150000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 30.300000 13.100000 m 34.500000 13.100000 l s 0 slj n 34.500000 13.500000 m 35.300000 13.100000 l 34.500000 12.700000 l f gsave 26.100000 10.350000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 26.540267 10.350000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 26.946667 10.350000 translate 0.035278 -0.035278 scale start_ol 3264 1792 moveto 1836 1792 lineto 1836 1408 lineto 2880 1408 lineto 2880 1324 lineto 2880 777 2429 381 1803 381 curveto 1455 381 1140 499 938 705 curveto 712 933 576 1315 576 1709 curveto 576 2494 1056 3011 1780 3011 curveto 2302 3011 2678 2757 2772 2339 curveto 3217 2339 lineto 3097 3008 2580 3392 1808 3392 curveto 1397 3392 1065 3290 802 3081 curveto 414 2770 192 2269 192 1687 curveto 192 693 824 0 1732 0 curveto 2188 0 2553 167 2880 524 curveto 2988 0 lineto 3264 0 lineto 3264 1792 lineto end_ol grestore gsave 27.420800 10.350000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 27.590133 10.350000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 27.996533 10.350000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 28.369067 10.350000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 28.775467 10.350000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 30.850000 12.350000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 31.256400 12.350000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 31.425733 12.350000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 31.764400 12.350000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 31.967600 12.350000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 32.306267 12.350000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 32.475600 12.350000 translate 0.035278 -0.035278 scale start_ol 768 3392 moveto 384 3392 lineto 384 0 lineto 2427 0 lineto 2427 381 lineto 768 381 lineto 768 3392 lineto end_ol grestore gsave 32.814267 12.350000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 33.152933 12.350000 translate 0.035278 -0.035278 scale start_ol 2139 1595 moveto 2121 1818 2070 1963 1979 2090 curveto 1814 2305 1527 2432 1193 2432 curveto 548 2432 128 1947 128 1194 curveto 128 463 539 0 1188 0 curveto 1759 0 2120 328 2166 888 curveto 1779 888 lineto 1714 534 1516 357 1189 357 curveto 765 357 512 673 512 1195 curveto 512 1747 761 2075 1180 2075 curveto 1503 2075 1706 1902 1752 1595 curveto 2139 1595 lineto end_ol grestore gsave 33.457733 12.350000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 33.796400 12.350000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 33.965733 12.350000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 34.101200 12.350000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 34.439867 12.350000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 26.850000 14.550000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 27.188667 14.550000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 27.527333 14.550000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 27.866000 14.550000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 28.035333 14.550000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 28.509467 14.550000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore gsave 32.850000 8.650000 translate 0.035278 -0.035278 scale start_ol 768 3392 moveto 384 3392 lineto 384 0 lineto 2427 0 lineto 2427 381 lineto 768 381 lineto 768 3392 lineto end_ol grestore gsave 33.188667 8.650000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 33.527333 8.650000 translate 0.035278 -0.035278 scale start_ol 2139 1595 moveto 2121 1818 2070 1963 1979 2090 curveto 1814 2305 1527 2432 1193 2432 curveto 548 2432 128 1947 128 1194 curveto 128 463 539 0 1188 0 curveto 1759 0 2120 328 2166 888 curveto 1779 888 lineto 1714 534 1516 357 1189 357 curveto 765 357 512 673 512 1195 curveto 512 1747 761 2075 1180 2075 curveto 1503 2075 1706 1902 1752 1595 curveto 2139 1595 lineto end_ol grestore gsave 33.832133 8.650000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 34.170800 8.650000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 34.340133 8.650000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 34.475600 8.650000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 34.814267 8.650000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 35.152933 8.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 35.322267 8.650000 translate 0.035278 -0.035278 scale start_ol 384 0 moveto 1660 0 lineto 2495 0 3008 642 3008 1698 curveto 3008 2750 2500 3392 1660 3392 curveto 384 3392 lineto 384 0 lineto 768 381 moveto 768 3011 lineto 1582 3011 lineto 2264 3011 2624 2559 2624 1694 curveto 2624 837 2264 381 1582 381 curveto 768 381 lineto end_ol grestore gsave 35.762533 8.650000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 36.101200 8.650000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 36.270533 8.650000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 36.609200 8.650000 translate 0.035278 -0.035278 scale start_ol 256 3392 moveto 256 0 lineto 640 0 lineto 640 412 lineto 815 133 1048 0 1368 0 curveto 1973 0 2368 489 2368 1242 curveto 2368 1978 1983 2432 1365 2432 curveto 1043 2432 815 2308 640 2036 curveto 640 3392 lineto 256 3392 lineto 1296 2070 moveto 1714 2070 1984 1729 1984 1203 curveto 1984 703 1705 362 1296 362 curveto 901 362 640 699 640 1216 curveto 640 1733 901 2070 1296 2070 curveto end_ol grestore gsave 36.947867 8.650000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 37.286533 8.650000 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore gsave 37.591333 8.650000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 24.800000 9.100000 m 25.800000 9.100000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 34.780000 9.095000 m 35.780000 9.095000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 29.730000 9.145000 m 30.730000 9.145000 l s showpage kamailio-4.0.4/doc/sip/figures/bye.dia0000644000000000000000000000306012223032460016236 0ustar rootroot‹í\]oÛ6}ϯ”×”)Q›8E;lÀ°-ÐÞÙVlm²dHt¿ô·”dÙ±äMTn-Ïv€:©h]ÞÏÃKÚwïžç‘ñ-H³0‰‡&F–iñ8™„ñth~ýòËÏ|wu7 ý·òß4õ熼#ÎÔÿ†æLˆÅÛÁàéé E«ÌIŠ¢p‰²`ðÝ" ß40ï·ïžøÂ/.øB¤áh)#öçÁÐùã¿§i²Œ'åã$JRã› ÍëÇüÇÜß ^ÜÛ µðAZ¡ÌIÊ1±ZìŒíÞ§^Ë¡LŽÄÓûë÷ÎuñÈòB«ç‹¹ŸNø„J?*¦AGi;‹J‚’‚ „ÙÃ"IEꇢD%Iøq&ÒeÐ*û‘´GM Üà1"i’âѲF1/Ü©ÝS¦i8Ùãˆ[C»w=…1{xî>¹`Õà[˜…£(¨dcÑaÕ ¡“n—á$Èöiw{p÷ÎY9:Ø#úfø¿ä4¦¶È_i öa“ÖŒR'¥§ïMFcQJýYøñÄO'Æã÷0ÌMf–y9œ ÍÖž)I[ë /’JÝôÆnk2…1UN3–ÂL£ €q§7D¾ÜRdÑ._ÚÂŽ“8~âI.Ô~ kWyÛÄËùxÑÞÍ wÒ>ÖÕ>‡Ñ¾WiŸCkŸ7jŸ£ö‰®ö± £~L*ýcÚk!ë—ж~ú¡`éÇA¤Ô?E\ö¡ÍÚ§ðÚoº[Šöà§iòT"®€°µ!¢ žŠY­ì[íÕµÁÊ9D[¨×ú•Ó!´˜c¹•c18ÇZKXó7vq¬àX_‚gÑàXT×±Â*Ó0´U‚YŒÑ[ÙŒ¨ÔÅykl¡&ÕLp·†jk£|©º³ýðçÏW–²I¼~ŒúÓxôça´’ÏðãÌ42±RtV*»x÷OÉ2 åòºåôfA8‰×xŒ óNCC×ÁÊÚ¢Èé4ž•¾6guYò¼6¥ºÚ)• Æ—œ _¢’/UbÖ®Žü‡ãLL×Ê5†)ÌÒ¡0…«Ö…)\0[¼tw ¡;‹ÃYÄÓµˆƒrrÆá8Üá Ï–¿åzšJq#ÅP¦Ùy—‚ ‡õ){Š5×^^#W’7"X@YDVjŒ‘«J5CÜé£TË2>þv.ÕZÓ†ÿïZ½Çñ±¥OS™ oˆ¥²Œ¸"©Ø*Xª¼^Xêç_?ŸÒäyu>\UÏŽ'IU±v_3 ¦ên:{L50(UeÍDU†ÛòT¬Ý_%Æ Äª¬@0¸ÖRÖ/¥´[¬hÕF6{<„Â[¡yÑFèqZÁé‘(\FR­î"[Ýkëpéu°×i‡<Ëàò¬êvØí^‹XïK»û¾åv¨η¼Ê·8 o•"Ö]Ž_|뀾Å:ä-ŒáŠ¢\œ®«"Æ ©«²¦ã‹ƒn¯k÷e±‡lÉ•`ˆ—²‘—w9¢$߬cÎe³î‡6@tMxšý?íÎ7!Èj}—Xyï›ØÈ%=ö¾ÏÈóuMx’žO¬žÑt\çΞû¾\u+ß—Ôõ²ïs÷׳ãiú?îÀyHÎ…] Ö“{|²U(v}‰€CPMKžfhï;¸79 ¤ß½±Ë7>‰ä?Dµ¿Ý^6>ÿÅ,Y CÊ—He¥òoyý\"AÏ §v—Å/‹„­Œ¹  }GÙ†‚¾UOòHÑ߀sÁޝº›ã« üøª»ïø*;Îã«D»ÊÊB˜Tpy.âŽêþÒüx’倵_J[kkÏ倦Ñßí<É*Áœµ]h'pfù—¬êi}›d_gúg‹m¹r:Ì"@Ÿ•+-û–"[êl1wX…üë{|.…»“O“Êzj·ƒ‰›óMuŒ¦D´ˆªâ®±Çv_ap.ôµ“%O3 x‡ê \ Èv1 ýWr6ÝíK9¨¾3ÀêðIÄ2‰0¨dséú2˜úL¢«àe9ðúŠƒs),yša ¿ÓÃfE¢dEDÆ„×'+ª>ìs.ÁÐÉžÇ ƒê;¯^~!ÖýÕ?ý™e_Pkamailio-4.0.4/doc/sip/figures/invite1.dia0000644000000000000000000000305112223032460017036 0ustar rootroot‹í\Qo£8~ï¯@ôµK°1`¶MW{ÕTÝê¶ÚöîµršrG §m^î·ŸmiЂ3MzI*5Mqü1ö|3Ϙ\|y™ÄÆS˜åQšôMdÙ¦&Ãt%ã¾ùçÝoŸ¨ùåòäb±Ïâwœ±‰!z$¹ü¯o>r>ýÜë=??[ñÌbPŠ=0±±Uðï-a– £Ðý4_#2¢2ÿìA…EMDTå²í]XáÐ÷ß'ê¨Àÿ7ó›Rì:BŒár쨪bØ4×B>žŸÙfŽ=Ðȱø’.Š3b»‰TÒÅÅ+¾9xwŽöäpÒî:ÝË@w®hʽz gOü…²‡%¤TàøÒÄßÝØÃ׫Ã1=uî§1è`5{®å9`‹C)Øï cÈjqð,Jwb ?înŒ[.7Éh…ÐQë^ä¸sy–Z>&žˆŸ=˜¨Ü#Dº‡_L|i *4+îÚ! «XaK§Dc¡„ƈµfbÎYÆÂ}ªÒð´¦’Í73p3Cà63ä]Sì\1[ â‚=ªHª'],ϱ>©¸”sÝ ‰ßa[|h´{5ÓµÎEÌ¢“tÕñ$ ¤Ìdâÿ èp‹Ÿ¦3Ö%_Kxhm‹êr»¯kÅñ8˜U(°ó@ƒ[Ë–"®fš:Jÿšh »½îš@r0P!·‚sU¸ÈlGÁö‹ã0;”8[Cû¹ñì^î!–h ®°ä‹d—&p{}cÜdéËü`v›Ýu¹ŸV@5ÂY[9 DäOf!E I ;ØQR-ᡎ*?¾ôªokzûUN—'ÿáôÒOkamailio-4.0.4/doc/sip/figures/invite1.png0000644000000000000000000002543212223032460017074 0ustar rootroot‰PNG  IHDR;¤ÁŽsBITÛáOà pHYsÐй‹çŸ IDATxœíÝ{TgÞð IWO" ‚EtÅ"Ò*.ö( ¬ZëzaëqñV´,][[´ZÔúZ”ÖK9µÂzV(¬µ—³®wië…CÛ  âe-xŽ ¨@W@nšP.r ™÷§Ò$„dL†™ðýüõäÉ\žÉ/ßÌä6#¢išpê€Ø$V$é5€¿$6//oÚ´i2™,00ðË/¿Ú1 [uuu+V¬P(NNN³fÍ:}ú4é×}‰$ÜÝÝ.\XQQa¸s¦¡õ$û<Š¢®\¹wàÀ¶¶¶ÜÜÜ‚‚+ÌóôÓOWUUµ¶¶&''9rÄpš¦iš¾{÷nhhèêÕ«.ÇœiÀº¸ÛçÑ4½téÒÏ?ÿœ6P]]½lÙ2777™L¶páÂÆÆFÒOýúœ`æÝwßõòòrvv^µjU{{;3Á¡C‡üüüÄb±áòAT*íìì4ì7|Àiš~üø±T*51±Þ4zµxüøq||üˆ#FŒñÚk¯=~ü˜¦éùóçéøá‡ððpOOÏææfÒÓÓÓãååõÄ[i‡._¾ìãã“——×ÝÝ]UUµ~ýzÓÓ-¨™(š¦===™4ê ÎÏÏïììlmmݺuëš5kZ_rrò¼yóîß¿ÿóÏ?¯_¿þÍ7ßd&XµjSr0mæÌ™Û¶m«®®Öë7|À›››wíÚj¸¦Ñ«ÅÖ­[-ZÔØØØÐÐ0þüwÞy‡¦ék×®M™2¥···§§gêÔ© |ð™%//oñâÅÖÜ`{a»}ža?EÓ´X,Öh4¦ÇÔÑÑÁ¼¾®Ïßß¿´´”´¼½½™ êëë-~†«ÚÚÚ¸¸8¹\¾zõ꺺:Ò¯û€®®®óçÏ¿uë–áBšF¯¾¾¾¤}ûöm___Ò^³fÍ¡C‡<¸mÛ6𦫫«ýüüzzzhšÞ°aCvv¶M¶\àl·Ï3ì§hšöððhjj2\ßåË—gÏžíêêJž"‘h õI$±X,‹ŒN yðàÁ¦M›"""ÈM‹¢šF¯_÷eº··W"‘öÉ“''OžìïïÏDýOúÓW_}Õ××HžAíöy†ý¦ÞÇ>õÔS'NœP©TZ­¶µµÕp5LÃÏÏïþýû†K@bYkoo×} ª×0ÁÌÄúúúVVV’ö;wÆŒCÚÿûß>üꫯ’žüqúôé.\øóŸÿlùv ¶ÛçöS4M_¼xÑÇÇç»ï¾Ó{ßìáá‘““ÓÕÕU]]c"±{÷î]¸paeeeoooiiéªU«ô&süá(,,|üøqssó{ï½÷ÜsÏ‘~[$öwÞY²dISSSccãÂ… išþÏþ3iÒ$F£ÑhBBBnܸA&ž3gNxxxaa!ëM³o¶Ûçöÿ2ÃÙ³g§NêèèÀ¬ûÔ©S&LH$ãÇ'ß4 ´¾¾¾¾}ûöùûûK¥Ò“'OêMæ8sæÌìÙ³e2Ùˆ#/^\UUEúm‘XÝÏŠãããÉáîóÏ?ÿ¯ý‹LPPPðâ‹/’öéÓ§ýýýµZ-«Í²¶Ûçö#Q0¸¿ýíoÿ÷ÿ7Ô£à5íó ûE4þ &µ¶¶Îœ9S©TúúúõX€’ õ€×D"‘X,>|ø0âÊØÇ þm $ÆŠ£¢¢X/$°µÿûßû÷ïg7ïòåËwîÜiÝñ—Œ'¶°°Ûa€š››KJJØÍûÜsÏYw00'Ùí­[·nýúõFï2žXûÉ-[¶PuèÐ!Ö£'´|ùrÓÁÛ°aEQŸ~ú©á]^^^¶üÖ“ìöæÌ™3Ð]òDþƒ‹Ï«ø 5âÓ‰%{àvãÆ7nœÑ»X;„ñëÙÛgźç㈈ˆÐ»+33“üXLOLLLff&™WdŒÑ~Ûo €>{K¬.WWלœÝžØØØ .ÔÔÔèvÖÔÔ\¸p!66–Üd~w¦×¦ ~¯ËÅ6ü–='öã?ÞµkWoo/ÓãääôÆo¤¦¦êN–––öúë¯;99q>@‹ÙÛûXòÇB¦òÖ[o1=>œv |ðÁ'Ÿ|òóÏ?3=+V¬ÈÈÈ 7³²²^~ùeOOOs–†÷±0äì|KQTrrrww÷Þ½{™žêêêÈÈÈŸ~úI$Mœ8ñ‡~ Û©b VĺFöŸØÎÎ΂‚‚§žzŠöŠ+/^ìèèxòäIæÓ)$8úFöÿo;—]»víÞ½[·311ñµ×^“ÉdiiiC50ìKQ”V«1cFqq±î°ŸþyFséÒ%Ãyº©·.>?‡ŽŠí[RRRbb¢›››™Ó£Fü‡ÄÚ3‘H$—Ë7oÞüöÛo“/¥žBø ‰µg̹™¹Eø‰µgzo¡Í-jÄH¬=3úk ¹Eø‰µg&~_e4·¨ÿ!±ölÐ_Dêå5â?®i隀53Ï?ÂäV¡PPH,¿qXà'777rY: ‰å7®‹SœrÉœ³òé㨘ÿð>Öž™>®1üð 5â?ü`8²è‡P`XABV‡-$V`Õa‰ d($VU`à³bÐh4‰¯­¨ÿá\ŠöÌ¢¸‚}Cb„‰$@HX!Ab„‰$@HX!Ab„‰$@HX!Ab„D`‰ýJ¯Ÿ¦éíÛ·+Š‘#Gîܹ“ùÓ@ý† Ô5謵9–Xbiš6ú—ÂÌÌÌ¢¢¢òòòÒÒÒ£Gšî×[ Y¦nÛô¬±)¬Ðb7—u ,,L©T’¶R©Œˆˆ0Ýob™E:tÈÏÏ¢(OOÏææfÒßÓÓãååÕÔÔ¤;ezzº¿¿¿T*6mÚõë×Igggll¬‹‹‹··wJJ ÷j¦±®‘Àö±)++›>}:i‡††–••™î7íêÕ«×®]£izÕªU¤3??ÆŒ^^^ºS*•Ê¢¢"µZO:wïÞ­R©jjjnܸqþüù'ß:€~œ½6X‘áúúúH»¯¯O,›î7±LŠ¢êëëI»ººÚÏϯ§§‡¦é 6dggëMÙÒÒBÚ‰„´ÇŒSUUEÚ•••Ü?\|¨˜ÆºFv²usskoo'í¶¶6wwwÓý¦ùøøÆøñãò³³µZmaaáÒ¥Kõ¦ôðð FCÚ ãÆ#m¦`v’ØàààââbÒ.)) 6Ýo¾mÛ¶¥¥¥]ºtiÖ¬YNNNæÌâíí]SSCÚLÀ:8Û›[‘áÒÓÓÃÂÂêëëëêêfΜ™••eºßÄ2 >gΜðððÂÂBÓS27·nݺdÉ’æææ¦¦¦%K–pÿpñ¡F`ë ,±½ÜhµÚÄÄD¹\.—ËwìØ¡ÕjM÷]²^ƒqúôifÞAÛÑѱvíZggçQ£Fíß¿_*•²ß`VXþc]#›Ÿ¯¸½½ýã?NJJ²h-¼räÈ‘ººº½{÷²˜÷öíÛ‹-ª®®¶ú¨LÀùŠùç+V«ÕIII~~~ï¿ÿ¾íÖbk­­­‡~óÍ7-škÓ¦M=jhhضm[tt´ÆÃMN]­V«SSSÓÒÒÔjµ-–Ï‘H$‹>ìëëkÑŒAAAK—.MNN¶Ñð`²òQñ@YÅ—pTÌCÅg»Ù¯ð™‹¬p手¬pŒeb-ÍjTT»€.–Ÿ<;v,!!ùÉ.ð>yâ3ÖŸ<±ÿ¬X­V§¥¥¥¦¦š³›-((°tdÀ9¢AbùlKnš™[<{¸„owøoÈK š[<{¸„Äòß'–0‘[<{¸„Äò/KÍ-ž=\BbùG‰%ôr‹g—Xþã]b &·*•ÊÒ‘kH,ÿñ4±„F£‘Hlò'!0 ‰å?>þ?–¸X‹œ™ `˜@b„‰$@HX!Ab„‰$@HX!Ab„‰$@HX!Ab„‰$@HX2ä< ¤¡wWfffLLŒá\111™™™d^‘1Fûm¿5Ab\]]srrt{bcc/\¸PSS£ÛYSSsáÂ…ØØXr“þ•^[÷¦n§@b>þøã]»võöö2=NNNo¼ñFjjªîdiii¯¿þº““çä .ÎÌJD¢_ž~¤òÖ[o1=>œÖ ¥FzûXŠ¢’““»»»÷îÝËôTWWGFFþôÓO"‘hâĉ?üðC@@5ØNûX.$&&ž}úÅ‹ ç5ç&(Š’Ëå{öìQ©TfNÏn8*¶CB©‘áQ1EQZ­vÆŒÅÅźãþùç5Í¥K— çè¦Þºlýh0k”Ëå›7o~ûí·É§e¦§g1*$Ö¡FÜÓ{4·H,ôC¸gôãh¹Eb¡jÄ=_ Í- ýP#î ú•¯^n‘XèGj9ÔF Í™ŒÉ­B¡ Xå׉´[f>‡€KjµúàÁƒ¦?F6 ûX;DjTPP0ÔF¢¢¢F÷ÀGÅÐ5âžé÷±†>±®ŽŠlÈÌT˜‰° «g•@b¬ÌFY%X«±iV $À 8È*ÏŠíjÄ=F#‘X°ÿÃ?Ú†’Eq}H,€ ±B‚Ä  $H,€ ±B‚Ä  $H,€ ±B‚Ä  $H,€ ±B‚Ä‚M tqtš¦·oß®P(F޹sçNÝë2í7\ ùW^·§K³3X° Ý«¤êÊÌÌ,***///---,,8UVV6}útÒ -++3ÝoÚÕ«W¯]»FÓôªU«222Hg~~þŒ3¼¼¼t§T*•EEEjµ::::>>žtîÞ½[¥RÕÔÔܸqãüùóO¾u\àìµ8ßÃÁÁ¡¯¯´ûúúÄb±é~ˤ(ª¾¾ž´«««ýüüzzzhšÞ°aCvv¶Þ”---¤ÝÑÑ!‘HH{̘1UUU¤]YYÉåƒÆºFØÇ§ÜÜÜÚÛÛI»­­ÍÝÝÝt¿i>>>¤1~üø°°°ììl­V[XX¸téR½)=<|øàÁƒÅ‹oÙ²ÅFc3Äú1Çuw†ÝýêPÅjD"‘X,>|ø°¯¯¯E3uvv.]º499ÙFó"Û¡jd:«¨)—pv0Å.÷«ÃkçU;ƒÄÚ-K³eë!Á“ÃûX;DjtìØ±„„æ§ÀC,r„ÄÚ!¦Fjµ:---55ÕœÝlAA퇿 G4H,P”AÌÌ-jÊ%Ö9BbíÑ š[Ô”KH,ô3Q#¹EM¹„ÄB¿Akd4·¨)—Xègfôr‹šr ‰…~ՈɭJ¥²ñ¸  ýXÔH£ÑH$ø9 wXçgŠ¢(ÄU(X!Ab„‰$@HX!Ab„‰$@HX!Ab„‰$@HX!Ab„‰$¬Iô+½þŠŠŠ ¸ººººº.X° ¢¢‚ôÓ4½}ûv…B1räÈ;wý‡7³Lww÷yóæ•——3ý¬ÉnF>@bÁšt/Žª+&&&<<¼®®®¶¶vÆŒ+W®$ý™™™EEEåå奥¥………G5±ØÚÚÚÈÈÈ5kÖ0¬ÉnF^°ô‚³ìæ. y ×îääÔÕÕEÚ?vvv&í°°0¥RIÚJ¥2""ÂôÒºººõú)ŠJOO÷÷÷—J¥Ó¦M»~ý:éïììŒuqqñööNIIў݌VĺFØÇ-Z”’’ÒÚÚªV«?ú裗^z‰ô—••MŸ>´CCCËÊÊL,¤µµ5%%%$$Äð.¥RYTT¤V«£££ãããIçîÝ»U*UMMÍ7Ο?ot™¬g2œ½6g†¼F†k¯­­;v,ØøñãëëëI¿ƒƒC__i÷õõ‰Åb£KcøúúÞ½{Wo-Eµ´´vGG‡D"!í1cÆTUU‘vee%elkÑŒVĺFØÇâââÖ­[§R©T*Õš5kÖ¯_OúÝÜܘ«ïµµµ¹»»¦i­V[YY9a„›7oNàááA...†´ÆGÚLÃZ3Î^€3C^#õËd2Ý÷±2™Œ´-}{ïÞ=Ÿ¶¶6ú·»J£ÓûúúºµhF+b]#ìcjooOJJ²é*‚ƒƒ÷ï߯V«ÕjõžyæÒûÞ{ï=xð ¾¾~×®]̾w cÇŽ ;~ü¸9+]¹rå–-[ZZZš››·nÝjþhYÏÈÎ^€3æ×H¥RíÙ³G.—[«¦=»nݺ5wî\gggggç¹sçÞ¾}›ôkµÚÄÄD¹\.—ËwìØ¡Õj.S÷f^^^hh(mÆ®²££cíÚµÎÎΣFÚ¿¿T*Õ›ÀÒ­ˆuŽpM;dNÔjujjjZZs±,û®éíÛ·-ZT]]ÍÙŒ¦áš`.µZ””4~üø÷ßßœk· Ú¦M›=zÔÐаmÛ¶èèhf´5$vVY%ƒ‚‚&Nœ8bĈäädf´5Û!ÃBM¹Ä:G¸>’3'« H¬Ý²4«QQQ¶<9Û!R£cÇŽ%$$0¿(b‘#$Ö15b®¿nÎn¶  ÀöCƒ_#$(Ê Ffæ5åë!±vÈhÍ-jÊ%$ú™¨‘‰Ü¢¦\Bb¡ß 52š[Ô”KH,ô3³Fz¹EM¹„ÄB?‹jÄäV¥RÙx\Љ…~,j¤V«Éî€H,ôCøÿ¶X!Ab„‰$@HX!Ab„‰$@HX!Ab„‰$@H„‘Xѯôú+**,Xàêêêêêº`Á‚ŠŠ ÒOÓôöíÛ ÅÈ‘#wîÜiôÌ2ÝÝÝçÍ›W^^Îô³$»Ì'ŒÄê^ÀOWLLLxxx]]]mmíŒ3V®\Iú333‹ŠŠÊËËKKK =jb±µµµ‘‘‘kÖ¬a:Y’ÝŒ°ôò•ìæ² Ãõ:99é^ùÛÙÙ™´-½òwWW—£££^?EQéééþþþR©tÚ´iׯ_'ý±±±...ÞÞÞ)))ºÓ³›ÑꆰF`&Ö5Æ>v ‹-JIIimmU«Õ}ôÑK/½DúËÊʦOŸNÚ¡¡¡eee&ÒÚÚš’’bx—R©,**R«ÕÑÑÑñññ¤s÷îÝ*•ª¦¦æÆçÏŸ7ºLÖ3 ‚³×†'g¸ÞÚÚÚ±cÇ’!?¾¾¾žô;88ôõõ‘v__ŸX,6º4†¯¯ïÝ»wõÖBQTKK iwttH$Ò3fLUUiWVVRÆö±ÍhuCX#0ë {·nÝ:•J¥R©Ö¬Y³~ýzÒïææÆ\o¦­­ÍÝÝÝèì4MkµÚÊÊÊ &ܼyÓpÒpqqÑh4¤ÝÐÐ0nÜ8ÒfÖš`œ½6<9ÃõÊd2Ý÷±2™Œ´-}{ïÞ=Ÿ¶¶6ú·»J£ÓûúúºµhF«Â™X×HØûØàààýû÷«ÕjµZ}àÀgžy†ôÇÆÆ¾÷Þ{<¨¯¯ßµk³ïÈØ±cÃÂÂŽ?nÎJW®\¹eË––––æææ­[·š?ZÖ3ôãìµáI 4æ[·nÍ;×ÙÙÙÙÙyîܹ·oß&ýZ­611Q.—Ëåò;vhµZ£ËÔ½™——J›±«ìèèX»v­³³ó¨Q£öïß/•Jõ&°tF«’EX×Èæg?moo?xðàž={,Z‹Pܾ}{Ñ¢EÕÕÕœÍhœý”ÿøxöSµZýþûïûùù%%%Ùn-CbÓ¦M=jhhضm[tt436I,Éêøñã“’’ְ̹àMœ8qĈÉÉÉÌ@Xù¨X­V§¦¦¦¥¥éGh\ÂQ1ÿ±®‘ÄZ#(«`EVH,² À™'J,² À1–‰µ4«QQQìVºX~òtìØ±„„æ·»ÀCøä‰Ï†àú±Ìµ½ÍÙÍX:2`Ñ ±|6dW|63·xöp ßîðß_£}ÐÜâÙÃ%$–ÿ†8±„‰ÜâÙÃ%$–ÿx‘XÂhnñìáË>>W®\!w1̹sç&L˜põêUÛo¢¬k„ÄÚ!áÖ¨££C&“‘vXX˜R©$m¥Raº_ײeË>ûì3ææÑ£G£££I›<2999ååå¶ÙŽÁ!±ÐO¸5úæ›o"##I{Ĉ­­­¤­V«år¹é~]žžžÌ͆†///Ò¦(*##cÒ¤I5556Ú s°®çyŽ ´F%%%111yyyE‰ÅâÞÞ^Š¢´Z­£££F£1ѯK"‘tww‹Åbr³¯¯ÏÉÉ©··—¢(‘HäààpîܹY³fq¹uzx}ž'€A,_¾<;;›Ä•¢(777æRlmmmîîî¦ûu)Ї27>|¨P(˜›YYY111¥¥¥¶Ø[Cbaèegg¿òÊ+999¡¡¡Lgpppqq1i—””›îמ››ËÜ<{ölxx8s3..îÀ/¼ðBII‰Õ·Åæ8;þΫF;v¬á‡@éééF?¨_×Å‹GŸŸßÝÝŸŸ?zôèK—.‘»˜GæÄ‰EEE6Û2SX׉µC–Ö¨­­-))Évã1Íp/ÒÖÖFÓ´V«MLL”Ëår¹|ÇŽZ­–L?P¿žS§NK¥Ò   o¿ýVwuL;''G¡PØrãÄ:GøäÉ™_#Ý‹­ ¦\b#œû{˜²è‚ÝÀHì°ƒ¬ ;Œ «v‰U»ÄÚ9dÕÎ ±vËÒ¬FEEÙzHðäðíŽ"5:vìXBBóƒ>à!9BbíS#‹v³¶ü‚Ñ ±@Q523·¨)—X牵CFk4hnQS.!±ÐÏDLä5å ý­‘ÑÜ¢¦\Bb¡Ÿ™5ÒË-jÊ%$úYT#&·*•ÊÆã‚~H,ôcQ#rŠ3›ô!±Ð5â?œ™ `X@b„‰$@HX!Ab„‰$@HX!Ab„‰$@HX!±ŸÄ~ÿý÷ ,pvvöððX»vmSSé§izûöí …bäÈ‘;wîÔ½ Ñ~=¹¹¹Ï>û¬L& >sæ ÓOþ{AܼysÒ¤I6Û2–^¾’Ý\X°`Annn{{{ccãÆçÎKú322Œ^ x ~]—/_öññÉÏÏïêêR*•>>>W®\!w1¹sç&L˜põêUÛo¢¹x[#`°®‘ý$VWGG‡L&#í°°0¥RIÚï¬Ôs ±IDATJ¥2""Ât¿®eË–}öÙgÌÍ£GFGG“6yrrr ¯/>´Q£a‰ýo¾ù&22’´GŒÑÚÚJÚäL ¦ûuyzz66627¼¼¼H›¢¨ŒŒŒI“&ÕÔÔØh+XD†9Ö5²ÃsP”””ÄÄÄäååP%‹{{{(ŠÒjµŽŽŽÆD¿.‰DÒÝÝ-‹É;¾>''§ÞÞ^Š¢D"‘ƒƒÃ¹sçfÍšÅåÖ™ƒÿ5œƒâË—/ÏÎÎ&q¥(ÊÍ͹öL[[›»»»é~] …âáÇÌ͇* æfVVVLLLii©-6À(»Jlvvö+¯¼’““Êt“vIIIpp°é~]ááá¹¹¹Ìͳgφ‡‡37ãââ8ð /”””X}[ŒãìøÛÖ<8vìXÃÒÓÓ~&}zÊ”)þþþ¾¾¾¿ÿýïO:EúU*•‡‡‡ÑY6nܘ••e­¸‚}Cb­ì‹/¾`}×­[÷Å_¶\.ôè‘ÑY²²²bbbJKK9"m!vs z'Ž”H$ 4M/]ºôóÏ?7œ…<˜_~ùå¨Q£Š‹‹­2 ÔˆÿX×ûXkúÇ?þ±fÍÝÇ766öøñãEíØ±cçÎßÿ}OOÏO?ýôꫯêÎøÊ+¯üõ¯}ñÅ/^¼8Dc‡'’””ÄÑå”9{m¦NZPP ÛSXX8uêTÒ>{öìÔ©S˜ý­îƒ™““£P(ž|¨÷(Š’Ëå{öìQ©TfNÏ®Fø¬Ø¡FÜc~ #—Ë7oÞüöÛoËåòA§gQ#$Ö¡FÜÓýíeFn‘Xè‡qO/±„‰Ü"±Ð5âžÑÄFs‹ÄB?Ôˆ{&Kèå–ëÄFFFZº&àLaa!…q‹<æƒbr«P((v¯ª–~¸lñ @‡››Û¡C‡H›»ow ÿ,üE¡FÜ"¹iºÆx ýP#î™~køáëáêé6dæ*̇Ä؄ճJ ±Vf£¬H,€ÕØ4« `d•ÀgÅv5âžZ­¶(«øvú¡Fü‡3³ H,€ ±B‚Ä  $H,€ ±B‚Ä  $H,€ ±B‚Ä  $H,€ £ÄŠ~åîî>oÞ¼òòr‘1zS.\¸°¢¢Âpiuuu+V¬P(NNN³fÍ:}ú4³N· †™a”Xê×:×ÖÖFFFê^š™Ò9׳î”wïÞ ]½zµá¢bbbž~ú骪ªÖÖÖäää#GŽpº%0lYzJrvsñî°»ººÞ¥wóñãÇR©ÔpiR©´³³Ópz,EQ‡òóó‹Å4Mk4šwß}×ËËËÙÙyÕªUíííd²êêêeË–¹¹¹Éd²… 6662³ïÛ·O¡Px{{ýõ×ûöí9r¤··÷wß}G&ÈÍÍ –J¥þþþŸ~ú©î0,~€€C¬k4¼ö±DkkkJJJHHÈ S¶´´|øá‡S¦L1¼+44tÏž=wïÞÕí¤uÒÎt^½zõÚµk†¢¨}ûö_»v­±±Q&“íØ±ƒL³dÉ’Í›7755555MžX×h8¾¥(jìØ±aaaÇg½„Å‹Ÿ;w®«««¥¥åÀÌ»b¹\~çÎæzýõ×_{íµªª*FSVVÆ| ýøñcGGGggçššš¿üå/æcÕªUååå===Z­¶¯¯õæ€`pöÚ0äô†——jô.s6ðÌ™3³gÏ–Éd#FŒX¼xqUUéÿðÃÝÜÜ(c;Iš¦ûúúöíÛçïï/•JCBBNž£9²(DfícM¼$àg´f(,…GÅ\0KóC;øQñ Ë‰Df®8SXX8ÔCëü³b÷pÀÌw³8*ë\£=22Ò*Ë« ÇÃ( ¯XëMŠuŽŠñ¯à<4hެvT<è‚ðÌ”阘"³ÞÇšXâ À%s?y2šLÄÀ|ºÿ0Ýi‚Ÿ<‘åâ=À“xÂáÛ!Ab„‰$@HX!Ab„‰$@HX!Ab„‰$@HX!±ø¬18m"ÿ¡FüǺFÿÿN@BðJ®IEND®B`‚kamailio-4.0.4/doc/sip/figures/trapezoid.png0000644000000000000000000003052512223032460017515 0ustar rootroot‰PNG  IHDRÅ"â^ésBITÛáOà pHYsÐй‹çŸ IDATxœíÝy@yÿ?þ÷éœv**-a¢Ñ—² •¬E$%ÛX'ë rãcÛ0îa¸od_nŒJ‘BZÙÊ­)Ëe+-Gåúýñžûüδ)®s®sNÏÇ_ç\ëëtužç}-ïëâ1 Cà‹iq]€†@ž°y Àä);§ì@ž°y Àä);§ì@ž°y Àä);§ì@ž°y Àä);§ì@ž°y Àä);§ì@ž°y Àä);§ì@ž°y Àä);§ì@ž°y Àä);§ì@ž°y Àä);§ì@ž°y Àä);§ì@ž°y Àä);§ì@ž°y Àä);§j÷?M›62dÈÇ•³:…®¥‘Sò6½xñâ AƒôõõMMM'L˜PXX¨ÐÕi0ä©&`†a˜ÇwïÞ}ܸqJX—BWD¹ÛtóæÍ .|óæMvv¶±±q`` BW§ÉPsòQ(jkkˆoÙ²¥uëÖ|>ŸŽš>}º‘‘‘‘‘ÑŒ3„B!Ã0LJJ¢ÓÇÇÇ»¸¸˜™™½~ý𩬬477/,,¬{¥À:N¶)UVV¦«««˜¥ùÐ>ÕoÞ¼Y·n]—.]dCnÞ¼yûöm±XLY¶lY~~~NNÎÇŸ|H_?xðÀÚÚš¾?~ü–-[6oÞ¼hÑ"†aòòòZ·n]YYÉ0LPPЉ'j[©â>p²M†IOOoß¾}NNŽ‚>—ÆÃ·BíÕmU†óù|±XL_‹D"@@_ŸŸ¿mÛ6|ñ4¶©Ò } Àô`ò€ÈSv OØ<`ò€ÈSvàz~M³wïÞÝ»wáB† ¶fÍVêvaûª2䩦yùòeFF†@ð™777÷›o¾a·*` ¶¯*Cžj¦U«V™šš~Þ¼~~~ì¬ÃöUMÈS•——÷ñãÇÍÂçó;v쨠z€]ؾ*ç£4V||¼¨.\¸ÀuÕP_ؾ*íSÕ´iS‡ÍrïÞ=¬ÃöUAhŸ°y Àä);püTcݾ}»¡ÇËÞ¾};nÜ8Õóåx¼¿nÎãñ\\\®^½*?j÷îÝ 'Nœ¨2W@@À€‚‚‚†áñxÕ[ãpտϺæm_ €<ÕX]»v lÐ,ááá *†u†††‘‘‘¾¾¾²!“&MZµjÕ“'OllldŸ|ø bjj:f̘]»vÑ·{÷îõõõ533«ÏÒx§ŠAÓ!OA]YYYMžo>Ú§ì@žj,†aÄ $•J¹®Z£¸»»'''+háØ¾*ûûë¿ÿýovvvƒfyûöí„ TO#”œœœœœìææöÃ?¸¹¹±»pl_„<ÕXݺuÃù_U  TÅöUAØßP†äädwww…Î!O”©ªÙ§Ê†TÕT8~ª±JJJîܹӠYÞ¼y£ b º/<®Ší«‚§«W¯^eeeuL ¯¯/cÐ?ÿüsÈ! Zú}¹ÏNU%l_h(䩯º|ùr§Nj+‘H^¼x1fÌúöÑãGJŒz<ªC‡Ê*¾¶¯ Bžj,333OOÏÚÆŠÅâÓ§Oïܹ³gÏž÷îÝ8p ý{ûa‡5hè¦Y·ú´ß?{_ Û ç£µž={®ZµjĈEEïZi½üóÏ?¸®¨qssKJJJJJbýRl_%CûTc•——×qý¶T*mÑ¢E^^ÞªU«ž={öÿþ_'©”è+³ÂF‹•kû±}UòTcMŸ>½žSöèуòõ×Ûµ´°¿¢X,ö’ÂöUAÈSø ¾l ¥ ^üõ‡í«ÈSÅâWYY™ŽŽŽ™™™¡¡!ö U¶¯êCûTsÐÆK}žQ\O ÃüöÛoØ+TؾªíS !k¼°¸L7`À€±cDz¸Lø<ؾjyª!²²²:wîÌúb±W¨"°}Õö÷5Áýû÷§L™Ò¢E -¿  àæÍ› Z8|¶¯º@ž°ûûì@ž°y Àä);§ì@ž°yªfÞ½{Çu P3ä©Úˆ‹‹ëÓ§Off&×…€Zúá‡Þ¿Ïuyªê†‰ŠŠêÙ³ç°aî]»æèèÈuE ~víÚµfÍ—üü|®kÑdÈSÕ%•Jûí7GGÇQ£F¥§§Blllš5kÆu] ~FíààðàÁ77·çÏŸs]ŽÆBžª"±X|äÈ{{û€€€¬¬,Ùð®]»rX¨/ssóÄÄÄîÝ»çääôïßÿéÓ§\W¤™§ª¥²²rß¾}vvv“&MzðàA•±Ýºuã¤*ÐÍ›7¿té’““S^^^ÿþýóòò¸®H!OUÅÇÃÂÂlmmg̘‘››[ã4hŸÂ—011‰wqqyúô©››[NN×iÜ_Š{ååå»wïÞ´iÓ'ÏäççË?±à3”””x{{§¤¤X[['$$ØÙÙq]‘æ@žr,55ÕËË«¤¤ä“SZZZ¾|ùR %Æ+++1bDbb¢……EBB‚½½=×iìïs¬wïÞ³fͪÏs,°³l144Œ‰‰4hPAA»»;.jf ò”c`ãÆ§OŸ611©{Jä)°H__?::ÚËËëõë× ¸}û6×iä©JðññÉÈȨ;1‘§À.==½ÈÈHŸ·oßzzzâ‘'_yª*Úµk—––6mÚ´Ú&ÀÅRÀ:“'Oúùù½ÿ~РAiii\W¤Þ§*DOOâêêj``Pe”‰‰IÛ¶m¹( 4œ¶¶vxxøØ±c‹‹‹‡ ’’’ÂuEj yªB:´ÿþŒŒŒððð:Èrttd÷Ùë2àèÑ£'N,))6lXbb"ש+䩪ÈÊÊš3g!$,,lĈ·nÝòóó“ÅÁSP(>ŸèС©S§–••y{{_¼x‘ëŠÔòT%|øðÁÏϯ¼¼|Ú´i“'O&„¡wïÞ¦¦¦qqqqqqFFF½{÷æº"…Œ05´wï^--­%K–¬[·ŽërTÇý >EV!!dàÀò£„B¡••ÕãÇågyüø±•••P(¬òéê~«ááá„]]Ý[·n)í_îðáÃ|>Ÿ²bÅ ®kQ9ê”§™™™vvv•••ò£Ö¬Y3þ|ùY,X°zõjæSªü<ÍÎÎnÒ¤ !$,,LÉ«`Qxx8í­·xñb®kQ-ê”§ ÃL›6mÛ¶mòCÞ¼yÓ¢E‹÷ïßÓEEE¯_¿fT,OKKKéSÏÆ¯Ìõ(©S§èz‚ƒƒ¥Ré.MþkîââReÔîÝ»ýýý«Ïåïï¿{÷n:om;ßJÞ#W³<ÍÏÏoÛ¶mqq±ü¨9sælذ¾þùçŸgÍšUeÞÚÞ*ó=~üxBˆ½½}ii©BW QQQººº„yóæ}a¤jÌa=5ËS†aV¯^ýÿ÷òCrss[·n]YY)‰lllrrrªÌRŸ· FiÒ¤Ivv¶ÒV  hçΣ• ’H$Ÿ½9¬§~yZVVfkkûüùsù¿”ŸŸß¡C‡Ž;6jÔ¨êóÖç­âܺu‹þŒ‡‡‡+gJsáÂ}}}BÈÔ©S?;R5æ°žúå)Ã0‡š2eŠüëׯwîܹGW¯^­>o}Þ*ÈÛ·omll!sçÎUÂê”/!!ÁÐÐ2qâD±XüKИÃzj™§‰„ö#–Ÿ²oß¾½{÷®qÞÚÞ*ú-•J½¼¼!ÎÎά/@E\¾|¹iÓ¦„±cÇŠD¢†Î®‡õÜOZ¡Ö¯_¿lÙ2SSÓÛ·oõÕW\— @iiiÆ +..öóó;vì=û_O<Þ_A${Q^^îè蘔”ÔªU+YF3ÆÛÛ›>ã:22²Ê¼õy«pÊ ïF%11‘ÏçkiiÅÅÅq] €2ܸq£Y³f„Ÿí5?¬'£êýMÕT~~~`` D"Y¶lÙ!C¸.@œœœ.]ºdjjíëëûñãÇ/YÚĉÿýwù!ÎÎÎ&&&ÚÚÚ...õ_Ž2»•cŸ}b±ØÃÃ#55ÕÓÓóüùó´s@#‘™™9pàÀׯ_4(**Šžýo$Ð>eßÒ¥KSSS[¶lù믿"L¡±qppHJJ²°°¸xñ¢··wYY×)Ú§,‹ŠŠòõõIIIUnˆÐxôèÑ#''gÚ´iûöíãºUPPàééy÷î];;»„„kkk®+bòô‹0 3f̘ˆˆGGÇ´´´Fué2@C½~ýzàÀ™™™¶¶¶‰‰‰­Zµâº"–aÿ‹lݺ5""ÂÄÄäÔ©SS€º™››'&&vïÞ=''§ÿþOŸ>åº"–¡}úù®^½êîî.‹###GŽÉu9ꡨ¨hðàÁ7oÞlÓ¦Mbbb»ví¸®ˆ5hŸ~¦Â€€‘H‚0¨?“øøx—§OŸº¹¹åääp]kÐ>ý‰dÈ!—.]êÛ·obb"}v.Ô_II‰··wJJеµuBB‚×±íÓϱzõêK—.YXX?~a ðš6mëáᑟŸïæævïÞ=®+bÚ§ vþüy///ïîîÎu9jL(Ž9òâÅ‹æææñññ\WôEÐ>m˜gÏžM˜0A*•®Y³a ð…ôõõ£££½¼¼^¿~=`À€Û·os]ÑAû´*++ûõëwãÆ //¯³gÏ¢“>+*++ýýý£££›5kvþüy'''®+úLhŸ6@HHÈ7lll>Œ0` }ÄžŸŸßû÷ï ”––ÆuEŸ íÓú:~üx`` ®®î•+WèÓª€Eb±xâĉÇoÚ´iLLL¿~ý¸®¨ÁÐ>­—̘1ƒ²eË„)€"‚£GNœ8±¤¤dذa‰‰‰\WÔ`hŸ~ZYY™³³ó½{÷ÆôèQ®ËÐdR©tÆŒÐ×׊Š4h×5Ú§ŸtïÞ={{ûÝ»ws] €†ÓÒÒÚ»woPPP(ôññ‰åº¢@ž~ÂÎ;ýõ×&Mšœ:uÊÐÐër4Ÿ––ÖÎ;çÍ›÷ñãG__ßèèh®+ª/ìï×%==ÝÕÕµ¢¢"<<|ìØ±\—Ј0 úË/¿hkk‡‡‡=šëŠ> íÓZ½{÷n̘1sçÎE˜(ÇÛ¼yóâÅ‹E"ÑØ±c?ÎuEŸÆ_µj×5¨"†aüýýoݺåìì|üøq>ŸÏuE‘§§§T*MNNŽŠŠjÛ¶­ŠwHEžÖlÆ »ví255½téRóæÍ¹. ñrww ÑÑÑ­[·îÚµ+×Õ yZƒ¤¤¤©S§òx¼ˆˆˆîÝ»s]@cׯ_?}}ýøøø˜˜KKK•½yZU~~þ AƒJJJ–/_N¯áιºº_¸páܹs¦¦¦ªÙÇyú7b±xøðá÷ïß÷ôôÜ·oŸ–ÎרŠÞ½{›ššÆÅÅÅÅÅõîݛ늪BžþÍâÅ‹?Þ²eË‹/6mÚ”ëràoœœœ,--cccÏŸ?¯¯¯ïêêÊuEƒ<ýÿEEE-X°@[[;&&æ›o¾áº¨A=Z·n/Tê¶)ÈÓ¿äææzyy}üøqÓ¦Mþþþ\—µêÚµk»víΜ9“ •JUçÎî8>H!B¡ÐÏϯ¨¨hôèÑ ,àºø„‰'=zT ¬]»vÉ’%µM&•Jããã•Vú›BÈôéÓ÷ïßokk›žžnddÄu9P/"‘(88xÓ¦MUîò.•J'Ožœ™™™™™©¤‚˜FïàÁƒ„ƒÌÌL®k€†‰ŠŠÒÕÕ%„Ì›7O*•ʆK$’©S§Ò”»wïžrŠiìûûYYYsæÌ!„ìØ±£K—.\— ããã©§§·mÛ¶Y³fI¥RBÃ0³gÏ>pàFy}ÿ•Ûª©¸¸ØÖÖ–2mÚ4®k€ÏwáÂ}}}BÈÔ©SÅbñ¼yóäSÎÖÖV9e4Þã§ ÃŒ3&""ÂÑÑ1--n PS‰‰‰#FŒ(++«ñfT·nÝRB/ÕÆ»¿¿uëÖˆˆ“S§N!LÔ‡‡ÇåË—=<þüêÕ«Š®¡Ñå©D"?~ü‹/úöí»aîËlܸqÅŠuO®è2Ýù¨•+W®]»ÖÂÂâöíÛÖÖÖ\—_jË–-ÁÁÁŸœÌÜÜ[dd¤ŸŸ_=sìüùóƒV\1hÿÙ³g&LJ¥kÖ¬A˜h†‘#G¦¤¤,\¸ÐÆÆæ“+ú,ciŸVVVöë×ïÆ^^^gÏž­ÒÏÔÃ0·oß>}útdddvvvÓ¿zõJOOOA54–<7oÞöíÛmll222ð|=Í–ùßÿþ·JÄEFFŽ5JAëmyzüøñÀÀ@]]Ý+W®¨ì“¼€uOž<¡-Ö´´4zý©¿¿ÿ‰'´:ÍÏÓôìÙ³´´4,,lÖ¬Y\—xõêUTTTddäÍ›7Ÿ?Þ¤IE¬EóótÏž=AAAãÇ?zô(×µÇD"Qiii³fͱpÍÏSBHxx¸¯¯/½I"€‚4Š<P‚Ftý)€B!OØ<`ò€ÈSv OØ< Äû]]]WW׬¬¬œ9sF6Att´§§§ü”2ÜU jOu¯?e&%%eçÎwîÜÉÉÉQ³_dLMM;uêäãã3eÊÜqâÄ¥K—Nš4©Æ)Uóò*++}}} !ÿþ÷¿% W•ˆD¢µk×Ò§Q-^¼˜T­ç²²2{{û§OŸ~ûí·ø¼&aFFF~~þ«W¯þüóO###+++KKK{{{KKËÏXZZZš§§§P(yQ$íØ±ƒNµ°°ððð˜:uê²eËvïÞ]÷¹q‰DòW7øc) -è?þ¨q¬P(¾ýöÛº¹;wÎÁÁAþMòõéÓçÝ»w²É*++:´nݺ¹sçŽ1bÑ¢E òQûîÝ;ºÿ.>œÇã¹¹¹]ºt©¶µÏž=[…ò´¤¤„Çãikk‹Åâêc?~ü8pà@BHÓ¦MçÌ™síÚµ•+WBtttèó`†©¬¬Ü»woëÖ­«g.Õ³gÏÇË–9sæLBH³fÍ"##8@Ÿ{Êãñ~ýõ×+ %„¬Y³FŸ@s¸¹¹%%%±¾Xz6?55µÆ±çÎÓÖÖ&„èëë/^¼xýúõæææ„___YV­Y³ÆØØ¸z8ðùüI“&•••Ñ)E"}¦t‹-BCC§OŸN¯×ÖÖ>wî\ÐëaU%OBÈäÉ“òù4ýö±žªôÄQaaaõQÙÙÙt_sáÂ…R©T"‘8::BÜÝÝeaš 뤣£C=zôÑ£G---e§ûÛµk'kŸ‰D"wwwY>ˆD¢¹sçÒHýý÷ß«×píÚ5ÊÓGBÚ´iSãXÚ÷ôäÉ“ ÃH$úKõüƒŽ•J¥S¦L¡?2 .¤h[ÀÖ××ýû÷§µß~ûÎX\\L»çËvóiÂ9r¤z ÇŽ#„²þÙ4‰|»ÅT¥=kŠŠŠªÚ°a!dðàÁôíöíÛ !-[¶,--¥C222ôõõ !ÎÎÎô𨩩)ÇëÖ­M ]]]š!±±±t®ÒÒRkkkBHXX2lØ0BÈÊ•+«×MùÛDÑŸ—·oßÒÝê*®_¿Nquu%„=z4##ÃÊÊŠîòBV­ZuðàABˆD"±¶¶ž2e }ûÝwßÝ¿ÿÊ•+b±ØÉÉ©k×®<(((˜={¶§§g³fÍŒŒŒÂÂÂÆŽûý÷ß;ÖÐÐÐÅÅåܹsaaawîÜ©RÃüA©¬¬Tà_@³$'''''»¹¹ýðÃnnn_²(—/_®r…!„ž^§'EÞ½{·téRBÈöíÛé9äG 0@(B¦M›õîÝ»fÍš•””Bš4iòáÇŋoذáÙ³g&Lxøð¡©©©¡¡áöíÛ}}}—,Yмyóo¿ý666–~¨*5äçç¢2íÓ/^Ôý×lݺ5²W¯^„ƒÒ·ÅÅÅ´õÞ´iSºkÿþ}ooï1cÆH$’÷ïßwîܹC‡k×®¥Ë111!„Ìœ9S¶jºkpøða†aâââê.cøðáJÿÛ¨“Ú¾;_ØVýd/¦œœ†a6nÜHéÓ§lFÙyl--­¸¸¸~ýú¦§§¿zõªmÛ¶<oÓ¦M_}õ!„u6mšlvznêçŸfþ·]UélW^^N»Ô¦mÛ¶¾¾¾=êСƒAaa!½øÏž=AAAæææ¯_¿&„ÌŸ?ëÖ­B¡PWW—îò—””<þ¼sçΉĢ  @KK‹a˜'OžÐ¿ ]€.]ºTTT´ÿþO–Áæ'Ð,ußCö³Ûªþþþô;^#]]ݸ¸8שS§ììì3gÎ >œòòåËÖ­[Ó[Äñx<33³ëׯ[YYeddÐýÝçÏŸGDDlÛ¶-77×ÊÊêåË—4233i/ž3gÎøøøtêÔéÞ½{ à 2¤®ÔÏþ¹àÝÇ—?)Ô³gOBH«V­zôè1nܸں‹ᨠk222!Í›7¯¬¬¤C~úé'Bˆ½½}çΛ6m*;ÝTÅ/¿üB ÓÖÖ¦g¶7lØ@GUVVÒ³,UnÍW#uÊS©TJ[”‰‰‰tˆH$’m!“º»<-[¶ŒÞ¹ŠY±b…l”¿¿?!ä§Ÿ~b¥Îúü?멺`ÁBÈ÷ß/BÛ[„++«ˆˆˆ:æ]¼x1Ý£¥ç d£æÍ›GY¸pá' P§<½rå !¤eË–²F¨¬áݪU+Bˆ……E÷~¦}üe¨._¾\6êìÙ³„N:±R§rÿ'Ô»y*‰è]PåoÒ¦MBíNÙ¾}û/ þóŸÿȦ$„|ýõײQ·nÝ"„˜™™}²‡•:åéÖ­[ÉßO%Ñ<¥;ò|>ÿîÝ»uÌ~ëÖ-zÈ•^÷;qâDùåðù|W^^®ÀÐ(?I)zMN«V­ä~ýõײ•îܹ³ŽÙ“’’h2PU®Œ¤½7333ë®Aî9O¯X²³³“ ‘ù622Úµk×æÍ›åT‘­¥¥ehhèããCéСƒl”¶¶v»ví†ÉÉÉQTõð¿$MJJúÂ˧ª£}‚d;ø”žž!D[[»W¯^Íš5cj{GGÇo¾ùFKKËÉɉòòåKù±t±tuP§<¥¦cÇŽ²!`À€ Ã,Z´hïÞ½üöÛok|r_llìÔ©SKKKAaa!!„^&%Ó©S'ò¿ÈÖ).I)úå•ÏB½ùQ@@ÀŒ3ÆGû8U' ½½½³²²ÌÌÌhy]»v•Ÿ€.öÓùðyMkN´hÑ‚"ߟa˜ððpBí;AiÖ¬Y•C$yyyÁÁÁô²2BHóæÍy<ž––VAAüd‹/&èžðŪ‡Œ‚ÎæW1nÜ8"we:uéÒ%Bˆ¹¹9½5!dõêÕUfŒŒŒ¤7[!„ºçJ/H—¡w?~|Ý5¨Mž–––Btttª\% åop ­­-»©Ã0ÏŸ?§' ‚ƒƒéi}Úþ—?¹Oѿׄ ”ña4—ò“”¢{œ×®]“(•Jé))Bm999ÉŸµ^±bÇ[ºt)íJ3×ÆÆ¦Ê©”´´4Úh­»µÉSz)/=RíYO;GíÙ³G6¼¤¤„¦mÒ5kÖÐÎQmÛ¶ …UòÛo¿Büüüû14ò“”¢×9=yò¤Êð;vÿÝ|¯K—.òû¯´§?Ýa]¹r¥@  ÷L9þ|•…<~ü˜Ò²e˺kP›ã§´]YQQQ}T``àªU«†!„<|ø°¼¼œ700 wN)**Ò××ÿõ×_‹ŠŠ´´´8@—&.¹úphE'­ ½º¼zç¥Ù³gÿý÷R©”ÏçϘ1C~wöĉ„sss©TšššÊçó%I@@@õ[-ÓÅÒUÔ…e 'îù|~mL›6~"}}ýõë×?~<999==½cÇŽZZZôf 666²GW±wï^BÈôéÓö @è)嬬¬ê£$I`` ͇‹/ÒVjii)mrÉžt2eÊù†2™™™„{{ûºkP›¤?DÕyxxxxx$%%;wîÙ³g¯^½zþüy¯^½†:tèкïOóðáCò¿#Ö vèäéÕÕéèè,Z´hîܹ;vì8xðà“'OÊËËŸtèP___Ú½FDî^õµú’%›4i!dÓ¦MŠX8}œl•“ƒ .–/_N™?~=§—H$wïÞ­íJUÐ.üÕ¯ ªBmÎGB†J¡}íÙõàÁƒÇ›˜˜Té\ê‚æCLLL=§×ÒÒ²··—è^Î;'[EÔiРAZZZ—/_3f̧Þ qõêUBˆì 3 ^œsss×­[gooÏâ’ïÞ½›——gbbB»¢ÖAUî']O‡¢Šb]Ë–-333éýi@íÚµkÖ¬YŠ[xPPPÝÓ¨YžBJJJN:Åî2uttÆŽ‹Æ)€º»xñâÞ½{% ‹Ë¤®4è“Sª_ž¨&u: Ê§ì@ž°y Àä);§ìÐü<•=³Oö@Ô2OÏŸ?ßµkW]]ÝŽ;>|˜ër@Í(¨™¥~yzýúõ©S§þôÓO%%%±±±III\WÜS…f–úåé† 6lØ0xð`öíÛŸOo%[}JP/ŠkfÕ6¼Fê·¿ß§OŸØØØêÃÇŒ3gΜçÏŸK¥Òââb¦öûfYYY=yòD,ÓGH¥REÖ gbbòîÝ»êï_¿Þ¯_¿&MšðxqâÄû÷ï···oß¾ýïÿ;×-R5òê“y™lÉ«+**ruuõðð(,,Tyå4oÞ¼ÇwttDFF~ðÁìV+W®lll”ýcêîî¶°°¨ªªª©©±°°èîî&å›6m ihh¨¯¯f÷+g¿ûÝï?~,‰f̘!Õ`¤„'OžDEEÙØØðùüÐÐКšRîèèXZZJ–ëêêÆO– ¶¶–,8p`åÊ•dyÅŠ_|ñ…‚ªÓ)wppØ»wouuµä£JÉ'æÌ™C–çÏŸÿŸÿü‡,ÛÙÙÝ¿Ÿ,WVV²û•³‹¦¦&²,‰ ¤Œ^ÅÓ§Oׯ_ïççGV ôõõõõõõôž¿~#å’g»¥¥e̘1ÍÍÍMMM|>¿µµUÁ Õ †áñxýýýlQ__ŸžžY¾~ýzHHˆ¥¥å„ ¾ùæR¨Ä19r„,;v,((ˆ,ëëë÷õõ‘ågÏž±5(¸ vS®*†††dy„ ?–}ŽÔÙ^¶lÙÞ½{?ÿüóÐÐÐam¨6À0ŒÝ?üÀÝ»wÏÞÞ^êyYYYÖÖÖdy¸ÇP__/5R700¨««cÆÖÖvÀ¾\Á]`ÊU"(((//ï§Ÿ~jllüøãÙqàÎ;ßzë­ÊÊÊgÏž•––²#©³}áÂOOOOOÏK—. kCµ†abcc.\øàÁƒ¾¾¾,X° >>ž<¼bÅŠ»wïöôôœ={vܸqÊÞ={V­Z%Yµgφa6nÜÒØØØÐÐÂÖ à.ØU>ŸïÞ½W=#ÕÙ³ggÏžmlllnnÌö;ýýý‰‰‰ŽŽŽ†††Ó¦M;qâ)—ú-ô÷÷O˜0ÁÉɉ½Ø¢à†j ÃôôôÄÆÆÚÙÙéëëÛÙÙmÞ¼¹··—<üïÿ{Ò¤Ižžžß~û­rÇ0uêÔÜÜ\É’¼¼¼©S§2 #‰ÂÃÃGeeeµk×.ö¥‚»`WwìØajjŠ=:јOkÝ»wïí·ß~øð!× Aº†ûwø×¯_ßÒÒRWW÷—¿üeÉ’%\7é îS>iÒ$ww÷×_ÝÜÜ|ûöí\7é ± MS^^~õêUå¶uwwŸ={¶jÛ£´ÞŠGˆ¸zõêš5k”Û6::ZwRþ*Á#ÎÝÝ=::zÀ‡H7/§ÃÖœˆÃ«X0å#SZZZttôûï¿Ï~†O“½j_>XR†ÑÓÓãñxb±øwÐ+¢u…¤? ‹4¦)ƒüfµẻ)GºSŽ”}9¦iL9Ò}˜r¤ ±`Ê‘&Á”#݇)GÊÀ ¦iL9Röå˜r¤I0åH÷aÊ‘2pÄ€)GšSŽ”}9¦iL9Ò}Üßui#±`_Ž4ɈHù`3ýÉ™÷3ccãY³f‘ ŒäT.\øå/ill|øá‡FDDHmûÓO?¥¤¤8;;˯*...>>þâÅ‹½½½L9RŽX0åH“`Ê‘2°/À”#M‚)GºSþÂÅ‹GeaaÞÐÐ@ʆ‰cÇŽgÿMV.%++kÊ”)ÆÆÆgÏžeË%ONII‰››µ#S=±h«äää 6455•——3fåÊ•¤<555??¿¬¬¬´´4///==]~¹¤¢¢¢Õ«Wïß¿¿££ãÀÑÑÑ×®]“zΕ+WÞyç2¢‚Ò¯^½ ³fÍ¢T?m"‘ˆÂÇÇ';;›,gggûùùÉ/—´xñâdz«éééK–,!Ëäägffººº–••Ñ9ZN:‹-âº! ¡•ò+W®ÀìÙ³)ÕOÛÉ“'ýýýɲ¹¹y{{;YnkkãóùòË%YZZÖ×׳«uuuãÆ#ËpèÐ!77·ªª*JGAÏéÓ§ $$„ë†(¿E1€âââM›6]¸p¬vvvššš’e333¡P(¿\Rkk«……»jiiÙÚÚÊ®®]»öòåËŽŽŽ4ޱðÕ§´ÜÜÜ¥K—fdd¸ºº’SSÓÎÎN², ÍÌÌä—KÍÍÍìjss³@ `WÓÒÒ–/_^ZZJã@¨ÂWŸZ›òŒŒŒˆˆˆÌÌLooo¶ÐÃÃãæÍ›d¹¸¸ØÃÃC~¹$__߬¬,võܹs¾¾¾ì*™Ô<  ¸¸XåÇ‚^ 4ÊÉÉ€9sæPªŸ†äädÙ‚ôññ©­­­©©™9sfZZšürIÖÖÖ999===999ÖÖÖ………ä!öä?~ÜÂÂ"??ŸÚ‘©Þ™3g 88˜ë†(„Vʳ³³`îܹ”ê§A¶ ssŠÅ☘>ŸÏçóãââÄb1yþ`åRNŸ>íááahhèîî~êÔ)Éݱ˙™™€æÁ©¹ðÄuCB+å—.]€€€Jõ#niWÊq\ŽtŸ¥œÌv@©=Hµ´ë‹F\/ÏËËKHHÐÆ©1ëZ G, _cÁ¬#5Ј+‰˜u­£]}¹F|ò–ð÷÷Ç”#4¢/Gˆ*L9R†vX0åH÷aÊ‘îÔ#eàˆSŽ4 ¦)ûrL9Ò$˜r¤û0åH8bÀ”#M‚)GºSŽ”#L9Ò$8«-Rö娗#M‚)GºSŽ”#mK9Çóóó“*a—³²²¦L™bllìááAf"O%¹¡ìCdboâÔ©SóæÍ£}h0å¬Ñ£GgffÊ–­^½zÿþýˆŽŽ¾víHÜéNjY’ä-× 11qÇŽì£;vìHLL¤xHèg˜òç>ûì³>úèÙ³gRåIII;wîœ3gޱ±ñܹswìØ‘””¤Ü.~ýë_ÛÚÚ’; ž?ÞÞÞþW¿úÕ«¶›#8bД{yyùùù¥¤¤H•±«AAAJï…í¿¥úuD¦ü…O?ýtÏž=’…­­­쪥¥ekk«‚JËÀÓÓÓÉÉiË–-ÎÎΪj¹úiW_®A÷cáœMddäîÝ»wîÜÉ ‚ææf+++²ÚÜÜ,¬pÀ$$$8;;?|øðÕŒ„}ùKbbbNœ8QSSÖøúúfee±«çÎóõõ}•]¼öÚkìO¤Ø—¿ÄÄÄä£>úä“OØ’¸¸¸¥K—:::úùùlÙ²eÀK1#vX°/—QRR®úúú¦¦¦®[·ÎÔÔôƒ>HIIñññá°yH Ø—¼Ü'éééݸqCòÑE¶•\•ÓÏiK¨34¨/ …Û¶mkjj¢Ô$¤B8bfÊÛÚÚ¶mÛæààÐÛÛK©IhÄâxÄÒÖÖ¶wïÞ}ûöµµµQj ¢A»úrÎRŽùFjÃAÊ1ßHÍÔšrÌ·ÎÀ €LÊ1߈C´RÎn¾Ã„B!íV¡W$‰ ¯¯ë†(„z_~õêÕÔÔTÅ»ðòòòúúzJ­Bª%‹¹n‚B¨§<$$dÁ‚GŽILL¬®®rï¾úŠÏçSjR•»wï®ZµÊÀ€úX@%Ô1.722ŠŽŽŽŒŒT$ëîîî¶¶¶”Z…TE[zqB}ï}’¬WTT¤¤¤8::RÚ/B²Ôý?f©7ŸcÁ¬k;íº^Îå§µ0ëH=¸ÿL"f]a_0üÏ—“¬?zôhܸq”š„F,MI9Áãñ é´\š•r¤-pÄ€)GšSŽt¦)G,˜r¤I0åHØ—`Ê‘&Á”#݇)GÊÀ Bšûòç.^¼8jÔ( ‹ððð††RÎ0Lll¬@ ;vl||¼äÍ>,—2àürðò™)))qss£vdSþ³äää 6455•——3fåÊ•¤<555??¿¬¬¬´´4///==]~¹¤Áæ—“tåÊ•wÞyçèÑ£TNå´kÄòÒl}*´iÓ&øì³Ï(ÕO•H$266&Ë>>>ÙÙÙd9;;ÛÏÏO~¹¤Å‹>|˜]MOO_²d Y&g>33ÓÕÕµ¬¬ŒÎqPtûömðòòâº! ¡•ò7@rr2¥ú©:yò¤¿¿?Y677ooo'Ëmmm|>_~¹$KKËúúzvµ®®nܸqd:äææVUUEé(¨ºsçL™2…ë†(ïÒ/­¸¸xÓ¦MdVNèìì455%Ëfffì‘+—$~¹µk×^¾|¿8¢8.InnîÒ¥K322\]]I‰©©igg'Y …fffòË%‘ùåØU©ùåÒÒÒ–/_^ZZJã@$Lù ™™™ÞÞÞl¡‡‡ÇÍ›7Érqq1;Iç`å’äÏ/•””P\\¬òc¡ _}2 ì_¿öíÛG©~•KNNvpp}!xðàAŸÚÚÚššš™3g¦¥¥É/—TPP`mm““ÓÓÓ“““cmm]XXHbÏüñãÇ-,,òóó©d‚1OOO®¢Z)_·nìß¿ŸRý*'û÷/ †‹Å111|>ŸÏçÇÅʼnÅbòüÁÊ¥œ>}ÚÃÃÃÐÐÐÝÝýÔ©S’»c—333̓S=L9Ã0ÌŸþô'8pà¥ú‘Êõõõ)þdòrÂÃÃ^{TÇåè¹Ù³g§¦¦êäìe˜rô\UUÕš5k&Nœ¨HÖµëÕ'¦½¤ººZñ¬k L9€ŽeSŽ%'ë8bÀ”ëè×é~ŽE(>}ú”Ò.:‘¬'&&nÙ²%22’ëæ ¥+”«W¯œ!H‹XYY)øL{{û„„pss£”Õ¢Û—›™™5ŠÒ.j)8UÇ›6mÚäÉ“i·G…è¦|Û¶mQQQ”vTkÈIËx<^PPÐÖ­[§OŸþÃ?€ö¼úÔŽ™ì·$óÍu[”ߢ@òh{¾ L9˜ü|k×õrL9’¦Îþ[6!ªúËáñxlU˜rô'ã5üCÀ÷>Ñsßÿý™3g‰8½ËíÛ·]]]Ùš†qqq¹sçN||¼•••‰‰Ihh¨H$b[rèÐ!'''##£7ÞxƒÜ\€4Çã‘L9zîÈ‘#^^^\·¦Njnnž››KVsrr——WbbâÍ›7oݺU__ollÇn’ŸŸßÖÖ¶dÉòv$#ñm,jï}†……À±cÇ(Õ¸UYY ¤Ó}²)gæÀ+W®$OX±bÅ_|Á0Œ££cii))¬««?~<[CSSY‰Dl9»—#ŽÉf=,,ìã?niiaæÂ… €ššš©S§’ç‹ÅbÉh±7½111éëë“ݦi@°`Á‚£G2 óöÛo“OCÙØØÚÛÛ+Q!ŽË‘2h_/ŠŠúÇ?þ‘žžÎ~Bäüãûï¿ÿþý¾¾¾»w†Ê¯Ïç“!¦©SssóáÇ¥ y/#…óçÏoooïììœ;w.)Ù¼yó›o¾9oÞ<“ðððßþö·ò÷3}úôç¾â«‡Á,[¶ 222(Õ¸uÿþ}pqqQðùŸ|ò‰‰‰Ipp0Õ† ÇåHŠXúúúÒÓÓ·mÛVWWìunu”#ŠÎž=WVVÆ–tuu©¿˜r¤Œ!ûò[·nÅÄİoî°8éËñÕ'R±ü1""bÆŒ²± m×ÞÞ¾k×®}ûöuwwöœÁF,½½½]]]"‘ˆü”]P¤¼··wÀ¿"L9R†ÔˆåÙ³g)))Û·ooll”¿¡H$ ”J'™»F% {ö왡¡¡T!¦ü9çëë[PP Yžú¬¬¬¸¸¸ŠŠ WW×Ý»wà GÇ0 »¡Ô† øðÃ-ZDJN:uàÀK—.Q:(õøæ›obccɵÅ!uuu]ºtI6Ó†††£G611=z4» µªH¹lĨ]/'¿HÉ;vk8˜?þÿû_ɲð¿ÿýÏÆÆ&''§»»;;;ÛÆÆ¦¨¨HjÛWeOoQQÑŒ3ØÕéÓ§_»vM…G¡6UUUàèèÈ0Ì÷ßÿî»ï)øWqúôéüüüï¾û®¢¢âÉ“'­­­===T[‹)nß¾íææÖÛÛË–9J=SjuÀNdñâÅçÏŸg&++Kª-"™râÉ“'›7o–œ8i0jn-­”‡„„¿ZJõ«Iäþðvf6£r&4”z¦Ôê€)/))™={6Ã0³fÍb?JªudSN…Âýû÷;;;ËIyuuµš[‹W_òé§ŸîÙ³§££C²Pþ„†òÉ~BÃÓÓÓÉÉiË–-ÎÎÎι¥»^njjºnÝºŠŠŠ¯¿þÚÇÇgÀmÕ1Sþ›ÈÈÈÝ»wKÊŸÐP>É…-LHHصk×¶mÛTÑdM¤¯¯¿téÒÂÂÂÂÂÂ¥K—êëëK>Š)ç^LL̉'jjjØù*áµ×^cê6Ÿ¯¿þº¢¢bݺuì$À¼ÉOi$´páBÈÊÊÖVýýý”Ú3$ÉSqäÈ‘÷Þ{-‘3¡¡ì¶ÌPãò!Ò ?þø#L˜0AñMZZZvíÚegg7ÜT¼:Zçú­·Þr1aHb±øôéÓÓ§O¯©©¡Ôž!IÆ®¿¿Ÿ|-lBCÙmL¹\ýýý 4š$Çï 1 söìÙíÛ·³ó s…‘7ëééݸqCòÑrÕhÈm%W™gƒm¢u”þ®žžÞ¸qã(´HÎR®9ùF:ƒ”c¾‘š©5å˜owI9æqˆzʇ•ïÆÆFm¼Ä>544pÝ„a ›òï¾ûnÆ äŠX°`A}}=¥&!•à ööö3gμwïž‚óDZZZêéÑz;©¹ìÍu+Fé:üo~óÈËËc¦ªª*::Z‘Ïsø®–ÚÚZ°±±áº! QÇçXSRR***Ì:Bª¥¾OkaÖWÔý™D̺nЮëåÜ|ò³ŽÔ‰Ë ˜u¤Ü‹‚dýÁƒÑÑÑ8—´vЮ‹¦ÜÅÞÞ>%%…RcÐÇ}_Ž´‘võå˜r¤û0åH÷aÊ‘2pÄ€)GšSŽt¦)G,˜r¤I0åHØ—`Ê‘&Á”#݇)GÊÀ ¦iLù /^ 5j”……Exx8ûu†abccÁرcãããÙl°r)YYYS¦L166öðð8{ö,[.yrJJJÜÜܨÙˆGé[Ó“'O€»wïRªŸ†ÀÀÀ¬¬¬ÎÎÎúúúµk×Î;—”:tÈÇǧ¶¶¶¦¦fæÌ™iiiòË%É™_Ž=ù—/_vqq¹~ý:ýCT™––\7D!´Rîîîeee”ê§M$“eŸììl²œíçç'¿\’œùåHÊ333]]]µîDaʆaÈÿßòòrJõÓvòäI²lnnN&f¦­­ÏçË/—$g~98tè››[UU¥£ ‡Ì6à!k Z)Ÿ4iÜ»wRýTݼyÓÅÅ¥²²’¬êéé±SÁô÷÷ëëëË/—¤¯¯ß××Ç®öõõeÐÓÓ»zõ*¥£ J»RŽ·k“–››»téÒŒŒ WWWRbjjÚÙÙI–…B¡™™™ürIòç—KKK[¾|yii)A,¼Æò’ŒŒŒˆˆˆÌÌLooo¶ÐÃýaoqq1;Iç`å’äÏ/•””P\\¬òc¡J»®—Ó±¼þúëPQQA©~’““d_?..N,“çV.e°ùå$»˜ÌÌLίWøøø¤¤¤ôôô(òdL9Ã0Œ‹‹ Ü¿ŸRýHålllàçÛã ™õööv077WOÛ^ŽËÑKª««×¬Y3qâÄÔÔTï:¯ù0åhCf]»^}bÊÑ t¦_Ç”£!è@ÖéÞ'±¹¹of«HÖ·lÙ©]#Z×X&L˜ãÇçúø¢¬¬¬|¦½½ýþýûÀÔÔ”R~T‹n_Žs¾i±X¬ÈÓx<Þ´iÓ¦M›F»=*D7åß~û­¥] Õ²µµ•ÿ´uëÖéÓ§‹D"О‹¦Ü¿i2É|sÝe`Ê‘<ƒå[»^}bÊÑÀ´½ÿ–„)GÒt)ߦ½ x¾qÄ€)×B7nôòòâº!ª‡)GÏ9rDñ'kW_Žo٠݇ŸÖBºSŽ”#L9Ò$˜r¤û0åH8bÀ”#M‚)GÊÀ¾SŽ4 ¦é>L9RŽX0åH“`Ê‘îÔ#eàˆSŽ4 ¦)ûrL9Ò$˜r¤û0åH8bÀ”#MB÷{Ÿ˜r¤ xÚòOi-´`Ê‘îû?Aêµû°ÏLeIEND®B`‚kamailio-4.0.4/doc/sip/figures/event.eps0000644000000000000000000007171212223032460016643 0ustar rootroot%!PS-Adobe-2.0 EPSF-2.0 %%Title: event.dia %%Creator: Dia v0.91 %%CreationDate: Tue Jul 15 14:59:10 2003 %%For: janakj %%Orientation: Portrait %%Magnification: 1.0000 %%BoundingBox: 0 0 345 412 %%BeginSetup %%EndSetup %%EndComments %%BeginProlog [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def /cp {closepath} bind def /c {curveto} bind def /f {fill} bind def /a {arc} bind def /ef {eofill} bind def /ex {exch} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth pop} bind def /tr {translate} bind def /ellipsedict 8 dict def ellipsedict /mtrx matrix put /ellipse { ellipsedict begin /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc savematrix setmatrix end } def /mergeprocs { dup length 3 -1 roll dup length dup 5 1 roll 3 -1 roll add array cvx dup 3 -1 roll 0 exch putinterval dup 4 2 roll putinterval } bind def /dpi_x 300 def /dpi_y 300 def /conicto { /to_y exch def /to_x exch def /conic_cntrl_y exch def /conic_cntrl_x exch def currentpoint /p0_y exch def /p0_x exch def /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def /p2_x p1_x to_x p0_x sub 1 3 div mul add def /p2_y p1_y to_y p0_y sub 1 3 div mul add def p1_x p1_y p2_x p2_y to_x to_y curveto } bind def /start_ol { gsave 1.1 dpi_x div dup scale} bind def /end_ol { closepath fill grestore } bind def 28.346000 -28.346000 scale -0.300000 -14.550074 translate %%EndProlog 0.100000 slw [] 0 sd [] 0 sd 0 slc 0.000000 0.000000 0.000000 srgb n 1.850000 1.050000 m 1.800000 14.450000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.830000 0.980000 m 9.850000 14.500000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 1.850000 3.050000 m 9.050000 3.050000 l s 0 slj n 9.050000 3.450000 m 9.850000 3.050000 l 9.050000 2.650000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.850000 5.050000 m 2.650000 5.050000 l s 0 slj n 2.650000 4.650000 m 1.850000 5.050000 l 2.650000 5.450000 l f gsave 4.050000 2.650000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 4.456400 2.650000 translate 0.035278 -0.035278 scale start_ol 2560 3392 moveto 2560 1091 lineto 2560 651 2220 381 1659 381 curveto 1400 381 1190 439 1022 552 curveto 849 678 768 844 768 1091 curveto 768 3392 lineto 384 3392 lineto 384 1083 lineto 384 415 869 0 1659 0 curveto 2441 0 2944 424 2944 1083 curveto 2944 3392 lineto 2560 3392 lineto end_ol grestore gsave 4.896667 2.650000 translate 0.035278 -0.035278 scale start_ol 384 0 moveto 1855 0 lineto 2163 0 2391 88 2566 282 curveto 2727 458 2816 698 2816 962 curveto 2816 1369 2632 1614 2204 1780 curveto 2478 1925 2624 2178 2624 2525 curveto 2624 2773 2537 2994 2370 3153 curveto 2204 3317 1985 3392 1679 3392 curveto 384 3392 lineto 384 0 lineto 768 1920 moveto 768 3011 lineto 1581 3011 lineto 1816 3011 1947 2978 2059 2889 curveto 2177 2795 2240 2654 2240 2466 curveto 2240 2282 2177 2136 2059 2042 curveto 1947 1953 1816 1920 1581 1920 curveto 768 1920 lineto 768 381 moveto 768 1536 lineto 1823 1536 lineto 2032 1536 2172 1485 2274 1375 curveto 2376 1269 2432 1122 2432 956 curveto 2432 795 2376 648 2274 542 curveto 2172 432 2032 381 1823 381 curveto 768 381 lineto end_ol grestore gsave 5.303067 2.650000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 5.709467 2.650000 translate 0.035278 -0.035278 scale start_ol 2977 2327 moveto 2845 3043 2428 3392 1702 3392 curveto 1258 3392 900 3254 655 2988 curveto 355 2668 192 2207 192 1683 curveto 192 1150 360 693 673 377 curveto 927 120 1254 0 1685 0 curveto 2492 0 2946 428 3046 1288 curveto 2604 1288 lineto 2567 1069 2521 920 2452 793 curveto 2314 530 2028 381 1668 381 curveto 1000 381 576 889 576 1687 curveto 576 2507 982 3011 1631 3011 curveto 1903 3011 2157 2932 2295 2809 curveto 2419 2700 2488 2564 2539 2327 curveto 2977 2327 lineto end_ol grestore gsave 6.149733 2.650000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 6.590000 2.650000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 6.759333 2.650000 translate 0.035278 -0.035278 scale start_ol 384 0 moveto 1855 0 lineto 2163 0 2391 88 2566 282 curveto 2727 458 2816 698 2816 962 curveto 2816 1369 2632 1614 2204 1780 curveto 2478 1925 2624 2178 2624 2525 curveto 2624 2773 2537 2994 2370 3153 curveto 2204 3317 1985 3392 1679 3392 curveto 384 3392 lineto 384 0 lineto 768 1920 moveto 768 3011 lineto 1581 3011 lineto 1816 3011 1947 2978 2059 2889 curveto 2177 2795 2240 2654 2240 2466 curveto 2240 2282 2177 2136 2059 2042 curveto 1947 1953 1816 1920 1581 1920 curveto 768 1920 lineto 768 381 moveto 768 1536 lineto 1823 1536 lineto 2032 1536 2172 1485 2274 1375 curveto 2376 1269 2432 1122 2432 956 curveto 2432 795 2376 648 2274 542 curveto 2172 432 2032 381 1823 381 curveto 768 381 lineto end_ol grestore gsave 7.165733 2.650000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 4.700000 4.550000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 5.038667 4.550000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 5.377333 4.550000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 5.716000 4.550000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 5.885333 4.550000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 6.359467 4.550000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 1.350000 1.050000 m 2.350000 1.050000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.330000 0.980000 m 10.330000 0.980000 l s gsave 0.300000 0.750000 translate 0.035278 -0.035278 scale start_ol 2560 3392 moveto 2560 1091 lineto 2560 651 2220 381 1659 381 curveto 1400 381 1190 439 1022 552 curveto 849 678 768 844 768 1091 curveto 768 3392 lineto 384 3392 lineto 384 1083 lineto 384 415 869 0 1659 0 curveto 2441 0 2944 424 2944 1083 curveto 2944 3392 lineto 2560 3392 lineto end_ol grestore gsave 0.740267 0.750000 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore gsave 1.045067 0.750000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 1.383733 0.750000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 1.586933 0.750000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 1.756267 0.750000 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 2.162667 0.750000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 2.501333 0.750000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 2.840000 0.750000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 3.178667 0.750000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 8.850000 0.700000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 9.256400 0.700000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 9.595067 0.700000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 9.798267 0.700000 translate 0.035278 -0.035278 scale start_ol 1313 0 moveto 2239 2432 lineto 1806 2432 lineto 1124 459 lineto 479 2432 lineto 46 2432 lineto 894 0 lineto 1313 0 lineto end_ol grestore gsave 10.103067 0.700000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 10.441733 0.700000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.855000 7.310000 m 2.655000 7.310000 l s 0 slj n 2.655000 6.910000 m 1.855000 7.310000 l 2.655000 7.710000 l f gsave 4.655000 8.760000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 4.993667 8.760000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 5.332333 8.760000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 5.671000 8.760000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 5.840333 8.760000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 6.314467 8.760000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore gsave 4.600000 6.850000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 5.040267 6.850000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 5.514400 6.850000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 5.886933 6.850000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 6.056267 6.850000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2372 1536 lineto 2372 1920 lineto 768 1920 lineto 768 3011 lineto 2593 3011 lineto 2593 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1536 lineto end_ol grestore gsave 6.428800 6.850000 translate 0.035278 -0.035278 scale start_ol 1728 1331 moveto 2991 3392 lineto 2479 3392 lineto 1542 1740 lineto 579 3392 lineto 49 3392 lineto 1344 1331 lineto 1344 0 lineto 1728 0 lineto 1728 1331 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 1.855000 9.195000 m 9.055000 9.195000 l s 0 slj n 9.055000 9.595000 m 9.855000 9.195000 l 9.055000 8.795000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 12.300000 10.200000 m 10.699306 10.266696 l s 0 slj n 10.682654 9.867042 m 9.900000 10.300000 l 10.715959 10.666349 l f gsave 10.900000 9.900000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 11.306400 9.900000 translate 0.035278 -0.035278 scale start_ol 1313 0 moveto 2239 2432 lineto 1806 2432 lineto 1124 459 lineto 479 2432 lineto 46 2432 lineto 894 0 lineto 1313 0 lineto end_ol grestore gsave 11.611200 9.900000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 11.949867 9.900000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 12.288533 9.900000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.805000 11.045000 m 2.605000 11.045000 l s 0 slj n 2.605000 10.645000 m 1.805000 11.045000 l 2.605000 11.445000 l f gsave 4.655000 10.695000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 5.095267 10.695000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 5.569400 10.695000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 5.941933 10.695000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 6.111267 10.695000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2372 1536 lineto 2372 1920 lineto 768 1920 lineto 768 3011 lineto 2593 3011 lineto 2593 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1536 lineto end_ol grestore gsave 6.483800 10.695000 translate 0.035278 -0.035278 scale start_ol 1728 1331 moveto 2991 3392 lineto 2479 3392 lineto 1542 1740 lineto 579 3392 lineto 49 3392 lineto 1344 1331 lineto 1344 0 lineto 1728 0 lineto 1728 1331 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 1.855000 12.945000 m 9.055000 12.945000 l s 0 slj n 9.055000 13.345000 m 9.855000 12.945000 l 9.055000 12.545000 l f gsave 4.705000 12.595000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 5.043667 12.595000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 5.382333 12.595000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 5.721000 12.595000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 5.890333 12.595000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 6.364467 12.595000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore showpage kamailio-4.0.4/doc/sip/figures/trapezoid.dia0000644000000000000000000000257712223032460017474 0ustar rootroot‹í[Qâ6~¿_±/­Ä™Ø‰“øXöt·j«“ªk¥n+õ b ½ ',KúÛ;Y–…ÐÁˆ ÂjÑnŒ¿Œ=ß|öŒÃíǧil= ™EiÒkad·,‘ Ó0JƽÖï?¾ZïÞ݆ÿ¿cɧôH2õ_¯5ÉóÙ‡Ng±X x™ñ<•(Žæ(xó|¨ÓºÛìòœ¯.ð<—Ñ`ž +áSÑk øðÛX¦ó$,{ Ó8•Ö#{­›Qñjuîn;¯úVCÍøLÈ5Êt–f´åËÙVÛv?õ^6eÐ’Œïn>¹7«[–jÝ?Ÿr9Ž’J ¯†AP@©;Š”ØŠ4‚eýY*sÉ£¼D¤i,x²Ëå\ԅʆ<ì„댢ÖCšˆh<ÉwãQbí1f“tÑßY×Ö¼QÍú“TF§IÎc-ñÚ‹ÜÊ£¡´Ã*E¶Á‹¥ÎÞ°_EÀÑ€u£` ª †yF"B!®‹i—¸Èw=§ˆˆÀ5‡™ÜDÅ%GÅO¨€÷°>AGi}÷.„ü¾"Dˆnˆ0äÑ6EÄHt˜‹Vx¸‹1"Nà´®k»F¢CÛÚÿ `³0#1Fà§ CQ™k‰§|Ïp£igª‹ln+×; ÓÁÔ–(êOkħQ¼|žd-+Ë—j·A¸úô}:—ð·æÐö°ÊFAíI® ml#×Õ*@cõ°HÇ+Rs»xÕE¬mœLÅzÖD2Ÿ‘œ@*]©Ä¾I­Th –¥V2óZ©on#–X^­X×"–¿ …\† –?G•y–««Œx7¡Œù>cmÄ¡~—ABD¬ð±q}Ï$I_$aaæ›6ïì3`Býªøk8;¸„Vö¹”ébÇýŽ6D?ɸBÀ5âã«z-Øu,ë¨özŒ‘×ögd5†N-–A–M¸çšâÙ¦•»+4¡:#hhv<ÍòUe‘˜`]‘ (Žu Fn»~9ð-¦m¹[ÉTÅ}44Ó ÙƒÚÇïÒ,Ð?1RÔ°6s`` ¹Ä§]RFR@ÓúÒi0qùòõ/?\Iú¢íÅS'.öI—=ägÚk;H"lÀŒp¿À¢+îcŠRìíîŸ>u×tâERÛŽ:J2ˆ©ƒQU¾¤>°_í0ÉJú=ÖÐÿÄô×÷ãe€öÓ°5-ÎÙ}#ê_‚Ù›ò¯.žEÿ?ÿy5â¯ëÄË$ÿçþ´­6fŽýiÛALI¿ª£©]?EöY”ÿW™>--|%ä×uâerÿƒ\Çù °gö3䜟ýäZ¤_×—IíÓ:‚lÚ&†.À0ò»%ªO›3ê“íôuw™„§ç&¶¿¸`ÛàC¼Ž§It;†8-Z"l!´º·›x­÷ÝÝûwϼªŽŽö ýîð§Æ5ªµ¶$ÜFyöæÎÖ¼êžTL/6›þÍŠjÔï£â:Ëÿôž{o“l£Ï¸qfðåx~:øÉßsIÚº¹àUv{» b|(›Îš†™Noy3+Ât‘D÷‘Æ8bÕ/J¢åd–åé­ç¶š2Ä~LkŠ8·Â¹ŠâÅUñ¨1½×WÙõäQÈjmg—I¼š\eyüW–abåK{ñ€{ôzïУ[m"ïN\n•/áÖV(úŸÞe¸Œ“-à‡ézà­‹­V&è¬üé·Ù&µ /m¡|ԘߵêUˆKIè®m,;“:Õ¤Q¾ùhŠv‘.£Û»¥›å¿ððÑÝ=i}¯½ Ðèê*K£Ķv…]ØGJÂçûHñ±DBÚ °ÂNüÐf¤O¸!A‚ ìÆToŠÎLш­İ9ž’@p#¦% n$¸“€ÍH{ ³¾€ ø œ˜{_]d×Qþu¨­0w!–VC…T0Æ\k«¥bNä`5ЧdÄ…TNôÒ"ðÙë¡Ï“?£<"’&¸ÖcÝH“÷U˜u…ÀwTœ1Åú¡ÞUÀwSÐcê˽íí‘ùò.•,CÞ Ý °*C],ÆW™!s–Z´Ï {‹ì¬EúUfÈ:“>]= ¬M‘;2EÂtý覂J„®§T«¡öõ£î–PE€ƒ8•ƒË¼“¤r.e`1Ø^Ç'„s`Ü<Ìç „ãZ H[ (,ÅÈI­@!ÌjBAÀ$ë®5AáÍù6ËÒt¥s3ÊO y·bD‘ÔÑ¢4Ñ{¶jÝÜ„yž]?J¨5Ä$‰ÒE\-’¥;¬zåïú·¬SÖµzŽ„™&'eèf(ACjèË’t ØèŠtF¼{ ž õëážtÿ#é°}×D+®'Š9Y88&}¡)|ްälT!Œ2WÌÛõîQ¦yÉ,Z¥zîYpïu>«£žu¿ LS ‰áÄð$¾PÆñ§j¬Á¹Ä_›‚á'ˆwÌ;ÇdéxÍY7Ûä¢É<^Ã…Ì¢Góô´ø= ëYx¡«@54´î̬ ”N⮃\Èõà¡BãS¼¹½:¬|aä½{ÿÛ»‹oOº²:j;—ÿuùËw_þj û>‘™G7KÌGÌh@!ßhHìF]‘€íT§ì› c5%Z'i°ÃHi @Î (ò"Þ1 XOåqJ€·ˆÒQm Å51@¢Àòæƒ @Þ›ß;lfñ8¹o½ö'o’'ì7` <…Þ?Qæ?>RáþYÔâ[OáqR¿ÅŠ*«ÜT+4"„YóóIéüX‚ýo²iWž}í§ñ8ùo½Ü§ËÈàÖNƒ%˼_/ÒúåA¬•g·(D€Ó™å\§ì×Âeì䎚B±I€L €)©*@BNÓNÉÀ~>R Äo±(%t‹’àŽ:  šo„ n„ ” aDE¬+ùõL§ìßGB¨Þ^æf#5Ó•8Q> Q6ì*ƒìJ °Æ/ý­öÊÒ¢.ªø:ª‹*†Õ˜}Ôq*é0Лe|G©íÆÚo˜é7Ìtv+µ@‚‚X½_ÆgG¼c¦A–ЦeÀçàc®lQ×n«¶…¨®›Ðu |óþÜ;r¸G'ÝY2²›Ë/;S®o`$ÌþÝUPÀ‡ÒÍFݯªÂH6wÒ¹ ‚g-Û÷‡ü˜pŒÙ?ÑÀèC … } £•ól>äÎ*؆UÛ,Ü‹=±ä¿ûÙ;ÿå7ïäLfÚ±R¶Õ¤¥'[¯ãƒmJÒG»h¨/ÀaJq =Yá@êÂ*QÌYSùƒ1׆f…”;O¦”¨ ·d+K-x¨t õÝ,¨4ÈLY÷M r22E^—T¬'ô8ËÉÒ¾œ,4u…£7æÚ)¥ÀÒ)!%L åâJÐÕÆ0Ýz¯¿ Xt…»™Ê㔀j±¨H½õœFÍ"'†ÿPþ¿éÿíæñó'ÿèöw8<üg'ÿT‡%_/gkamailio-4.0.4/doc/sip/figures/message.eps0000644000000000000000000011334412223032460017144 0ustar rootroot%!PS-Adobe-2.0 EPSF-2.0 %%Title: message.dia %%Creator: Dia v0.91 %%CreationDate: Tue Jul 15 14:59:28 2003 %%For: janakj %%Orientation: Portrait %%Magnification: 1.0000 %%BoundingBox: 0 0 440 413 %%BeginSetup %%EndSetup %%EndComments %%BeginProlog [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def /cp {closepath} bind def /c {curveto} bind def /f {fill} bind def /a {arc} bind def /ef {eofill} bind def /ex {exch} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth pop} bind def /tr {translate} bind def /ellipsedict 8 dict def ellipsedict /mtrx matrix put /ellipse { ellipsedict begin /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc savematrix setmatrix end } def /mergeprocs { dup length 3 -1 roll dup length dup 5 1 roll 3 -1 roll add array cvx dup 3 -1 roll 0 exch putinterval dup 4 2 roll putinterval } bind def /dpi_x 300 def /dpi_y 300 def /conicto { /to_y exch def /to_x exch def /conic_cntrl_y exch def /conic_cntrl_x exch def currentpoint /p0_y exch def /p0_x exch def /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def /p2_x p1_x to_x p0_x sub 1 3 div mul add def /p2_y p1_y to_y p0_y sub 1 3 div mul add def p1_x p1_y p2_x p2_y to_x to_y curveto } bind def /start_ol { gsave 1.1 dpi_x div dup scale} bind def /end_ol { closepath fill grestore } bind def 28.346000 -28.346000 scale -0.300000 -14.550074 translate %%EndProlog 0.100000 slw [] 0 sd [] 0 sd 0 slc 0.000000 0.000000 0.000000 srgb n 1.850000 1.050000 m 1.800000 14.450000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 8.130000 0.980000 m 8.150000 14.500000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 1.850000 3.050000 m 7.400025 3.006299 l s 0 slj n 7.403174 3.406287 m 8.200000 3.000000 l 7.396875 2.606311 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 8.150000 5.100000 m 2.649975 5.056349 l s 0 slj n 2.653149 4.656362 m 1.850000 5.050000 l 2.646800 5.456336 l f gsave 3.750000 2.650000 translate 0.035278 -0.035278 scale start_ol 2120 0 moveto 3072 2843 lineto 3072 0 lineto 3456 0 lineto 3456 3392 lineto 2882 3392 lineto 1897 437 lineto 894 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2843 lineto 1665 0 lineto 2120 0 lineto end_ol grestore gsave 4.258000 2.650000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 4.664400 2.650000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 5.070800 2.650000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 5.477200 2.650000 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 5.883600 2.650000 translate 0.035278 -0.035278 scale start_ol 3264 1792 moveto 1836 1792 lineto 1836 1408 lineto 2880 1408 lineto 2880 1324 lineto 2880 777 2429 381 1803 381 curveto 1455 381 1140 499 938 705 curveto 712 933 576 1315 576 1709 curveto 576 2494 1056 3011 1780 3011 curveto 2302 3011 2678 2757 2772 2339 curveto 3217 2339 lineto 3097 3008 2580 3392 1808 3392 curveto 1397 3392 1065 3290 802 3081 curveto 414 2770 192 2269 192 1687 curveto 192 693 824 0 1732 0 curveto 2188 0 2553 167 2880 524 curveto 2988 0 lineto 3264 0 lineto 3264 1792 lineto end_ol grestore gsave 6.357733 2.650000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 3.950000 4.750000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 4.288667 4.750000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 4.627333 4.750000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 4.966000 4.750000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 5.135333 4.750000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 5.609467 4.750000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 1.350000 1.050000 m 2.350000 1.050000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 7.530000 0.980000 m 8.700000 1.050000 l s gsave 0.300000 0.650000 translate 0.035278 -0.035278 scale start_ol 2560 3392 moveto 2560 1091 lineto 2560 651 2220 381 1659 381 curveto 1400 381 1190 439 1022 552 curveto 849 678 768 844 768 1091 curveto 768 3392 lineto 384 3392 lineto 384 1083 lineto 384 415 869 0 1659 0 curveto 2441 0 2944 424 2944 1083 curveto 2944 3392 lineto 2560 3392 lineto end_ol grestore gsave 0.740267 0.650000 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore gsave 1.045067 0.650000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 1.383733 0.650000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 1.586933 0.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 1.756267 0.650000 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 2.162667 0.650000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 2.501333 0.650000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 2.840000 0.650000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 3.178667 0.650000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 7.300000 0.700000 translate 0.035278 -0.035278 scale start_ol 832 1408 moveto 1879 1408 lineto 2141 1408 2348 1488 2527 1654 curveto 2729 1843 2816 2065 2816 2381 curveto 2816 3028 2451 3392 1803 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1408 lineto 832 1792 moveto 832 3011 lineto 1719 3011 lineto 2126 3011 2368 2782 2368 2402 curveto 2368 2021 2126 1792 1719 1792 curveto 832 1792 lineto end_ol grestore gsave 7.706400 0.700000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 7.909600 0.700000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 8.248267 0.700000 translate 0.035278 -0.035278 scale start_ol 1346 1258 moveto 2157 2432 lineto 1723 2432 lineto 1143 1550 lineto 562 2432 lineto 124 2432 lineto 931 1239 lineto 78 0 lineto 516 0 lineto 1129 933 lineto 1733 0 lineto 2180 0 lineto 1346 1258 lineto end_ol grestore gsave 8.553067 0.700000 translate 0.035278 -0.035278 scale start_ol 1788 2432 moveto 1120 533 lineto 502 2432 lineto 92 2432 lineto 908 -16 lineto 760 -403 lineto 700 -575 613 -640 452 -640 curveto 396 -640 332 -631 249 -612 curveto 249 -963 lineto 327 -1005 406 -1024 507 -1024 curveto 631 -1024 765 -982 866 -908 curveto 986 -819 1055 -717 1129 -521 curveto 2203 2432 lineto 1788 2432 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 14.455200 0.995186 m 14.405200 14.395200 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 13.905000 1.095000 m 14.905000 1.095000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 8.155390 3.995370 m 13.705425 3.951669 l s 0 slj n 13.708574 4.351657 m 14.505400 3.945370 l 13.702275 3.551681 l f gsave 12.755000 0.795000 translate 0.035278 -0.035278 scale start_ol 2560 3392 moveto 2560 1091 lineto 2560 651 2220 381 1659 381 curveto 1400 381 1190 439 1022 552 curveto 849 678 768 844 768 1091 curveto 768 3392 lineto 384 3392 lineto 384 1083 lineto 384 415 869 0 1659 0 curveto 2441 0 2944 424 2944 1083 curveto 2944 3392 lineto 2560 3392 lineto end_ol grestore gsave 13.195267 0.795000 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore gsave 13.500067 0.795000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 13.838733 0.795000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 14.041933 0.795000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 14.211267 0.795000 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 14.617667 0.795000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 14.956333 0.795000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 15.295000 0.795000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 15.633667 0.795000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 9.805000 3.595000 translate 0.035278 -0.035278 scale start_ol 2120 0 moveto 3072 2843 lineto 3072 0 lineto 3456 0 lineto 3456 3392 lineto 2882 3392 lineto 1897 437 lineto 894 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2843 lineto 1665 0 lineto 2120 0 lineto end_ol grestore gsave 10.313000 3.595000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 10.719400 3.595000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 11.125800 3.595000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 11.532200 3.595000 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 11.938600 3.595000 translate 0.035278 -0.035278 scale start_ol 3264 1792 moveto 1836 1792 lineto 1836 1408 lineto 2880 1408 lineto 2880 1324 lineto 2880 777 2429 381 1803 381 curveto 1455 381 1140 499 938 705 curveto 712 933 576 1315 576 1709 curveto 576 2494 1056 3011 1780 3011 curveto 2302 3011 2678 2757 2772 2339 curveto 3217 2339 lineto 3097 3008 2580 3392 1808 3392 curveto 1397 3392 1065 3290 802 3081 curveto 414 2770 192 2269 192 1687 curveto 192 693 824 0 1732 0 curveto 2188 0 2553 167 2880 524 curveto 2988 0 lineto 3264 0 lineto 3264 1792 lineto end_ol grestore gsave 12.412733 3.595000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 14.411300 6.045370 m 8.911325 6.001719 l s 0 slj n 8.914499 5.601732 m 8.111350 5.995370 l 8.908150 6.401706 l f gsave 10.505000 5.645000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 10.843667 5.645000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 11.182333 5.645000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 11.521000 5.645000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 11.690333 5.645000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 12.164467 5.645000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 14.411300 8.045370 m 8.911325 8.001719 l s 0 slj n 8.914499 7.601732 m 8.111350 7.995370 l 8.908150 8.401706 l f gsave 10.005000 7.745000 translate 0.035278 -0.035278 scale start_ol 2120 0 moveto 3072 2843 lineto 3072 0 lineto 3456 0 lineto 3456 3392 lineto 2882 3392 lineto 1897 437 lineto 894 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2843 lineto 1665 0 lineto 2120 0 lineto end_ol grestore gsave 10.513000 7.745000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 10.919400 7.745000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 11.325800 7.745000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 11.732200 7.745000 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 12.138600 7.745000 translate 0.035278 -0.035278 scale start_ol 3264 1792 moveto 1836 1792 lineto 1836 1408 lineto 2880 1408 lineto 2880 1324 lineto 2880 777 2429 381 1803 381 curveto 1455 381 1140 499 938 705 curveto 712 933 576 1315 576 1709 curveto 576 2494 1056 3011 1780 3011 curveto 2302 3011 2678 2757 2772 2339 curveto 3217 2339 lineto 3097 3008 2580 3392 1808 3392 curveto 1397 3392 1065 3290 802 3081 curveto 414 2770 192 2269 192 1687 curveto 192 693 824 0 1732 0 curveto 2188 0 2553 167 2880 524 curveto 2988 0 lineto 3264 0 lineto 3264 1792 lineto end_ol grestore gsave 12.612733 7.745000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 8.111350 8.995370 m 2.611325 8.951719 l s 0 slj n 2.614499 8.551732 m 1.811350 8.945370 l 2.608150 9.351706 l f gsave 3.705000 8.695000 translate 0.035278 -0.035278 scale start_ol 2120 0 moveto 3072 2843 lineto 3072 0 lineto 3456 0 lineto 3456 3392 lineto 2882 3392 lineto 1897 437 lineto 894 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2843 lineto 1665 0 lineto 2120 0 lineto end_ol grestore gsave 4.213000 8.695000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 4.619400 8.695000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 5.025800 8.695000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 5.432200 8.695000 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 5.838600 8.695000 translate 0.035278 -0.035278 scale start_ol 3264 1792 moveto 1836 1792 lineto 1836 1408 lineto 2880 1408 lineto 2880 1324 lineto 2880 777 2429 381 1803 381 curveto 1455 381 1140 499 938 705 curveto 712 933 576 1315 576 1709 curveto 576 2494 1056 3011 1780 3011 curveto 2302 3011 2678 2757 2772 2339 curveto 3217 2339 lineto 3097 3008 2580 3392 1808 3392 curveto 1397 3392 1065 3290 802 3081 curveto 414 2770 192 2269 192 1687 curveto 192 693 824 0 1732 0 curveto 2188 0 2553 167 2880 524 curveto 2988 0 lineto 3264 0 lineto 3264 1792 lineto end_ol grestore gsave 6.312733 8.695000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 1.805390 10.995400 m 7.355415 10.951699 l s 0 slj n 7.358564 11.351687 m 8.155390 10.945400 l 7.352265 10.551711 l f gsave 4.155000 10.645000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 4.493667 10.645000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 4.832333 10.645000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 5.171000 10.645000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 5.340333 10.645000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 5.814467 10.645000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 8.155390 11.995400 m 13.705425 11.951699 l s 0 slj n 13.708574 12.351687 m 14.505400 11.945400 l 13.702275 11.551711 l f gsave 10.455000 11.695000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 10.793667 11.695000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 11.132333 11.695000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 11.471000 11.695000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 11.640333 11.695000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 12.114467 11.695000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore showpage kamailio-4.0.4/doc/sip/figures/ua.png0000644000000000000000000006100212223032460016113 0ustar rootroot‰PNG  IHDRo¼ÕÃsBITÛáOà pHYsÐй‹çŸ IDATxœìÝy@ÌùÿðÏLMwºs¥%*M%MXébY+¬#l‘kic‘”%Ç²Ž­Ük,é\¤ ¥û@råZVtÓ¡cæ÷Ççûý|ç×¥šjfò|ü5ó~Ž×‡êùyN—Ë%€tA ò¦üBš@wˆuppPSSc0}úô™1cF\\\ë³Ðh´F:QEE…²²²²²rEEÿKCš@—Û²e‹§§§››[^^^UUUtt´˜˜ØäÉ“XRHHÈ”)SlllBCCù_ W!@—ºzõêúõëÓÒÒ¤¥¥yÛwíÚµaÆVf¤ÑþRÔ‡Ndff¶sçN.—ëé陚šÊçÒ06€®åçççååÕ(J ‚ ¢´°°pÅŠ222êêêË–-«¬¬lei‘‘‘£G–””2dÈÉ“'ÛØÕÈ“'OJKKY,–µµuqqqnnn‡¶ì¦ÐµÒÓÓÇßÊÆÆÆcÇŽ-))¹{÷nMMÍ/¿üÒÒ”±±±ÎÎÎÞÞÞ.\عsçÅ‹¿ØÕ›Ívqq!?»¸¸°ÙìmÙÿàH/t-ƒQ]]-..Þ–‰?}ú4lذÿý—hîH¯•••»»û´iÓȉ³²²~üñÇœœœÖ»ihhÐÒÒÊÊÊRUU%¢¨¨ÈÄÄäÙ³gbbbÞF¤)t-55µÇ“ÑÕÔû÷ï7nÜûîÝ»††‚ èt:ù¡išÊÊÊVUUñÎNMÜJW#—.] ¦ZæÌ™3þ|;;»o#Žô@×255MLLl©wöìÙrrrñññŸ>}âr¹µµµ§¥‰9Îǹ<¨¼l¥«6›BãÂçÁ^¤)t­•+WnÛ¶­¦¦¦QûîÝ» ‚HNNÞ½{÷°a䤤‚HHHheQFFF·oßno¯âââk×®•••ñæniii\\\qqq·¨)¤)t­)S¦ØÙÙ7.&&¦¼¼¼¦¦&33ÓÉɉ¼¦×ÀÀ`ÇŽ¥¥¥ÕÕÕQQQ .leQ[·nõððHKKûüùseeåõë×ííí¿ØÅëìÙ³ŽŽŽ ¼ŠŠŠAAAßH.@׋‰‰±µµUQQïÝ»÷ôéÓãââ¸\îÓ§O¿ùæ)))))©Q£Fýõ×_T65ýÀårããã­­­eee¥¥¥ÇÕ–.Š¡¡á7š¶ß¸qÃÐаÃ[‡«ø…#½üBšð i À/¤)¿¦üBšð i À/¤)¿¦üjÓÛæà«-èD ž,Ñh4A— b06€æ988ºÑ±)4FŽMmD£Ñp¿¦üBšð i À/¤)¿¦üBšð i À/¤)¿¦üBšð i À/¤)¿¦üBšð i À/qA=Jtt´ Kè8‡ŽÍHëÕ FDÇ‚œWDux“16€Î×áAž ð9¤ÆØãl*ráÂç&ã*$~!Mø…4àÒ€_HS~!Mø…4àÒ€_HS~!Mø…4àÒ€_HS~!Mø…4àÒ€_HS~!Mø…4àÒ€_HS~!Mø…4àÒ‰F£µ¥½¢¢BYYYYY¹¢¢¢éı±±jjj £OŸ>3f̈‹‹ë’r[€42eÊ›ÐÐÐF][¶lñôôtssËËË«ªªŠŽŽ›~丰cÑÞyi´æ“¨Q»™™ÙÎ;¹\®§§gjj*Õ~õêÕõë×§¥¥IKKóξk×® 6t]ÙæïÀlÝéÉ“'¥¥¥,‹ ˆâââÜÜ\²ËÏÏÏËË«Q”Ñ®(åŽô€°c³Ù...äg6›Mu¥§§?^@uýŽô@cBu¤·¡¡AKK+++KUU• ˆ¢¢"“gÏž‰‰‰Á`0ª««ÅÅù=ÔÊç&cl BíÊ•+L&“ŒR‚ TUU™Lfll,ùUQQ±¬¬LpÕýÒINN®´´´Qcqq±¼¼<ù™Íf‡„„Ðx„„„P{MMM»µâæ M@ÆŽK 4)±±±&&&A_»v­¬¬ŒË£´´4..®¸¸˜ ˆ•+WnÛ¶­¦¦¦ÑvïÞÝ=õ“¦ H>>>îîî>|hhh(**:s挻»»AgÏžuttTPPàEQQÑÁÁ!((ˆ ˆ)S¦ØÙÙ7.&&¦¼¼¼¦¦&33ÓÉÉ©›¯éÅUHÐXw^…DDzzúŽ;RSSËÊÊÍÌÌ<==™L&A£GÞ·oŸµµu£YâããÝÝݳ³³É¯—/_öóóËÌÌ,//WQQ177_¾|ù¤I“º´lÞy‘¦ÐX7§©0À5½†4àÒ€_HS~!Mø…4àÒ€_HS~!Mø…4àÒ€_HS~!Mø…4àÒ€_HS~!Mø…4àÒ€_HS~‰ ºè¢¢¢]B·¢q¹\A×Â…F£ѱ€ çQÞdŒM 39:: ºÀØãglú¢Ñh¸ €_HS~!Mø…4àÒ€_HS~!Mø…4àÒ€_HS~!Mø…4…ž†Ãá0™Ì^½z1 ggç”””nXuHHHŸ>}h4ZëïÐh©—ÖDjheáXZ+k!1 MMM__߆††N\>€ÈAšBO³zõ길¸€€€ÂÂÂÊÊÊË—/­Y³†ìmW¨´7Ö­[÷÷ßs¹Ü?+œûÿul!݃¬°ºº:888<<|Û¶m‚®@ºõ2±±±~~~eee***ææænnnß|óMkõÑþS!õ u²²²………rrrÍö¶ë©½?ut:½¡¡á‹ÜÒb;凼{~S­%''ÇÑÑñÕ«W]½^è6x‡L»të;d¶lÙâéééææ–——WUU-&&6yòän+¾***-×%ÿ@4=ŽZXX¸bÅ uuõeË–UVV¶2qddäèÑ£%%%‡ ròäIÞ…s¹\:NÍØìÚ;æèÑ£ZZZÆ ;qâï2Ïž=;vìX99911±Fs=~üX[[ûðáük§ÑhAAAcÆŒ‘’’RPP˜9s懨YNœ81tèPrEGmcÍzzzÿþûo+%µTÿ²eËœœœx5{öìeË–aiiÁÛUTT¤¬¬Ü–z€Û-bccGUUUÕ¨}çέÏHUØm¥‚¨‹ŽŽVUU?~¼§§çéÓ§Ÿ={ÆÛÛìÒ€N:U]]ýæÍ›… ®X±¢¥‰¯\¹¢¤¤táÂ…šššììl--­ˆˆˆfÞtÞ/þ0·Ô~úôéÁƒ§¤¤|þü9--mðàÁgÏž¥fÑÓÓKMM­­­m´ëׯkjjÞ¸q£éÚ ’““?þ\XX¸hÑ¢o¿ý–ì ÓÒÒÊÈÈøüùsFF†¦¦fKÍÎÎ8p`K%µRmm­¥¥åüA~ °´´$g¼|ùòøñãy×âëë»dÉ’fëN×Ñtß?–ƒƒu>©YïÞ½[¾|¹´´´ššÚÒ¥K?}úD¶7ûèâÅ‹†††þù'ÕND`` ‰‰‰¬¬,Nï‚íÑPYYáíííä䤮®>yòäwïÞ‘]_ü™ÿøñcŸ>}Zšxüøñ¼ñ™™™i``@}å?M›ÝÙ522Љ‰¡&»té’‰‰ 5KfffÓµœ~üøûï¿ß»wï´iÓÈiôôôNœ8allÌ`0ºa»@øÕÕÕ­[·îÙ³gÑÑÑDs§ß¿¿qãÆØØØwïÞ‘¦’§?›XVV¶ªªŠ·…š¸ÑôMçýâE-µKKK—””HKK“_«ªªTTTª««ÉYêëëyñÒh´Í›7߸qãÊ•+òòòÍ®ÃáðÂ¥ºš®ˆ ÚfK%?ˆ‰‰ 4ÈÙÙyÓ¦MâââÍ–ÔJý¤»wï~÷Ýw’’’cÆŒ¡ÚÏ;wáÂ…ÀÀ@‚ BCCýýý“’’š]¡ÃçM£¢¢Èß5QäàààèèØi4Z7ízˆ‹‹×ÕÕµqâfÔ‡VD“b.—ûñãG)))ò3ÑdwÛÚÚzõêÕyyyÕÕÕ\.·¶¶¶éOEJJêãÇ-­ˆhylÊápZYlëíRRR¼gI*++[Ù‚ BBB.]ºÔ웥¥µ·Ôf»Z©ŸtäÈCCCMMÍ .ð¶s8ƒ7oÞp¹\ssóÐÐЖV Ž ‰ÌèííÝñ44ooïÿs‰wO‰ŠŠŠeeeªªªÍö6;8hiQ·nÝúöÛoy[x'æÝ±…¯ÓªU«öïßOŽ“H………ÔÕ+ä8Œwd–œœ#%%E~MHH ºšNL{´²²úb222eeeŠŠŠä×Çwx‹FŒ‘˜˜hccC~MLLÔ××oez'''''§€€€Ù³g·}E)))“&M"¿vÖMº­×ÿþ}//¯”””ŠŠ ;;;}}}ò|-A4ÍÃÃÃß߯Œ/_¾œ1cF§ÔݠÃ}zèСiiiµµµéééC† á½ ©¥…deeõîÝûÈ‘#Ú[™%$$dذaYYYµµµYYYÆ ko©ÍvµRÿ§OŸ†H~=|ø°¡¡!ùw€TWWgddôÝwßýúë¯-­ºB‡‚›vx'(|–Ý}izùòåF¿$¤]»vq¹\ƒÁÛuõêÕVÒÔÂÂ"!!¡Ùµ MË妦¦.^¼xðàÁâââòòòL&óĉToPPЀ¨sB\.÷éÓ§ß|ó”””””Ô¨Q£þúë/ª«éÄ\.7>>ÞÚÚZVVVZZzüøñQQQTïdïÞ½›1c†¼¼¼„„„¡¡áÙ³g;œ¦\.÷ðáÚšš CKKëØ±c­ÌÂÛ’——7dÈò·¬-iÊår9¢¡¡!..®¥¥åïï/!!ÑÞR›íj©þ ,Z´ˆwʹsç.^¼˜·åСCÔudÐ=¦íÒ}iÊår7mÚdddtéÒ¥²²²êêꌌŒY³f‘´28hú¡•ÁÒ ݹsGWWWÐUp·oß>þ|AWñÕAš¶ ÑOôõõݶm›¿¿¿¦¦¦¼¼üÔ©Sëêêâââ‚ IOOïׯŸ²²²——×®]»ZYÎĉ÷ìÙãéé©¢¢¢¦¦ö믿º¹¹u×Fôp ,xúôi}}}NNÎÒ¥K—,Y"Øz’’’üüü¶oß.Ø2¾¨›®B"ÙÚÚÚÚÚ6m×ÒÒ"îR,X@~àòì"Q½,‹Åb5]ï4Г'OvttÌÏÏïß¿ÿ?þ¸víZC£ÑúõëwòäÉAƒ ° €¶èÖ4!7oÞ¼yóæ ºŠÿÀþ1ˆ¼C€_HS~!Mø…4àÒ€_HS~!Mø…4àÒ€_HS~‰GEE ºÑ#ZoÁ€®&>uêTA× zðøPàõŸ§Þ;88¶Q-è¾ [·nõññt ¤¼½½ñãÂæ?iŠã½mD£Ñ]ÂW£-¸ „—··w_|_ oooAÿT4i À/¤)¿¦ðÿÄÆÆ’>~üØÕëjéb”FíÊÊÊÊÊʼí' €ÉdöêÕ‹Á`hhh8;;§¤¤taÅ-ïþU€0SWW'?(((hkk3™L333&“9räH111”2eÊ.—êââBµ¯^½úŸþ ÐÓÓËÏÏ¿víÚš5k²²²º¹B¤)ü?£F"?ÐéôÜÜÜÜÜÜ¿þú‹ 9992\MMM©Ðíl6{çÎ\.×ÓÓ“7MÙlvaa¡œœùUWWWWWwÕªUÝVi ÿþŸ“€GŽ=ztZZZzzzzzz~~~|||||<Ù«©©Éd2Ép500`0]TÏ“'OJKKY,AÅÅŹ¹¹:::d—ŠŠJJJŠM­ºí¦Ð¼”””üÑÈÈhåÊ•A¼ÿ>##ƒ ׬¬¬üüüüüü³gÏ!--mllÌd2_¾|Ùée°Ùlj<êââÂf³wíÚE~=|øð¼yóôôô,--uttÌÍÍ555;½€¶@š@ó¨a(I]]ÝÑÑ‘|PyCCÃÒÓÓÉpÍËËKJJJJJ2dHçÖÐÐÐFuvv611ñõõ%ÏàÚÛÛ¿|ù2...;;ûÒ¥K†††§OŸîÝ»wç–ñE¸¦ Ô5¥4Í¢Q×±cÇœœœšÎåäätìØ1r^Zsšmïú­é —/_>þ¼Ù.11±Q£F-]ºôÔ©SnnnäÁáE‹Í›7¯sk¸rå “ÉTUU%¿ªªª2™Lêªc‚ ddd¦M›æããòÏ?ÿèêê.Y²¤skh ¤)ÀÈÊÊž?ž·eÁ‚III/^¼àm|ñâERRÒ‚ ȯÔ#œ}æýÊÛ(œZémhhXµjÕš5k8ÎöíÛOžÞñ¨¨¨|ÿý÷GŽ!¿?~|ÆŒÔé½Ö ÏyÓ>üý÷ß+V¬1bDß¾}œœ>üäɲWVVöÕ«Wùùùæº{÷.“ÉÌÎÎÖÖÖNKKkt^¹],--ÏŸ?¦««+))9|øð°°°ˆˆKKK‚ ØlöâÅ‹›ÎµdÉ’“'O‘šš:f̘•+W***ª««¯[·ÎÍÍmÓ¦M®§ÃpM/À—õíÛ×ÙÙy÷îݾ¾¾T£»»;‹ÅúùçŸi4Z@@@\\\—&Øs¥EEE‰‰‰ñññ >¤Š‘••µ°°°¶¶f±XfffAL™2åܹs ¼·ÄÄÄÌž=ûãÇãÇ¿páÿ‡U™Lfddd³]ÙÙÙͶ[[[“]fffd©‡4hòî Þc¹C‡555 ’=z´–––Ëk]II • <àp8d»ŒŒŒ¹¹9™ &&&žÀÀb±È4¥®’=tèÐO?ýT__?oÞ¼'NHJJv÷–+¤)@›ÈÈÈxzzzyyñ6zxx¸¸¸HJJ~ü˜œœL&è;w¨{.%$$ÌÍÍÉ533“––æsE,+<<¼¬¬LSSóÒ¥KÔãæ¡ŒMDÛœ9s°/û•øôéSJJ ™ ·nÝâMP 2AÍÍÍedd:q¥ä©Sssóˆˆ55µN\rƒ4^•••©©©d‚feeQÏcb0ÔyP YYÙ.*@WWwéÒ¥Äå»­Cš—ªªª´´´„„„øøø¬¬¬ÚÚZ²]\\ÜÔÔ”LÐqãÆQ¯ÈîR4zB´i xÕÕÕéééd‚fff~þü™l311a±XÖÖÖãÆ£žÂi 555d‚fddÔÔÔíbbbFFFT‚6zJ-'¤)@÷ùüùsff&ùL¢ôôôêêj²N§=šLPKKKEEEÁÖ í…4èZµµµYYYd‚¦¥¥UUU‘ít:}Ô¨Qä3‰Æ¯¤¤$Ø:HS€ÎWWW—••E^‹›ššZYYI¶Óh4}}}*AUTT['t¤)@稯¯¿uë™ )))Ÿ>}"Ûi4ÚÈ‘#Ékq­¬¬ÚøÖ6-HS€Ž«¯¯¿sç™ ÉÉÉ?~$Ûi4šžž• êêê‚­ºÒ }²³³ÉMJJâ}…øðáÃYÿÕ»wo Ý i ðeçîÝ»d‚&&&–——S]ÚÚÚT‚öíÛW€E‚!MšÇápîÝ»G%hii)Õ¥¥¥E%hÿþýX$ ¤)Àÿp8œOTHLL,))¡º†JƧµµõ€X$!¤)|í¸\îÇ©-**¢º444¨4h‹!‡4€¯—Ë}üø1™ 7oÞüðáÕ5hÐ 2>Y,–†††àjQ‚4€¯—ËÍÍÍ%ŸItóæÍÂÂBªkÀ€T‚:T€Eö ÑÑÑÿþû¯ «h‡Û·oó¹¤)ôpyyyd‚&$$¼{÷Žjïß¿?u%‘–––+ìynß¾Í>‰¤)ô@Ïž=£ôíÛ·T{ß¾}©ÕÖÖ`…=•££c¿~ý]Eux^¤)ôùùù ÿõÏ?ÿPí½{÷¦tøðá¬ðk`ddÄO&‰.¤)ˆ°çÏŸS úêÕ+ª]]]ÝÊÊŠLP]]]&À"ák€4S^^þ×_‘ úâÅ ª]UU•JÐ#F A¡;!M@¼~ý:!!!22’ ˆPí***ãÇ'T__ ‚ÒÝiJ£Ñ¸\îÛ+**ÈÛ¼^¼xÑ«W¯FÇÆÆúùùedd”••©¨¨˜››»¹¹}óÍ7]Y8t·7oÞÐøøøüü|ª]JJÊÆÆ†¼¡E__ŸN§ °H’ŽMCBB¦L™ÂårCCC]\\x»¶lÙ³uëÖ3gÎÈÉÉåääìÞ½{òäÉ͆4PDb_ößÿ¥ôéÓ§T»¢¢¢¥¥åçÏŸ¯^½º~ýú­[·vÖ:…îÓ±ÙlWWWWWW6›ÍÛ~õêÕÈÈȤ¤${{{%%%ƒall¾sçNA•ÚÕ¨#W4Í¢Q×±cÇœœœšÎåäätìØ1r^Zsšmïú­@îËÚØØ„††6êÚ²e‹§§§››[^^^UUUtt´˜˜ØäÉ“ù\ã»wïBCC—-[6|øð~ýúÍ;÷رcOŸ>íÕ«—½½ýž={nݺUTTiffFðüRa›>yò¤´´”ÅbQ\\œ››«££CvùùùyyyIKK7šeÆ XQAAA}}½¸¸0þ#4KVVöüùó3fÌ Z,XàããóâÅ Þ矽xñ"))鯿þZºt)AÔ°£éÐzhŠÍfïܹ“Ëåzzzò"÷eÓWŸ«» IDATÒÒ¨_@r_v×®]XKeeexx8yKè“'O¨EyyùqãÆ‘Ï$3fŒ˜˜ÿ[Ð „1HØl6õ;ìââÂf³©_×ôôô“'Ov֊Μ9nnn>iÒ¤‰' ù¯îÞ½{œœ Ù"%%åæævàÀÞë2<¸lÙ2)))• "¬Ûöe÷îÝK}–““³°° ÔÈÈH„vp(B÷SÛÐЖ••E~uvv611ñõõ%s®¬¬LII©³ÖÕ·oßwïÞݸqãÆA())±X¬‰'Nš4‰ú "T ,,,Ž=ºråJªqùòåzzz>>>ŠŠŠA”——?xð@pe‚ë¶}YƒA=×ØØ˜ÚAQBwÞôÊ•+L&SUU•üªªªÊd2cccÉ¯ŠŠŠeeeµ.WW×¢¢¢ðððeË–iii•––^¸paåʕÇ4hТE‹…íÁÍÛ¶mÛ·o_EEÕ¢¢¢òý÷ß9r„üzüøñ3fPÿ€­ÃySàEîË.\¸üêììÚÐÐ@~íÜ}ÙM›6]½zuãÆfffˆRèº;Måääxß_O*..–——'?³ÙìÞ?ñ!!!ÔµH¦¦¦‰‰‰X²²òÌ™3>üôéÓ/^üùçŸsæÌéÝ»÷ëׯO:5þüþýû9rÍš5QQQ¼&(}ûöuvvÞ½{7o£»»û¡C‡êêêêëëÜÝÝÛ¸4îÿ×õ‚(éÎ}Y€¦»ÓtìØ±Ô/'%66ÖÄÄ„ ˆâââk×®•••ñþ‰/--‹‹+..&båʕ۶m«©©i´„FéÒ1ƒ^¼xqPPпÿþ›““³oß>;;;YYÙ‡4449sÆÝÝÝÇLJ ˆ³gÏ:::*((ð΢¨¨èààDÄ”)SìììÆS^^^SS“™™éääÔ±ë ZB£Ñ Ö®]{éÒ¥’’’ÄÄDooo êX¨•••²²²Ýï¿ÿž““ÓÍ£:OOO///ÞFßÿ}ß¾}ÝY ˆaÞ—uݦ–––çÏŸ ÓÕÕ•””>|xXXXDD„¥¥%Al6{ñâÅMçZ²d uùƒ¯¯ï¶mÛüýý555ååå§NZWW×E3 KKKŸäädòo AúúúUUU—/_öðð044ìÓ§Ïœ9sNœ8ÁûÈÐ.5þüû÷ï󶘚š***2 ssó¶/çM¿*"±/ ¢pM/“É$¶ÙTvvv³íÖÖÖ¼]¶¶¶¶¶¶]R\«¨b÷îÝ#/¾víÚõë×_½zB„¦¦&yU°µµu/j5ðåÓétê²gJÓ£p­ß]Š¥_r_vÇŽkÖ¬)++STT433‹ˆˆ`2™A°Ùì}ûö5kÉ’%îîî«V­"Â××wܸq~~~ ,(//'Ÿ…Ôuû²"Dèî}úô™;wîܹs ‚ÈËË»~ýúµk×òóóóóó;F§Ó Éd7nœŒŒŒ Ká}Y!‡4íÚÚÚÚÚÚnnn wîܹ~ýúõëד““ïܹsçÎ={öHJJš››“Éjll,äψ EEE º‘4íLbbb&&&&&&6l¨©©III!ß¹s'>>>>>~óæÍŠŠŠVVVäÓ—tuu]2@‹¦N*èDÒ´«HIIMœ8qâĉA”––ÆÇǓɚ——wñâÅ‹/Ñ¿rÀ:qâÄ~ýú ºd‘G>—XD=zTÐ%ü£££ K1HÓî ¤¤4cÆ òiõ¯^½ºþ_oÞ¼9}úôéÓ§ ‚ÐÓÓ#“ÕÊʪÑu•ÐFÇŽt ‡4¡ÒÒùuh Ò´»‘Ï,\´h—Ë}øð!9`½yóæ£G=zäçç'..nllLXÍÌÌ$%%]²ˆ¹Xé!5¦C£ÑFŽI>¶°®®.33“LÖôÿÚ¾}»ŒŒŒ¥¥%™¬£F¢Ó…î¹ÊBÈÕÕUÐ%´Ò @š ƒaaaaaaáííýéÓ§ÄÄDò–›û÷ïÇÆÆ’Ï¯QUU0a™¬C‡tÉð?HS¡#''ggggggGDaaá7Èd}ùòeXXXXXAC‡%/qš0a‚ššš KøÚ!M…ZïÞ½çÌ™3g΂ ž={F Ž/(((((8~ü8N700 ¬–––²²²‚.àk„4ZZZZZZË–-ãp8ÙÙÙd²&''ß½{÷îÝ»{÷î•033#“ÕÄÄD\ÿ¹ÝpEN722222úå—_jjjRSSÉCÁ·oß¾yóæÍ›7½¼¼¬¬¬È[ntuuñD{€.…4mRRR&L˜0a‚¯¯oYYY||<™¬¹¹¹‘‘‘äcýúõ›ø_ tÉ=Ò´çPTTœ>}úôéÓ ‚øçŸÈCÁׯ_ûöí™3gΜ9CÄðáÃÉCÁ,KQQQÐ%ôHÓžiÀ€ÎÎÎÎÎÎ\.÷Ñ£Gä€õæÍ›Ožþ<<<<<<œ  ò$ë„ ÔÕÕ]2€ðB𡦦æäääääDD~~>y(8>>þÅ‹'Nœ8qâF300 ¬–––rrr‚.@¸ MáÿÑÔÔÔÔÔtuuåp8wïÞ%“5999'''''gß¾}L&“LVƒÑuÅ|þü™Ãáà<.?ü‚æÑéô1cƬ[·.66¶¤¤$>>ÞÓÓ“Édr8œÄÄDooo UUÕ©S§~ÓT¤Y'ÒÅ Š‚‚´iÓ¦M›FÄ›7oÈCÁä3"Ξ={öìY‚ tttÈCÁ,KII©Ãë*(( âÞ½{fff111#GŽì¬­èI‚ƒƒƒƒƒ]À׎ß4õöööööî”R@äôïßÁ‚ , âñãÇd¬&$$äæææææ:tHLLÌÈȈ|¨¡………””T»–_^^®¦¦¦¦¦öèÑ£qãÆ?~„ ]³)"IGGÇÑÑQÐUtŽŽŽ KèL¸ :‡®®®®®îªU«êëëoݺE&kZZZfffffæÎ;¥¥¥-,,ÈCÁcÆŒiãµE&L8uêÔÂ… ÃÂÂlmmOž<ùÃ?tõ¶ˆ êm} pHSèdâââL&“ÉdnÞ¼¹ªª*))‰LÖœœœk×®]»v eeekkk2Y‡ ÖÊÒ&Mš$%%<`À€}ûöÍŸ?ÿõë×6lè®­h¤)t!‚ ŠŠŠnܸAžg-((8wîܹsç‚|øÍ›7ÁÁÁ222ÛHS€!C†¸¸¸¸¸¸p8œ{÷î‘Éš””tÿþýû÷ïïß¿ŸœŒ˜òš1cF\\Ü´iÓ"##­­­£¢¢ðÈCxzN744ôðð¸|ùrIIIBB‚———™™yóÒ¤I“šÎbaa‘’’2tèÐÌÌLssó§OŸvxí-Ý"Õ¨½¢¢BYYYYY¹Ñs$8N@@“ÉìÕ«ƒÁÐÐÐpvvNIIép= º¦ ,$$$¬¬¬~ýõ×7nˆ‹‹Óétkkëf§ÔÑÑIMM566ÎÏÏ777OOOïÒÂBBB¦L™bccÊÛ¾zõ길¸€€€ÂÂÂÊÊÊË—/­Y³¦K‹á„4¡“’’RWW7jÔ(UUÕ–¦éÝ»wBB‚½½}QQÑĉ#""º®6›íêêêêêÊf³µIKKKHH7eeeu]% ´¦ t®_¿N´p˜——¬¬lDD„žž^UUÕÌ™3ýýý»¢˜'Ož”––²X,kkëâââÜÜ\ªKEEÇu€„«@è÷¤6½‰ ‡óøñã¬ÿºwïÞçÏŸ ‚hhhXµjUWÃf³]\\ÈÏ...l6{×®]ä×ÇÏ›7OOOÏÒÒRGGÇÜÜïWøj!MA¸”––Þ¹sGRRÒÒÒ’lyñâEff&ŸwîÜùøñ#51N×ÓÓ3ù/SSÓÎ-¦¡¡!,,Œ:xëììlbbâëëKÞêjooÿòå˸¸¸ìììK—.yxxž>}šºk¾HS.ñññ ƒ úí·ß²²²’““]I«¡¡Aŧ‘‘‘¼¼|×såÊ&“I¾UUUe2™±±±vvvd‹ŒŒ õ€ºººuëÖ-Y²$::ºëJá„4áBž4}þü9õŽ?uuu*>;ñS99¹ÒÒÒFo¹)..¦šÍfŸ;w.$$„w‚úúz*My1ŒíÛ·«©©uVy Bp—k×®õêÕKCCƒ ˆï¿ÿþÅ‹………ÑÑÑÞÞÞvvvû¬†±cÇÆÆÆ6jŒ511!¢¸¸øÚµkeee\¥¥¥qqqÅÅÅAøç·°°PYY¹+Q4áráÂ…²²²… ¡§§7xðà®[—»»{``à‡ŠŠŠÎœ9ãîîîããCÄÙ³gxgQTTtpp "ÂßßßÄÄ$::º²²²¦¦&99ÙÉÉiÅŠ]W0-¤)==½n{‹»¥¥åùóçÃÂÂtuu%%%‡A^Åf³/^Üt®%K–œt ÞóJ cРAsæÌÙ¼y³¤¤d{Õ9Gz:e9ÝwBDÝG$r¼½½É«½àk³eË–˜˜˜­[·ž9sFNN.''g÷îÝ“'OÔð†ZommíãÇ7lذ|ùò?ÿü³½Ëéœ4¹ã½Ýv @W¹AžH©OW¯^ŒŒLKK“––&[ŒÃÃév „„ĨQ£NŸ>=tèP¥)Êœ9sDkçãã#ºCjà“ŸŸŸ——¥” 6 ýõW6›-''7cÆŒßÿ]VV¶¥¥EFFz{{?zô¨_¿~^^^¼á·ÒõEä£CÛ wÈ@7IOO?~|+;¶¤¤äîÝ»555¿üòKKSÆÆÆ:;;{{{WTT\¸paçÎ/^übW+êêêîß¿¿hÑ¢Ž]„±)t“²²²FÏòläõë×ä‡~ýúùûû6¬¥—-îØ±ƒÍf“OÉ644 úñÇɯ­t5ÕèÄßàÁƒ322Ú¿e›@wQTT,++k©÷ýû÷K–,0`€¸¸8F“——ÿþ}KߺuëÛo¿¥ýר±cF»¡¡á‹]­6lØÑ£G#"":°iHSè&+W®Ü¶m[MMM£öÝ»w‘œœ¼{÷îaÆIII‘ÐÊ¢ŒŒŒnß¾ÝÞ®¶¨­­íÀ\HSè&S¦L±³³7n\LLLyyyMMMff¦““yM¯ÁŽ;JKK«««£¢¢È·_´dëÖ­iiiŸ?®¬¬¼~ýº½½ý»ZÁápž={¶téÒo¿ý¶›†« ûøúúŽ7ÎÏÏoÁ‚åååä³âââ‚ Y¾|y¿~ý‚ÐÑÑÙµkW+:qâÄ={öxzzfffr8“uëÖ}±«)ê*$:Þ¯_¿™3gnß¾½Û…4€nekkkkkÛ´]KKëêÕ«¼- , ?PO,â}d‹Åb±XÍ®¢•.^ø&éàÒ€_HS~!Mø…4àÒ€_HS~á~Sh,**JÐ%ˆ¤)ô4K—.t "¯cïøüš!M¡§9v옠Kè!]‚hˆŽŽFšBÏqôèQA—УàxoÑh4¤)ô®®®‚.¾R¸¦€_HS~uwšRo’k½½¢¢BYYYYY¹¢¢‚·Ãá0™Ì^½z1 ggç”””.¬àK„tl2eÊ›ÐÐPÞöÕ«WÇÅÅVVV^¾|ÙÈÈhÍš5‚ª@„ð¹/KŠuppPSSc0}úô™1cùžg€¯œ¦)›Ívuuuuue³ÙÚŒŒ¤¥¥%$$tuuW­Z•••%¨:zž–öe ‚زe‹§§§››[^^^UUUtt´˜˜ØäÉ“Rgw¢v8h4š……E£®cÇŽ9995ËÉÉ騱cä¼´æ4ÛÞõ[]BÓôÉ“'¥¥¥,ËÚÚº¸¸877—êRQQÁq]€.ÕÒ¾ìÕ«W###“’’ìíí•”” †±±qxxøÎ;Uª@ÈÊÊž?ž·eÁ‚III/^¼àm|ñâERRÒ‚ ȯÜÿjô™÷+o#ˆa¼C†Íf»¸¸Ÿ]\\Ølö®]»È¯‡ž7ožžžž¥¥¥ŽŽŽ¹¹¹¦¦f‡W4sæLµ&ÔÕÕUUUÅÅ…ñ_ «Qû²Aû²:::d—ŸŸŸ———´´t£Y6lØÐ]¾|ÙÀÀ`âĉ ü•ÜÝöîÝëäääèèÈ`0È)))77·8p€šìàÁƒË–-“’’P™ÐÝ„.3¨ƒ·ÎÎÎ&&&¾¾¾bbbAØÛÛ¿|ù2...;;ûÒ¥K†††§OŸîÝ»wÖÑÐÐдF£)))5MY555r‚·oß’ç:º•Bª•}Ùôôô“'OvÖŠ233¿ûî;:®¯¯ommmmm=~üxEEÅÎZ~×100°°°8zôèÊ•+©ÆåË—ëééùøø›P^^üàÁÁ• ÝMèÒôÊ•+L&SUU•üªªªÊd2cccíììÈ™iÓ¦M›6 ˆºººuëÖ-Y²$::ºëZ½zõþýûÅÄÄ–.]ZTTôGIIIII ïAf^ššš÷îÝ6lX‡6@Hµ¾/[VV¦¤¤ÔYë²¶¶¦Ñh)))999999=z4™¬ãÆ“——ï¬uuºmÛ¶YXX,X° W¯^d‹ŠŠÊ÷ßäÈr¤~üøñ3fPÇZ×è\)öЍîNS99¹ÒÒÒF¿“ÅÅÅÔo›Í>wî\HHïõõõTšòb0Û·o§†Œíõ믿ž;wîÕ«WÚÚÚT;—Ë-..þÐDaaaXXAëׯG”BÏÓú¾¬¢¢bYYYâ‹Æïããóùóç´´´„„„øøøŒŒŒ[·nݺukÏž=âââÆÆÆd²ZXXÈÈÈtÊJ;Kß¾}wïÞíëëK5º»»³X¬Ÿþ™F£´ýRgÄgÂí¨öÎ>a„àààFgÏž0a—Ë-**RPP(++ãí---UPP(**âr¹+W®¬««ãí}öìY¿~ý:\6ùJyyùW¯^}q®}ûö‘3VUUµwÐ.ÞÞÞAx{{ º¡ÖÞ%99¹’’’FEEEòòòäçï¾û®é‡™3g’½öööçÎ뺲+++ãââ6mÚdffÆ{EBBÂÒÒrË–-7nÜðôôÈõWŽúPYY9lذþù‡÷àÌ™3O:4}úô¦ó¶å«ðà3¾6HÓÄÄľ}ûž9sæýû÷õõõ>| Ïz&&&r¹ÜƒΛ7¯é\?üðÃüA®ÎÐÐ0**êÓ§OÕÕÕIIIFFF¾¾¾ü”=sæL‚ ¦M›Öú,oÞ¼¡Ðí]´Ò´-Úû¯Äç¾ìåË— «««-a×®]^öÇcbbÖ¯_obbBg&‘—Z[['%%}þü¹]ëåGÓ4år¹§NZ´hoKzzº¾¾¾±±qJJJÓyÛòUxào]» M¹\nZZš£££ŠŠŠ˜˜˜ŠŠŠƒƒCZZÙehhxãÆ¦³Ü¸qÃÐÐË妦¦.^¼xðàÁâââòòòL&óĉ|–ýæÍòªÂóçÏ·2 ïýdX#´ Ò´-Úû¯Äç¾,—ËÝ´i“‘‘Ñ¥K—ÊÊʪ««322fÍšÕÞ߈ö–]VV¹víZCCCÞSŒ²²²“'OÞ¹sgZZZ£CV®Ù4mhh066n´ù–––fffÍÎÛÒ×F:¹ôŽªb„Ÿ`ÒT4*›}ú|ûí·ä…Haaa‘‘‘kÖ¬Ù¸q#õ06C‡:tè’%K‚ÈÍÍ%c5!!áÍ›7Ahhh'Y­­­(è’EÞ­[·Ž;&è*:¨oß¾HÓÎ1yòä¹sç­X±âï¿ÿþé§Ÿ‚ضm[Ÿ>}‚ Ȳ|ùòÇïÚµ‹ÍfoݺÕÅÅW:9eË–q¹ÜG‘ÉzóæÍ/^œ:uêÔ©SAhii‘±Êb±úöí+è’E˜ƒƒƒ£££ «h‡¨¨¨Ž=¡–‚4mlÿþýW®\¹|ùò7ß|óâÅ‹1cÆ,_¾¼Ñ4ÎÎÎîîîÉÉÉË–-ûí·ßþøã{{{ íB£ÑFŒ1bĈ•+Wr8œ{÷î‘Éš””ôìÙ³gÏž?~œF£éèèPÉÚáÇ—~µŒŒŒ\]]]E;¼}û–Ï4ňª1uuõß~û ˆÔÔT:~èÐ!ÞG±PLLLÃÃÇ RPPààà`ccsïÞ½n¯:ŽN§®]»622²¨¨(++ë·ß~³µµ•““{òäÉáÇgÍšÕ»woƒŸ~ú)""¢¤¤DÐ%ƒBš6cñâÅãÇ'bÉ’%¦¦¦-MF£ÑfΜùôéÓíÛ·KII]½zÕÐÐpÞ¼y>|èÆb sˆ‰‰¯[·.&&¦¸¸855Õ××÷›o¾‘––¾ÿþü1}út55µ1cÆüüóÏÑÑÑååå‚.„Ò´4íÈ‘#ýû÷ß¹sç'óôô|óæÍš5k ÆÙ³g¸wï^ÞÛl@´0 33³M›6]½zµ´´411qëÖ­,KBB";;{ß¾}ŽŽŽªªª¦¦¦6lˆýôé“ KCš6OWW7##£íkPVVÞ¿ÿ£G¾ÿþûÚÚÚ"Òµ IDATuëÖõïßßßß¿®®®K뀮F½Ä&>>¾¤¤äúõë›7o7nNÏÌÌܽ{÷”)S”••-,,6oÞ|ýúõêêjA— €4mQÿþýÛ;‹¦¦fXXXjjê!CÞ¿¿jÕªáLJ……qñþB€AZZz„ Û¶mKJJ*))‰Ý°aƒ©©)—Ë% Oš4III‰Åbmݺ511ñóçÏ‚.º Ò´ó1™Ì‚‚‚ˆˆ‚‚''§!C†$$$º.èLÔKlÒÓÓ‹‹‹£££=<<ŒŒŒêëëoÞ¼éããcee¥¤¤4iÒ$__ßÔÔT©êÙ¦]eÚ´i<8vìX¿~ý^¾|immmkk{÷î]AׯW¯^öööäKl>|øñÓO?ÔÔÔG†-,,”••mmmûí·ÌÌ̆†A— iÚ…ÄÅÅ]\\^¾|¹ÿ~™+W®Í;7??_Ð¥@WQRRš6mÚrrrÞ¿¾bÅ ==½ÊÊÊ+W®üòË/¦¦¦***ŽŽŽûöíËÎÎæp8‚.:Ҵˉ‹‹¯Y³æíÛ·ä½ÌÁÁÁÚÚÚ?ýôSEE… K€®¥ªª:sæLÿ‡¾}û688ØÕÕuذaåååÑÑÑ?ÿüó˜1cÔÔÔf̘áççwÿþ}\c!º¦ÝDAAáèÑ£/^¼X¼x1Fûã?úôé³qãÆÒÒRA—Ý¡OŸ>³gÏ&_bóúõë3gÎ,^¼xÈ!%%%.\X½zµAïÞ½gÍšuøðá'Ožº^h¤i·8pàŸþùðáÃï¾û®¦¦f×®]C‡õõõÅÍj_•Ì›7ïÏ?ÿ,((xþüùÉ“'çÏŸ?pàÀ>„‡‡/_¾\WW·_¿~?üðùš9A× _†4¿ÿþ»  `öìÙeee›7oÖÔÔ}þý÷ß   òÈ0ùš¹S§N½|ùRÐõB󦣡¡onnþþýûµk×öíÛ÷èÑ£¸$à«¥¥¥åââôöíÛGÌœ9SMMíõë×§OŸ^´h‘†††¦¦æ?þøæÍ›î© ÷ö´Eç¼CFtß +p,+%%åÒ¥K›7o¾{÷î²eËvïÞ½eË–y󿉋ã ?_)¦«««««»|ùr.—ûàÁêrþù'AÚÚÚÔ‹nz÷îÝ•p8KKËÀÀ@--­®X~Ñ9¯§NÚ)ËùjÙÛÛÛÙÙ±ÙìÍ›7?þ|Ñ¢E;vìðööž={v³o° äåå‰Öîl^^ž K14M_____õêÕçîÝ»Ô+äòòòòòòŽ=J£ÑôôôÈמ³X¬¶?õ‹îÞ½›‘‘affaaaÑY‹íyøMSÑz¬0£Ñh‹/vvvööö~úôé¼yó|}}½½½gÍšE£Ñ] ©ààààà`AWÝ„N§3†|M}}ý;wÈdMNN~øðáÇètº¾¾>9f?~¼¢¢"?k$ŸãVTT4iÒ¤S§N999uΖô8ü¦iddd§Ô$:þÃ?8999sfûöí?ž={öÆ÷ìÙóÝwß º:.:::¢»;«££#èDž¸¸øØ±cÇŽûË/¿ÔÕÕeff’Éš–––“““““sàÀ111CCC2Y---åååÛ»–øøx‚ ŒŒŒnß¾=gΜçÏŸoذ¡ ¶FäáÌœ0_´h‘³³sPPÐúõëŸ?>sæL}}}//¯ï¾ûŽNǵc@1gΜ9sæº  Â|ÍçÏŸÓÓÓÉdÍÈȸ}ûöíÛ·÷îÝ+..nllL&«………ŒŒÌ[__Ÿ˜˜HÄÅ‹ÃÂÂ<<<6nܘŸŸèÐ!ƒÑõ›%JðwYxÑh´~øáÕ«W»wïîÕ«×ýû÷gÍš¥¯¯†ë~ %’’’VVV>>>7oÞ,))‰‹‹Û´i“¹¹9FKOOß¹sçäÉ“•””¨×ÌÕÔÔ´´¨;wîTTThkk÷ïßíÚµÿý·ŒŒÌ‰'ð4·F¦ÂNLLlýúõEEEGŽ8pà£GœœœÔÕÕ÷ïßL€ÖÉÈÈ/±III)))¹|ùòúõëMLL’““·mÛ6aÂ%%%ò5sÉÉÉîz'óZ[[“_§OŸß»wï«W¯Ž7îõëר$a…4 céҥϞ=;zô¨††Fqq±»»û!CBCCñ`Oh 99¹)S¦ìÞ½;33³¸¸822ríÚµ£G®­­ß²e‹¥¥¥’’ùš9r–FiJÄØ±cÓÓÓõôôîß¿Ïd2ïܹ#˜>HSQ"!!áêêZPPØ¿ÿW¯^Íž=[KKëøñãxŽ´‚‚ù›;wî|øðáüùó«V­9rduu5yd˜œìúõë4ÅbñΫ¡¡‘’’2a„·oßZYYEGG `„ÒTôPçS÷ìÙ£¤¤TPPàêꪩ©éçç×Êù€f)++OŸ>ý?þ¸ÿþ»wïBCCÝÜÜÈ®úúz]]ݦυPTT¼|ùòÂ… ?}úôí·ßúûûwxí-Ýþר½¢¢BYYYYY¹ÑùZ‡Àd2{õêÅ`0444œSRR:\O‡!MEN÷ðð(** ÖÓÓûçŸV¯^­¬¬ìåå…L€ŽQWWŸ5kÖ¡C‡¨Þü¼$$$ØlöÖ­[9ΪU«ÜÝÝ»ôJŽ)S¦ØØØ„††ò¶¯^½:...  °°°²²òòåËFFFkÖ¬éºJZ‚4mt:}öìÙ<¸pႉ‰IuuõöíÛÕÔÔöíÛ‡c¿À¿–Ò” ¶eË–Ó§OKJJîß¿æÌ™UUU]T›Ívuuuuue³ÙÚŒŒ¤¥¥%$$tuuW­Z•••ÕEe´iÚÐh´o¿ý633óÊ•+VVVŸ>}úù矕••×®]ûÏ?ÿº:Ut:ÝÊʪõiæÍ›—˜˜(&&váÂkkëÊÊÊN/ãÉ“'¥¥¥äs‹‹‹sss©.×m i򣯯Ø$$$¼|ùòǬ¬¬wîܹs第¬œ]ˆ%%¥ýû÷gdd¤§§ó¾¨œF£ijj2™L&óÿÚ»× ¦Î4à' I‹€$\䢀" 7¹,VT@]T”*Ý­:]oÛYZÝÝqZc­®î®în‘®ÃØ:Èm-åVF@-×ED¬ ÜBá$ýp¶µmäùÿ>d’÷ä‘'3¾þ}oÎñôððpqqa±X䡺ºº©íáÛo¿õôôT,‘9ާ§gnnîúõëÉ7nܸ‘ ˆ‘‘‘ÇïÞ½[ýßÛAšÎq¶¶¶Ÿ}öŸÏÿ÷¿ÿsëÖ­[·nqvvÞ»wïÅ‹kkkÉKþž8q"88x ãÊÝÝ=77wÂ`nn.Ç#B(æçç‹Åbù8"‘(//O(qèÐ!™L6¾¶££cÁ‚SÕÞ/‡4Õ L&óСCqqqÎÎÎmmmGޱ²²zÿý÷±õ È'ÕÕÕ±±±»wïvrrš¦»Wñùü¨¨¨„„„®®®ÑÑÑîîîøøø¨¨(>ŸODbbbHHˆ¢’¡¡apppRRAçÏŸçñx™™™R©´¤¤$<<üÀÓÑêOCšjƒYUU•““ãçç'‘HÎ;gmmýÖ[o566RÝPoåÊ•jûY>>>_}õUZZšƒƒ›Í^¶lYZZÚ×_íããCÄ¥K—^º­i÷îÝ_|ñAÀÍÍíàÁƒ†††&&&‡Þ·oŸâʈêôÿß›fdd¨ÿg…h4ZPPPPPPEEŹsçRSS“’’’’’V¯^ùÎ;ïàWª žžžß|óÍKUTT¼tÜßߟ<äååååå5ÍýbÿOÓÐÐPjûª¸ººÆÇÇŸ>}úÓO?½páBQQQQQѱcÇ>þøã·ß~›Á`PÝ À, Bu@= ‹3gÎ|òÉ'±±±üq[[ÛîÝ»£¢¢öìÙsðàÁÅ‹SÒÕñãÇ?NÉPŠÖ«Ö× ˜LæöíÛ—žžž’’’––vöìÙýë_7n|ï½÷V¯^Muƒ3v!ÁDt:}óæÍ©©©uuu[·nûꫯ|}}]\\.]º4<<¬†ø|¾àÈÝž3 Ò^ÉÁÁ!--íéÓ§ÇŽ355­®®~çw £¢¢zzz¨î`AšÂÏ033ãóù?¾|ùòŠ+¤Ré?ÿùO.—»eË–ÄÄDª»˜¦ð‹°Ùì]»vݽ{·  `ÇŽt:ýË/¿Ü¹s§M\\œâ Ôš i ÊñóóKJJêíí=sæŒAKKËoû[##£?üáão: Q¦0:::GŽ …)))Û·o—H$ÑÑÑW®\¡ºAµBšÂä1Œðððää䦦¦ÈÈH:^PP°mÛ6kkë?ü×þÍ4…)`kk'‰ÎŸ?ïààÐÞÞþÉ'ŸX[[oذ!++kllŒê¦Ò¦Œ¾¾þêêꊊŠ"""X,Vvvvpp°¾¾þ|€/ÕÀ†4…©çã㓘˜ØÚÚú·¿ýÍÖÖvppðìÙ³\.wÏž=ÙÙÙXªÀ܃4…éÂáp>ÜÔÔ”ŸŸ¿uëVƒñùçŸoذÃáœ:uJ"‘PÝ À”AšÂ´ûõ¯––&‘Hbbb.\(‰Ž=jhh¸nݺääd,U`@š‚š°Ùìýû÷·µµ%&&FDD0Œo¿ý6""ÂØØøƒ>¸ÿ>Õ LÒÔ-"""11±··÷Ô©S&&&b±øìÙ³ŽŽŽ^^^±±±}}}T7 4¤)PcÞ¼yùË_:::ÊËËß}÷]‹UZZúî»ïš™™íÚµ«¨¨ˆê”€4Šñx¼ .ôõõÅÇÇ %$$øúú.Z´è£>zôèÕ ü<¤)Ìl6{çÎä à/^üÃ?œ8qbÉ’%«W¯¾xñboo/Õ=¼Òfsss>ŸÿèÑ£‚‚‚·ß~[WW·¸¸xïÞ½æææ;vìÈÉÉ‘ÉdT÷0‘Õ ¼Fóóóóó󋉉¹zõê_|QTT”’’’’’bjj±sçN777ªÛ€—ËÌÌ|öìÕ](áÞ½{*þ 4¹\>%­L«ÖÖÖøøø¸¸8Å}ß–-[¹sçN+++j{˜{h4A“>ŸüøñièHŽ;Æçó'QH£Ñ¦0Ëܹs'!!!99¹««‹ñööÞµkWxx¸‘‘µ½Ì“NÓ{÷Σʊ+V¬X1‰B¤)ÌV2™ìúõ뉉‰éééA°X¬7ß|3""bãÆ:::T70»M:M5Òf½þþþ¯¿þ:)))//Ü ¤««º}ûö   ‹Euƒ³ÒT)HS˜;:;;¯\¹’œœ,ȿՆ††›7oÞ¾}{@@€–6Ü(iª¤)ÌA?NMMMNN®¬¬$G8NXXضmÛ|}} µíÌ HS¥ Ma.khhHNNNII©¯¯'GLMMönÝêããƒXø HS¥ MA#ÔÔÔ¤¦¦¦¦¦666’#&&&aaa[¶lÁj०JAš‚f©¬¬¼råJjjjSS9bllü›ßü&,,, €ÉdRÛÀÌ4U Ò4TeeåÕ«WÓÒÒ>|HŽÌŸ?Ó¦MÛ·o_»v-Ž+n‚¦Cš*i š®¶¶öË/¿ÌÉÉ)++#G´µµƒƒƒ·mÛ¶yófì…4U ÒàÿÚÚÚâããóóóoܸAŽhiilٲ孷ÞÂå @Ó M•‚4˜¨§§'11±°°ðÚµkäì Ñh<oÓ¦M!!!¯¿þ:Õ ¨ÒT)HS€W¼råJqqñåË—GFFÈÁùóçÿîw¿ Åf`˜Û¦JAšü<™Lvùòe@––&‘HÈA&“¹yóæÐÐÐÐÐP}}}j;˜rHS¥ M” —˳²²¾ûŒŒÿþ÷¿ä Fóöö Ù°aÎÜ4U Ò`’š››322233 ç.\º~ýú€€]]]j;PÒT)HSUI$’¼¼¼¬¬¬ÌÌÌÎÎNrÉdúûû‡……ùúúÚÛÛSÛ!À$ M•‚4˜2r¹üûï¿ÏÎÎÎËË+))QÌ,##£={ö¬Zµjݺu¸CÌHS¥ M¦Ezzú7’““¥R)9H§Ó7lØàååõæ›oº¹¹‘ÿZÌLª¤iffæT·£>ÁÁÁ“¨BšL/¹\~÷îÝ›7ofddܾ}{llŒçp8kÖ¬Y»v­……µM¼H•4ÕÿSœôGFš¨‰L&ËÊÊÊÉÉIOOoooWŒÛÛÛ„„„àÊû0C¨ž¦“[äQˆ\R#Mf“æææœœœ¼¼¼‚‚‚ÞÞ^rN§óx<ÿÀÀÀÕ«W#YBª§é¬ ?2Ò€JccceeeùùùUUU×®]Sœ ¦Óé¾¾¾þþþþþþHVP3¤©²µHS€™B&“¥¥¥UVVÞ¸q£¢¢B17µµµ}||üüüV®\ˆÆ M•­EšÌD===·nÝ*((¸~ýúƒãL&Ó×××ÝÝ}íÚµÞÞÞ¸gL¤©²µHS€™®¯¯/777//O (®hHFsww_³f——. Siªl-Ò`6‘J¥W¯^-///**ª®®VÌ_:îêêºnÝ:—uëÖẆ "¤©²µHS€ÙJ,—”””––ŽŸËîîîöööÞÞÞK—.¥°I˜¥¦ÊÖ"Mæ‚‘‘‘’’’ââb@PXX8<<¬8Dnb xã7üýýÙl6um¬4U¶i 0×W .++wîÜihhÔÝÝÝÉÉÉÃÃcÕªU¸…¼ ÒTÙZ¤)ÀGÞ妼¼¼¤¤¤¬¬L&“)1™L//¯U«V¹ººòx¼Å‹S×&Ì,HSek‘¦D.—ß»wïöíÛeeeÕÕÕ555ãr¹\ÞqNX“!M•­Ešh®¡¡¡ÂÂÂŠŠŠï¾û®¨¨¨¿¿üQ OOÏåË—¯X±ÂÝÝËåRÕ'¨ÒTÙZ¤)ü_kkëíÛ·ïÞ½[WW—››;222þ¨¹¹¹›››››Ûo¼áêêjccCUŸ HSek‘¦ê£¸S‹ÅâñxŸ}öÙÿøÇ÷Þ{/44”OOOŽŽÎÏÏñžV꟪ 555¥¥¥÷îÝ«¨¨‹Åãêëë“ÉêìììêêêââÂ`0ÔÜ!L¤©²µHSõQ̸ÁÁÁóçÏ_¾|ùóÏ??tèPyy9ùãîî>çfSSSEEE]]]iiiII‰D"”F£9999;;/_¾ÜÑÑÑÕÕÕÊÊŠªVAuHSekgÜŒ˜ÃÆÏ¸þþ~‡#•J7mÚôûßÿ>((('''66öÚµkÞ93‰ÅâÒÒÒÊÊÊÆÆF@P__?¡aƒáéééììüúë¯ÛÛÛûúúâN8³ÒTÙÚ™>cæ’ñkÓ˜˜˜o¾ù¦¸¸¸¶¶vÿþýEEE>>>.\prr"fCšN —ËëêêÊÊÊjjjZZZrrrÆ_A‚djjºlÙ2gggGGÇ%K–,_¾ÜÔÔ”’nág!M•­e3`VÿÛP==½[·n¹¹¹iiiÙÖÖ÷â;I³nªvtt455•——×ÖÖ>yòäæÍ›ã¿êJÒÒÒZ¹r¥““Ó²eËìììœíìì^üì ~HSek‘¦ê£˜qCCCäIÝÂÂB‚ š››mmm=z¤Ø(;'çæ?üÐÒÒBînmm½yóæ„mÃAÐh´×^{ÍÑÑqéÒ¥666ä333JÖdHSekçàŒ˜±ÆÏ¸ccc©Túâ¡_ÎUuuuíííõõõßÿý“'OêëëŸ|øðÁƒ•••mmm UUUCCC/¾“N§ÛÛÛ[YYÙÛÛÛØØXZZ.]ºÔÑÑ{¦ÒTÙZ ±”PüFPKKËÅÅåÂ… +W®Tš¦j5yªJ¥ÒêêꪪªgÏž566ÖÔÔ<{ö¬££ã¥oÖ××íµ×-Zdaa±dÉkkksss;;;\ËI)HSek‘¦0+ŽŽ–••uvv677×ÖÖvtt´´´ò¦---óçÏŸðæÜÜÜèèè²²2±Xlllìíí½oß¾5kÖL_Ûj‘¦ éärykkë£Gž>}ÚÓÓÓÜÜüìÙ3‘HÔÒÒÒÞÞþÓYKš7o—Ëåp8FFF\.wÁ‚FFFäKCCCò¥Á‚ tuuÕð‰T7Ó466¶°°P.—ìÝ»wü;?úè£ìììãÇ{{{ëééUUU9sæêÕ«Jõ4˜Fccc­­­"‘¨µµµ³³S$µµµuuu‰Åâ§OŸ>}útllì—ÿÆÆÆ†††óçÏ'õõõÉG===òQOOOWWWWWWOOOGGGGGGWWW[[{ú>æ30M½¼¼þú׿Êåò£G Åøõë×9rûöíyóæ/?}úôŸÿüçék{B­Ö$Ê4N·¶¶¶¶¶þ‰÷H¥Òööv¡PØÛÛÛÑÑ! ûúúÈÜííííîî‹Å}}}B¡pppP( …ÂÉ5C&«¶¶¶¶¶ö¼yóØl6ùœÅb±Ùl6›Íd2Y,‹ÅÒÒÒb2™L&“Á`Ïét:ƒÁ`0ôÑh46þ4µüGäËsçÎÉd2òqdddttT&“=þœ|)•JGGG‡‡‡‡††d2Ù‹W”œõõõ"‘ÈÏÏ ¡PøàÁ{{{òPttô‡~8!J ‚P*JU‡µ)€Z …B2e%‰X,–H$‰¤¯¯O"‘ô÷÷“/úûû†††^ú½ÛkjצúÓŸLLLÞÿ}‚ þñtwwŸ>}š<ÄårëêêTÿÎôhŠÁÁÁÁÁA©T:44$•J¥Réðð°T*}þüùððððððÈÈÈóçÏ GÅRrdddlllttttttìGä2tüijÚ¢££ ‚ˆŠŠ"W±ZZZä#¹Æ%WÀ ƒÅbikk3 6›MŽèêêúúúS𦣣£K–,¹s繿º»»›Çã566’·Ôe2™CCCZZªžjEšÀ›Q¿7ÍÊÊJHHHNNVÚ±cÇ®]»Ö¯_O—˽ÿ¾ê_dRñ#ϵ]Ý0»èé齸qZ(êëë“Ï/]º”’’B'%%åÒ¥KäQ¢¢"µvü2HS ’»»{nnî„ÁÜÜ\G„P(ÌÏÏ‹ÅòqD"Q^^¹™ëàÁƒ'NœPÜ@BáÌ™3ê韄4*ñùü¨¨¨„„„®®®ÑÑÑîîîøøø¨¨(>ŸODbbbHHˆÁøCCÃààत$‚ ‚‚‚Ö¯_ÿ«_ý*;;»··W*•–——‡‡‡cO/PLÍW,--=uê”@ ‹Å†††^^^GmìÁ‡IDATõôô$ÂÕÕõܹsþþþJ ¢¢¢***È—999ÑÑÑååå½½½äµöïß8­m¯EšÀD¸N¯²µ8Ó  *¤)€ª¦ªBš¨ i  *¤)€ª¦ªBš¨ i  *¤)€ª¦ªBš¨ i  *¤)€ª¦ªBš¨ i  *¤)€ª¦ªBš¨J‹ê`ÊÈÈ ºµ¢Éårª{€™…F£1¹€ kg©Id¬M`*…„„Pݰ6€‰TY›j †]HªBš¨ i  *¤)€ª¦ªBš¨ i  *¤)€ª¦ªBš¨ i  *¤)€ª¦ªBš¨ i  *Ü-^.##ƒêf Ü-&"ï¿ÜÿÜ4/ðu êÅIEND®B`‚kamailio-4.0.4/doc/sip/figures/transaction.dia0000644000000000000000000000307712223032460020014 0ustar rootroot‹í\[oâ8~﯈è+â“‹“™ÒÑ´Ú•F3Ú‘v»+í2àÒì†9¡-ó°¿}m'%’Nb<¥\Š(Æ''þ¾ãs3\||œFÆ=ei˜Äý2­ŽAãQ2ãI¿óçͯïüÎÇ˳‹qHÞó¿ #SƒÏˆSñ_¿s—e³÷½ÞÃÃ-R’%̌¹™ÒÞ$ŠH¨×¹\=&Éß YÆÂá<£FL¦´ß’Ñ¿–Ìãq1c”D 3îIÔïœßÊG§wyÑ{6·ZÔŒÌ([J™Î’4äcÙb¶6¶>O<C)‰'—矜óü’Å®ŸM ›„q!ŠQå·¦Mïb¨EJ¤E Ó"%L³„eŒ„Y!i˜$%q.,csÚTT:"ÇcC!ÔTÀm˜eI•·$J+Õè=£S³«LX8®!âÊÐú¬‡pœÝ Õo.°Pp¦á0¢KÂ8S”°h$Aimçᘦu«»:¸>ó®íÕ¨^ÿH¯^åÖ‘e…°«r[3Š5)˜ž6þCGY¡õ‰Ç„wÆ×0¦rgæûr8îw¾Y5·ÄÅpÛzºáYR.·×ÅM1B†Ã%kF\›ID 9®¸]?}@ži¹]pøsSÉ£$Ž4KÅ^Ðrómpš^#žOG³ælË^ Ô°õ€°lí n!kI.„ô±k©å&ëškâ×öürTøekä”ü²õòË®á—}â×ëñËUá—«‘_NÉ/W/¿Ü~¹'~ý~ÝÐǬ‚_^ëÐË7=2¹zH&¥a3øÉååf⎪SÆ•¡jƒ,þ¬•†>ÿö×ç›_ÊC·Iüt!ñÒ¸%Ó0Zð«8íi¶)"_ëüÓ×Éœ…”5½Á;Nî²mH¥ AY¾«(åYòÑT ‰ÂI<¥Ë+ ÎR©#lË|¬À|dˈÎźÈ/f< D¦•Kð.LY–qÃüõñ˜A{8Ò|K‘§6€DrpdH»'€|Ëø¿:.h‡ãAò?Pà?‘±§‹ÿBšˆ€°Œ²­]°øþÿíËñ¿%€ûMüºâwë€Ø¡uÀeýÛ‘h¬—zndž¦ƒ[ÜÂ+¶"Z÷"ø^ájk 9eKÈÕ F©æF¶o€@ à˧ !Ë’Pø ´pM¢ˆ²cñ-á;È0Ù ÞÀ»†çëIˆŸäY6–^A’ßòw’KþÓ#á¿ ‡©ôs@c¿Ê~!èíBM¿NýÂWìç Wá¬hlBÙ0[ëq°kNœœ†¯ØÐA­;:ÀSaÐâ¾±(ã!hF{»pÛg{â±Qó ~{Ð3\mßÀ ¸{Õ²“]˜6ˆ(•ûké·g„¿úûhú–mà;LÊ+tjºzŠÔ"}¢¼…AF»¡ü‘•ª[ ¸ß¬¿¢ßùz×ÎAûÌÌô»¾éè*NØ&¶¥@p¹8¦ë9²{f¹<æjúE ú}PŸ£=Ów}TÙðíê1ÛtDèåÖLåºÊk«ìžÅ” Fi5 š2ckä¡}—ÂÉ×#Ð }¾„àb½ãq»um\-دi¼ °Ÿõ¬#‡< ç¼0_ªë¼YÔx<@ ÙŒ¨féJh¸ÓCX$4| =|Jh~fBÓ ·vu ®ç9ˆ­mÇó|GH´1*½@kóvÏUÞÜ=9þ’ÏãÖéYõ>O¨kí§Ï³•|^Ëþ‘ÏÃÜgÅé f¸Ø×æôVT®tz|Ü®…ØÃ/ø+£1e¬WbÆFe1€´ÓØ´íd&Kp%”L°+Ör ÕdZev mç–À¹ÔD`à‰$Ïðœüb©ëˆø³«˜\ ñ²d(4ñÿ!fþSPš`]YôÚBpˆ It1”«·Cl¯+`!ñ¶ð¤~ý2ÿÑSæmµu6 TÞÒ²Q Ü-£Лƒ\¸{Ò/é†ëí‡;¬½TK£a%I<Û@ÅÚ½G£’A#âXåkK”“UWrR3\v]KZ¸Ð#] î ˜gºõÔm\^µw“±RÕ|@JG¡x]Ž– z2{¸Ït~Z:_Ë%z“ÈT›ÈJËDbé˜ITsiôPd`A€ö``!Û¶>ÿs ™‰HèwËLô½øØY‰ý¨YIý]úûJù¨Øn˜`&©ï¨€ìûX—>þ}uýáòDدïÄÃ&Eãjk¿'sQb&‡žÜ9]¡ýŽÕpFõÄzY™»,›Y:¦£{ÂGƒiãÆÜs–!™æi3ÈôᙂB@ʬ°êlÕ÷¾AEÀººþ|ùáÅ©äz><ÊüÂ×&>¤M m˜ðB ú·úþ4ÈúOñ€ebN¬?YÆú,å§Â~]G%ÿ¡Ý ‚¨<Ï`j„(8”7C°³‡(¸ä£@üÍ’Sa¿¾“ÿ zá¶Ú "3½p[mt(íºÅsìOƒéÙWýL3‚8HÿGÛ—ÇH?\Y›ÂfBÀ•åEGF”¨î^J,`@ΨíØ'Ã}=6õßÅweÌ×nw"àѶk‚÷©-*ÉÚ¢MõJ[€è™¸åð‡:•bæð‡ó?v–A¢/ƒPÌtý8Ü.ƒ« pÚÀ{I£†VY»%ÜQõ¼y”%gH(bÛÓŠíšèR Vh¢§“˜m/9/[¹QÓ²¿~¹ygAr ’_køSAòéB\¬OdEÙ/)O¬ý‰¤Ö øƒ1ë ƒY6ËAfrþîÂ¥áõã.íf¢› …¤lÚ•°êÀ4Ö/[¶´lj<…^·LZ&žTÌtºþ‚5.)¢!‹å‰j¿e-†—i°‚²³£µ{UØHÍ1óù@×w¤·}"¶­XžÓµIîr›˜rúšÕ›£¿Šß14ë÷ªÕP»Uƒ°ZôÍœƒÊÁºÈ‘»+_£ljz-|;š{"5 me}i÷hoŽ÷ª‹!Øé­3³3çýb|Å`£dB“ÙœØñà|q—À¾ÑdîÁÒÒU>Öª~âgŸªÈ†$”XvíL’,ÈFÍ‘mÅÒR"Òg²=)Ù´;XðÃS› 3»‡|_âw±Ü­Úù'Slãݪћ£Xo‡üˆ»ûIv­ˆ4l¨˜g+‘ARdòú6-2¸|E£šOñ,26 úE|@•ð˜Ù3H4_k”1žºv;‡ÃúÐÄÎmçnÙi ´^.Ž¦êƒžÚ2¸]Lä‡Ð~è/¿û‚Ï{¹§rªEß—Çnƒ%ÀSûjj ðò4£‹<àë}]™ÉuÒ§F ,º>«¢•O³þÓÀªµ›EÝgyB×è÷ü|. 'pN1;²¬f úæâeÕÚÍ|Y÷Yº¦3ÿ*àÕï ¾xñ?Y½þv]kamailio-4.0.4/doc/sip/figures/register.dia0000644000000000000000000000215212223032460017304 0ustar rootroot‹íZÛnã6}ÏWÊ«—)ë¶Ž½ØÛ¢hv³ÏmÑ2[™2(:Ž_úí%%ÅWe¦iãHìhÄ£žÃ!gâ»O‹Ìz ¼ 9Ú¸¶EØ4O(K‡öû_>Dö§ÑÍ]BñGù“r¼°äV¨¿†ö\ˆåGÇY¯× ÛXädt âü³ ;ò&ÇíN°ÀÕ,§“• à 2´'xúWÊóKêÓ<˹õ€³¡};+_¶3ºsÆ6C-ñ’ð-Êb™TÚÄfyd;§~צBZX:ºýÜ¿­Y_hõ|±À<¥¬†âgUD¡¶QLŒ dFP¸ZŒ—9SQ#Mò<#˜U`‚¯H[¨bŠ3ÉljC°-ÀŒ ‘7y1ÃYÑè†s §vOI9MÎqÏt6ÚZËŽ­ÓekÑ*Çd„r†‹ã Ö\R¡¸’3/r9ómí¬êòN{ ÎŽÓNüB]~cy=Ä‘ ~c†q¬àP»pƒØu¡"ÊwSt¸|jóõÄõrŒ‡ÿ ã¨ÓŠö ®h¯¨– ‘Ù5í5¯éX7=ŽGK/ǘó|]# WAxÚ㌰TÌOvVDúXå6ÝêRy2‹ßó ê¬ÿ¤3ߠμlÒ ÿ®³—ÑÙ=y :ëëꬠÚýc2«± ýAPˆTžýÖ*¦æåžé¤)kãÊñÛ—_û~ÿåÛÍÚÉ­)' a‚ÊÚ¢C=9ËÙÓ£ÕGk†4ÛÈçbVØV!6õ®UÝýs¾âTÖ¸-CžšÎÅ%B3ÂjYí7Tþnùj‹"‹Â”-ÈvºvKÐíRv\º|ݵàÏ—y20±J,Äþ ýÞk,¾^‰Úuyû«ýÌ #Ð?Éz&kSOÖ¦HJ]V+æN±ÞÙÊézßþtq)¡þYÏ3YE¢Hyè/Rsä™;îygkGõ4½(^€‘3;A¤ËˆŒE†Yx•bákì? ­ϩLVW²%èø&@±®ì#¨Y3¡ú Êõª}¥³)˜,HJåg̯Dôšô½ÉcìÔ£÷{hßb}¦Õâú½ ª«€#h²­·sµ¡£Ç{Ãåò ¡~ÇIžfz.š•M…¥ÜWnº\eÏE—×·™€Q‡V·Ì“²~0•CÉC\e`•%rdòßgûΞ&hÝPÞ“°$ìé'á¨IÍ$ájëƒAPnúZ 0˜‘ëZ_¿š„«Ëá?á:Ûo°~½etóûÂý:-(kamailio-4.0.4/doc/sip/figures/bye.png0000644000000000000000000001614012223032460016270 0ustar rootroot‰PNG  IHDR¨ôAë%¯sBITÛáOà pHYsÐй‹çŸIDATxœíÝ}PgâðEД^‚„PmAK£‚ÑÚ^´'Á;ñ¬èQïŽÃ79™2£Ž X#©-G½óÐQ¯õ@qðЛr^™Ó*R‰ž8h•…ˆL¯X1Öð¢ÜßO»·¿!$ò|?8Ï>y²<Ïî“o6»1ëÆ²,@“QŽîÀpsêàsssst„ç’ƒ«9ç|pÎ^ ÈîÁ7cÆŒëׯ“rww·OOOY¼~ýúo¼Áð¶²6·ËïE“ºüx3LrççT³ÔîÁ·råÊãÇ“ree¥^¯ÿÏþCsssW®\É0 Î3 Ûs˜a’[ÁÁ„µ³{÷î?¾§§‡eÙ?ÿùÏcƌٷo˲===ãÆkll$ã7Ù ä¹ Ã:tH.—=ú­·Þºqã©ïêêŠ÷òòòòòúýïßÕÕŵçÿéVkÒfß¾}'NtwwgYÖh4¾ÿþû/½ô’§§gttt{{;iÖÝÝ””äëëëíí‘‘1h7øë|úôillì‹/¾8~üø?ýéO–tÃÆº;;;×®]k¾K`9{Lr>ÌRÁg©Ýø&Mšôú믟?ža˜ÒÒÒ¸¸¸«W¯2 SXX8yòd¹\ÎßümA_¹råñãÇË—/'•;wî¼ÿ~}}}]]]cccjjª™ô»Z¾ëׯWVVF†aÒÓÓËËË+++›››E"ÑöíÛI›?ü°ªªª²²²¡¡á›o¾´üu~ðÁ­­­7nܸxñâ@ýä?EÀö»?øà½^ߨØxóæÍË—/›Y9X“Üf©™•[CÀȧŸ~ú»ßýŽeY??¿o¾ùF.—³,õÙgŸq¤À->|ø”;::<< ‚¨ƒàê ø€:> ‚¨ƒàê ø€:> ‚¨ã:Á×ÔÔô›ßüF*•¾ð sçÎ%¿Éüÿû™‰dñâÅuuu}WbIé~øÍ~“Z“š   )S¦˜4 ìÝ?ËEEEM™2E§Ó}ÿý÷}ôÑgŸ}Ö· ù Â{÷î©T*r·SëÚ€“s)7òX±sÏŸ?æéé)“ÉV¯^ÝÒÒòÿnûÖ÷GPY–-++S(J¥òêÕ«fš9ÜèÑ£;;;ûÖ3ýýÐkWWW¿?è:PÆ‚;Š.\¸°¤¤„´/**R«Õ¾¾¾Ü½fº»»_zé%›Gi óû—eÙÀÀÀÉ“'ók¾úê«… ¾ð >>>«V­jnn¶{/åS:$$D£Ñ<|øðéÓ§ÅÅÅ‹/&õ}çskk«F£Q©T}WbI›ÇŠVPPÐÞÞÞÜÜœ˜˜øË_þrðà{ï½÷>þøã?þñ ƒöÀæÌ™³mÛ¶††“ú~'ÊÎ;(ü6 ÃDGGs)–””ôî»ï677?xð`áÂ…ÉÉÉ,ËVVV¾ñÆ===ÝÝÝ3gά««Û¸qãÇLžRXX¸dÉ!l1A&Ê0õU .0¥ñF>«w.ÑÑÑ!‰ ¾§OŸÊd²ÆÆÆ¦¦&™LöôéSó=p o¿ý6..î•W^ñöö^¹reSS©çOâ'?ùÉÂ… kkkû®d 6Œew]µjÕ¾}û222¶mÛÆ²lCCÃĉ»»»Y–MHHÈË˳ËÈ#ÈD±cÿìÀ¦4ÞÈbõÎ%þõ¯…††|'Ožœ?>)/\¸ðÿø‡ù8ƒï¾ûnÓ¦M!!!d±ßwÈ ÔÆ¤~ ;ŠžëvîçŸîïï_QQA–eSRR~õ«_ݽ{×h4Þ½{wÑ¢E;vì`YvïÞ½111ü?·wï^nÑ©fÉ™3g~þóŸ‹D"//¯%K–èt:Ro‰ÒïEñ‹_üóŸÿ$ JJJ-ZDÊ_~ù¥\.þü¹UÀ edq)7òX±s322^}õÕššî!†eÙgÏž¥¤¤øûû»»»ûûû¿ÿþûäcüÌ™3¹Ë:„V«9s&ÛçþDöêˆöé§Ÿj4v@‰2²¸À”Æù@lß¹N±ƒ]Ûãǹ3Ó!ÈD1 é<8‡¿‘ ·—´#777ww÷ƒ¾÷Þ{Žî €¾ÿþû9sæûùù9º/6ñ¼ X o*àJ¸7ò‘žz n(úሯ®®®³³ÓÝÝ}Ô¨Qîîî} -º»»;v”3 Vÿú€D"1ùÿü#B]]Á`°î¹S¦L‘H$?ñÍŸ?_«ÕZ±–yóæY÷DÇÒh4UUUÖ=7==}úôéÂöÇÞBCC/]ºdÝsµZí¼yó„í½Q5^­V;þ|ëžZRR"l†íû÷‡#>…BñøñãÞÞÞÞÞÞçÏŸsÿööö¶´´<{öL*•Ž5Š_O #ôˆïêÕ«Vo¸ääda;` ‰D¢R©z”‘ܾŽÄÃ=†a¦NÚÞÞÞïCæÇË0ŒX,f,9ÇGÂuĽ šwõêÕ¶¶¶~"ƒfëÔjµL&³gKî_30^×fáx)½ª«V«z(##ƒ4 d¢PÈu~zÀB> ‚¨ƒàê ø€:> ‚¨ƒàê ø€:> ‚¨ƒàê ø€:> ‚¨ƒàê ø€:> ‚ÀVn?‰Dsçνyóæ‚ NŸ>Í58uêÔ;ï¼ÃoÉq\¯©†à˲,Ëêõú¥K—®\¹2===--{4---==ß’ã þÒÁ ˜_|111Q§Óýìg?óóó+,,dæÜ¹s&Lxûí·Ý;ø€`:;;:4{öl†w”grô΀ÒÛK‹;['‹É½ê§OŸ>iÒ$F T*û¶$ði×!FÌ΃3#'ì:;;ÓÒÒ’’’Hå®]»>ùä“?ü°oKœãs¬| ΃ÓóôôŒ/++#‹¯½ö÷/8•‘|΃ÓêêêÊÌÌ ptG`#ïŸÉùãÄÄÄÅ‹§§§ÿõ¯ut×€^䌊‡‡ÇÌ™3ÿö·¿ Ú’ƒO$1’‚çÁ9™™`&a*:‰‘|dÒtuueee%%%iµZ†avíÚÐÐÐз%@¿FÞ9>œ?6/>>þرcF£ÑÑ»ÀþÄÈ >œ?6O§ÓÅÆÆáåá’°1’‚|)ÏËËëïÿû ç)ÿ^® û×F#&ø¸/åõôô”——Ïš5‹ÿP¿-ñ=>¼<\ö¯ÕFLðÕðòpmØ¿V@ðÑ/׆ý;$>ºàåáÚ°-dið…††öýÏÿ.‰|5zäŽ÷ñãǃîMþ˃Îs Ø¿”ÃPÇÒàÓjµ}¯–º¤yóæèñz{{º7 ENNNmmmLLŒ•_÷Áþ¥ÜHú/k`;…B‘ššºjÕ*ìz„ýk!lZà%áÚ°‡ÛÈõá%áÚ°­€-åÊð’pmØ¿VÃörMxI¸6ì_a«¹…B±nÝ:¼$\ö¯ °í\Í‘#Gݰ#ì_Aà Ì@PÁÔAðu|@PÁÔAðu|@PÁÔAðu|@PÁÔAðu|Ö;þ|XX˜§§§L&[½zuKK ©gY6%%E*•úøøìرƒýñVöÕ›(((˜1c†H$R*•gΜáêù7H½uëV`` ÝF08·‰D¢¹sçÞ¼ysÁ‚§OŸæœ:uêwÞá·ä8®×?@ðY/##cË–->¬­­;vltt4©ÏÊʺråJMMMUU•V«ÍÎÎ6_ÏWVVàÀ'Ož|8**ꫯ¾š>}º]` ’_]]]YYYIIIZ­–a˜]»v444ômé<ðQ×&yyykÖ¬ÉÏÏW©T\¥R©,//'劊 î}o z>µZ]PPÀ-ž={V­Vs‹qqq»wï^°`AEE…àc°Ž§§g|||YYY|íµ×¸–ðÁ§Óébccùg¦\ÕÞ½{SRRŠŠŠ~úÓŸòë×®]›ššúÝwßÝ¿çα±±æëù¶oß¾cÇŽ’’’îîî’’F³}ûv~ƒ5kÖ8p`Ñ¢E4laººº233ì±òøøøcÇŽF×+ÔU–eëëëcbbÈ ÞGîU3ËÇÛwc –eŸ?¾uëVoooooïíÛ·?þœ´¨ÞÄéÓ§•JåèÑ£ƒ‚‚N:Åÿs\9??_*•Ú4Îá*§k³ëU]ÂÃÃC¥R}ýõ×ü‡úmiyìpHÿ ENNNOO…í¯0çøt:]ZZÚ‰'„f'ÖïeÆÍÍmÏž={öì±°ÞDDDDDD„ù?9Äþi ùß÷!3--D>G¦¥¥¥¦¦®ZµŠ]ÙÂÖº¤CAAAv9ø‘€ic}ð!ò`ø ’<Ö"ËÆZð!òÀyXH–ž#ljjеüòÅæÍ›oܸay?œMhh¨£» ˜‘;ŸgΜ9hþ¥ /¤à Ì@KƒÏßß?''§¶¶–û¦žyû÷ï·ü«:N…Îïyk¹óÙÛÛ{ÐÑ‘où‘t²ðÇþ†vÄÇÿ¶•ÀV'’5uàX6¦õçø0üI[/n þ`x˜6Â\ÕEü€ýž0B~ñ²SªO¤£‚¯(täÈ{¬_`ê ø€:> ‚¨ƒàê ø€:> ‚¨ƒàê ø€:> ‚¨ƒàê ø€:>°ÈùóçÃÂÂ<==e2ÙêÕ«[ZZH=˲)))R©ÔÇÇgÇŽìw¶¨ÞDAAÁŒ3D"‘R©°T^^Þš5kòóóU*W©T*ËËËI¹¢¢B©Tš¯çS«ÕÜâÙ³gÕj5··{÷î TTT;|Ô‹ìÝ»wÿþýEEE&gÜÖ®]›ššúÅ_°,»sçÎõë×›¯çÛ¾}ûŠ+äryHHHii©F£ÉÏÏç7X³f͘1c-ZtêÔ©¡Æ‚à‹$''3 3mÚ4®Æ`0ˆÅâ„„„»wï’ú„„.àªçS«ÕYYY7n¬««S(™™™Ü5NTTÔ˜1c"""=z$ÔXðQþG§ÓÅÆÆò/­rú~)„\»pssÛ³g^¯×ëõ»wïæ¾xº þ(?½¨?í(Œ?0 eñçâÀ!!ñçè^ØŽø€:> ‚¨ƒàê ø€:> ‚¨ƒàê ø€:> ‚¨ƒàê ø€:> ‚À&çÏŸ óôô”Éd«W¯nii!õ,˦¤¤H¥RŸ;v°,k¾ÞDAAÁŒ3D"‘R©ê˜Òétiii'Nœ@Þ¹*Àÿ ò(à`De|@;D…|@/Dµ|@£¦¦¦ØØXË#oóæÍ7nܰw¯`Øàë,@ÐÈßß?''§¶¶6&&ÆÃcðÏ=û÷ïžïß Ž|¡L ø€^ …bHñ.Á´CüQÁÀ0ˆ?Ê øþñG €)ÄŸËCðôñç°;Ì!ñçè^€ÀpÄÔAðu|@PÁÔAðu|@PÁÔAðu|@PÁÔAðu|@PÁÔAðu|@PÁÔAðu|@PÁÔAðu|@PÁÔAðu|@7–eÝ€aõSkâí­öpIEND®B`‚kamailio-4.0.4/doc/sip/figures/invite1.eps0000644000000000000000000013252512223032460017101 0ustar rootroot%!PS-Adobe-2.0 EPSF-2.0 %%Title: invite1.dia %%Creator: Dia v0.91 %%CreationDate: Tue Jul 15 14:59:19 2003 %%For: janakj %%Orientation: Portrait %%Magnification: 1.0000 %%BoundingBox: 0 0 446 598 %%BeginSetup %%EndSetup %%EndComments %%BeginProlog [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def /cp {closepath} bind def /c {curveto} bind def /f {fill} bind def /a {arc} bind def /ef {eofill} bind def /ex {exch} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth pop} bind def /tr {translate} bind def /ellipsedict 8 dict def ellipsedict /mtrx matrix put /ellipse { ellipsedict begin /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc savematrix setmatrix end } def /mergeprocs { dup length 3 -1 roll dup length dup 5 1 roll 3 -1 roll add array cvx dup 3 -1 roll 0 exch putinterval dup 4 2 roll putinterval } bind def /dpi_x 300 def /dpi_y 300 def /conicto { /to_y exch def /to_x exch def /conic_cntrl_y exch def /conic_cntrl_x exch def currentpoint /p0_y exch def /p0_x exch def /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def /p2_x p1_x to_x p0_x sub 1 3 div mul add def /p2_y p1_y to_y p0_y sub 1 3 div mul add def p1_x p1_y p2_x p2_y to_x to_y curveto } bind def /start_ol { gsave 1.1 dpi_x div dup scale} bind def /end_ol { closepath fill grestore } bind def 28.346000 -28.346000 scale -8.150000 -27.050000 translate %%EndProlog 0.100000 slw [] 0 sd [] 0 sd 0 slc 0.000000 0.000000 0.000000 srgb n 9.000000 7.000000 m 9.000000 27.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 16.000000 7.000000 m 16.000000 27.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 7.000000 m 23.000000 27.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.000000 8.000000 m 15.200000 8.000000 l s 0 slj n 15.200000 8.400000 m 16.000000 8.000000 l 15.200000 7.600000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 16.000000 10.000000 m 9.800000 10.000000 l s 0 slj n 9.800000 9.600000 m 9.000000 10.000000 l 9.800000 10.400000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 16.000000 12.000000 m 22.200000 12.000000 l s 0 slj n 22.200000 12.400000 m 23.000000 12.000000 l 22.200000 11.600000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 14.000000 m 16.800000 14.000000 l s 0 slj n 16.800000 13.600000 m 16.000000 14.000000 l 16.800000 14.400000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 16.000000 m 16.800000 16.000000 l s 0 slj n 16.800000 15.600000 m 16.000000 16.000000 l 16.800000 16.400000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 16.000000 18.000000 m 9.800000 18.000000 l s 0 slj n 9.800000 17.600000 m 9.000000 18.000000 l 9.800000 18.400000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 20.000000 m 16.800000 20.000000 l s 0 slj n 16.800000 19.600000 m 16.000000 20.000000 l 16.800000 20.400000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.000000 24.000000 m 22.200000 24.000000 l s 0 slj n 22.200000 24.400000 m 23.000000 24.000000 l 22.200000 23.600000 l f gsave 11.550000 7.600000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 11.719333 7.600000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 12.159600 7.600000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 2972 3392 lineto 2516 3392 lineto 1585 521 lineto 599 3392 lineto 138 3392 lineto 1346 0 lineto 1806 0 lineto end_ol grestore gsave 12.566000 7.600000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 12.735333 7.600000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 13.107867 7.600000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 18.685000 11.725000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 18.854333 11.725000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 19.294600 11.725000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 2972 3392 lineto 2516 3392 lineto 1585 521 lineto 599 3392 lineto 138 3392 lineto 1346 0 lineto 1806 0 lineto end_ol grestore gsave 19.701000 11.725000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 19.870333 11.725000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 20.242867 11.725000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 11.000000 9.600000 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 11.338667 9.600000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 11.677333 9.600000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 12.016000 9.600000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 12.185333 9.600000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 12.557867 9.600000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 12.761067 9.600000 translate 0.035278 -0.035278 scale start_ol 1788 2432 moveto 1120 533 lineto 502 2432 lineto 92 2432 lineto 908 -16 lineto 760 -403 lineto 700 -575 613 -640 452 -640 curveto 396 -640 332 -631 249 -612 curveto 249 -963 lineto 327 -1005 406 -1024 507 -1024 curveto 631 -1024 765 -982 866 -908 curveto 986 -819 1055 -717 1129 -521 curveto 2203 2432 lineto 1788 2432 lineto end_ol grestore gsave 13.065867 9.600000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 13.201333 9.600000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 13.540000 9.600000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 18.085000 13.625000 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 18.423667 13.625000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 18.762333 13.625000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.101000 13.625000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.270333 13.625000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 19.642867 13.625000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 19.846067 13.625000 translate 0.035278 -0.035278 scale start_ol 1788 2432 moveto 1120 533 lineto 502 2432 lineto 92 2432 lineto 908 -16 lineto 760 -403 lineto 700 -575 613 -640 452 -640 curveto 396 -640 332 -631 249 -612 curveto 249 -963 lineto 327 -1005 406 -1024 507 -1024 curveto 631 -1024 765 -982 866 -908 curveto 986 -819 1055 -717 1129 -521 curveto 2203 2432 lineto 1788 2432 lineto end_ol grestore gsave 20.150867 13.625000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 20.286333 13.625000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 20.625000 13.625000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 17.950000 15.700000 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 18.288667 15.700000 translate 0.035278 -0.035278 scale start_ol 1821 1738 moveto 2136 1929 2240 2086 2240 2378 curveto 2240 2861 1835 3200 1248 3200 curveto 666 3200 256 2861 256 2378 curveto 256 2090 360 1934 670 1738 curveto 307 1567 128 1308 128 966 curveto 128 395 563 0 1184 0 curveto 1805 0 2240 395 2240 961 curveto 2240 1308 2103 1567 1821 1738 curveto 1248 2838 moveto 1619 2838 1856 2657 1856 2373 curveto 1856 2101 1614 1920 1248 1920 curveto 882 1920 640 2101 640 2377 curveto 640 2657 882 2838 1248 2838 curveto 1184 1536 moveto 1584 1536 1856 1300 1856 951 curveto 1856 598 1584 362 1175 362 curveto 784 362 512 602 512 951 curveto 512 1300 784 1536 1184 1536 curveto end_ol grestore gsave 18.627333 15.700000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 18.966000 15.700000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.135333 15.700000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 19.575600 15.700000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 19.711067 15.700000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 20.049733 15.700000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 20.388400 15.700000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 20.523867 15.700000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 20.862533 15.700000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 10.835000 17.575000 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 11.173667 17.575000 translate 0.035278 -0.035278 scale start_ol 1821 1738 moveto 2136 1929 2240 2086 2240 2378 curveto 2240 2861 1835 3200 1248 3200 curveto 666 3200 256 2861 256 2378 curveto 256 2090 360 1934 670 1738 curveto 307 1567 128 1308 128 966 curveto 128 395 563 0 1184 0 curveto 1805 0 2240 395 2240 961 curveto 2240 1308 2103 1567 1821 1738 curveto 1248 2838 moveto 1619 2838 1856 2657 1856 2373 curveto 1856 2101 1614 1920 1248 1920 curveto 882 1920 640 2101 640 2377 curveto 640 2657 882 2838 1248 2838 curveto 1184 1536 moveto 1584 1536 1856 1300 1856 951 curveto 1856 598 1584 362 1175 362 curveto 784 362 512 602 512 951 curveto 512 1300 784 1536 1184 1536 curveto end_ol grestore gsave 11.512333 17.575000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 11.851000 17.575000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 12.020333 17.575000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 12.460600 17.575000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 12.596067 17.575000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 12.934733 17.575000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 13.273400 17.575000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 13.408867 17.575000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 13.747533 17.575000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 18.800000 19.650000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 19.138667 19.650000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.477333 19.650000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.816000 19.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.985333 19.650000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 20.459467 19.650000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 16.000000 22.000000 m 9.800000 22.000000 l s 0 slj n 9.800000 21.600000 m 9.000000 22.000000 l 9.800000 22.400000 l f gsave 11.449700 21.587500 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 11.788367 21.587500 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 12.127033 21.587500 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 12.465700 21.587500 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 12.635033 21.587500 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 13.109167 21.587500 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore gsave 13.699700 23.687500 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 14.106100 23.687500 translate 0.035278 -0.035278 scale start_ol 2977 2327 moveto 2845 3043 2428 3392 1702 3392 curveto 1258 3392 900 3254 655 2988 curveto 355 2668 192 2207 192 1683 curveto 192 1150 360 693 673 377 curveto 927 120 1254 0 1685 0 curveto 2492 0 2946 428 3046 1288 curveto 2604 1288 lineto 2567 1069 2521 920 2452 793 curveto 2314 530 2028 381 1668 381 curveto 1000 381 576 889 576 1687 curveto 576 2507 982 3011 1631 3011 curveto 1903 3011 2157 2932 2295 2809 curveto 2419 2700 2488 2564 2539 2327 curveto 2977 2327 lineto end_ol grestore gsave 14.546367 23.687500 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore gsave 11.999700 25.637500 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 12.439967 25.637500 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 12.812500 25.637500 translate 0.035278 -0.035278 scale start_ol 832 1408 moveto 1879 1408 lineto 2141 1408 2348 1488 2527 1654 curveto 2729 1843 2816 2065 2816 2381 curveto 2816 3028 2451 3392 1803 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1408 lineto 832 1792 moveto 832 3011 lineto 1719 3011 lineto 2126 3011 2368 2782 2368 2402 curveto 2368 2021 2126 1792 1719 1792 curveto 832 1792 lineto end_ol grestore gsave 13.218900 25.637500 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 13.388233 25.637500 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 13.794633 25.637500 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 13.963967 25.637500 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 14.167167 25.637500 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 14.505833 25.637500 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 14.844500 25.637500 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1478 lineto 704 1819 960 2093 1277 2093 curveto 1565 2093 1728 1922 1728 1621 curveto 1728 0 lineto 2112 0 lineto 2112 1478 lineto 2112 1819 2368 2093 2685 2093 curveto 2968 2093 3136 1918 3136 1621 curveto 3136 0 lineto 3520 0 lineto 3520 1773 lineto 3520 2197 3270 2432 2816 2432 curveto 2492 2432 2298 2335 2071 2062 curveto 1931 2321 1741 2432 1433 2432 curveto 1116 2432 908 2312 704 2021 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 15.352500 25.637500 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore 0.150000 slw [] 0 sd [] 0 sd 0 slc n 9.124650 26.000000 m 22.849700 26.000000 l s 0.150000 slw [] 0 sd 0 slj 0 slc n 8.724650 26.000000 m 8.724650 25.862500 8.862150 25.725000 8.999650 25.725000 c 9.137150 25.725000 9.274650 25.862500 9.274650 26.000000 c 9.274650 26.137500 9.137150 26.275000 8.999650 26.275000 c 8.862150 26.275000 8.724650 26.137500 8.724650 26.000000 c f 0.150000 slw [] 0 sd 0 slj 0 slc n 23.249700 26.000000 m 23.249700 26.137500 23.112200 26.275000 22.974700 26.275000 c 22.837200 26.275000 22.699700 26.137500 22.699700 26.000000 c 22.699700 25.862500 22.837200 25.725000 22.974700 25.725000 c 23.112200 25.725000 23.249700 25.862500 23.249700 26.000000 c f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 22.450000 7.000000 m 23.475000 7.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 15.429700 6.947530 m 16.429700 6.947530 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 8.500000 6.950000 m 9.500000 6.950000 l s gsave 8.150000 6.637500 translate 0.035278 -0.035278 scale start_ol 2977 2327 moveto 2845 3043 2428 3392 1702 3392 curveto 1258 3392 900 3254 655 2988 curveto 355 2668 192 2207 192 1683 curveto 192 1150 360 693 673 377 curveto 927 120 1254 0 1685 0 curveto 2492 0 2946 428 3046 1288 curveto 2604 1288 lineto 2567 1069 2521 920 2452 793 curveto 2314 530 2028 381 1668 381 curveto 1000 381 576 889 576 1687 curveto 576 2507 982 3011 1631 3011 curveto 1903 3011 2157 2932 2295 2809 curveto 2419 2700 2488 2564 2539 2327 curveto 2977 2327 lineto end_ol grestore gsave 8.590267 6.637500 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 8.928933 6.637500 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 9.064400 6.637500 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 9.199867 6.637500 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 9.538533 6.637500 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 14.700000 6.637500 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 15.106400 6.637500 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 15.275733 6.637500 translate 0.035278 -0.035278 scale start_ol 832 1408 moveto 1879 1408 lineto 2141 1408 2348 1488 2527 1654 curveto 2729 1843 2816 2065 2816 2381 curveto 2816 3028 2451 3392 1803 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1408 lineto 832 1792 moveto 832 3011 lineto 1719 3011 lineto 2126 3011 2368 2782 2368 2402 curveto 2368 2021 2126 1792 1719 1792 curveto 832 1792 lineto end_ol grestore gsave 15.682133 6.637500 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 15.851467 6.637500 translate 0.035278 -0.035278 scale start_ol 832 1408 moveto 1879 1408 lineto 2141 1408 2348 1488 2527 1654 curveto 2729 1843 2816 2065 2816 2381 curveto 2816 3028 2451 3392 1803 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1408 lineto 832 1792 moveto 832 3011 lineto 1719 3011 lineto 2126 3011 2368 2782 2368 2402 curveto 2368 2021 2126 1792 1719 1792 curveto 832 1792 lineto end_ol grestore gsave 16.257867 6.637500 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 16.461067 6.637500 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 16.799733 6.637500 translate 0.035278 -0.035278 scale start_ol 1346 1258 moveto 2157 2432 lineto 1723 2432 lineto 1143 1550 lineto 562 2432 lineto 124 2432 lineto 931 1239 lineto 78 0 lineto 516 0 lineto 1129 933 lineto 1733 0 lineto 2180 0 lineto 1346 1258 lineto end_ol grestore gsave 17.104533 6.637500 translate 0.035278 -0.035278 scale start_ol 1788 2432 moveto 1120 533 lineto 502 2432 lineto 92 2432 lineto 908 -16 lineto 760 -403 lineto 700 -575 613 -640 452 -640 curveto 396 -640 332 -631 249 -612 curveto 249 -963 lineto 327 -1005 406 -1024 507 -1024 curveto 631 -1024 765 -982 866 -908 curveto 986 -819 1055 -717 1129 -521 curveto 2203 2432 lineto 1788 2432 lineto end_ol grestore gsave 22.050000 6.687500 translate 0.035278 -0.035278 scale start_ol 2977 2327 moveto 2845 3043 2428 3392 1702 3392 curveto 1258 3392 900 3254 655 2988 curveto 355 2668 192 2207 192 1683 curveto 192 1150 360 693 673 377 curveto 927 120 1254 0 1685 0 curveto 2492 0 2946 428 3046 1288 curveto 2604 1288 lineto 2567 1069 2521 920 2452 793 curveto 2314 530 2028 381 1668 381 curveto 1000 381 576 889 576 1687 curveto 576 2507 982 3011 1631 3011 curveto 1903 3011 2157 2932 2295 2809 curveto 2419 2700 2488 2564 2539 2327 curveto 2977 2327 lineto end_ol grestore gsave 22.490267 6.687500 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 22.828933 6.687500 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 22.964400 6.687500 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 23.099867 6.687500 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 23.438533 6.687500 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore showpage kamailio-4.0.4/doc/sip/figures/register.eps0000644000000000000000000010646712223032460017354 0ustar rootroot%!PS-Adobe-2.0 EPSF-2.0 %%Title: register.dia %%Creator: Dia v0.91 %%CreationDate: Tue Jul 15 14:59:44 2003 %%For: janakj %%Orientation: Portrait %%Magnification: 1.0000 %%BoundingBox: 0 0 307 314 %%BeginSetup %%EndSetup %%EndComments %%BeginProlog [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def /cp {closepath} bind def /c {curveto} bind def /f {fill} bind def /a {arc} bind def /ef {eofill} bind def /ex {exch} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth pop} bind def /tr {translate} bind def /ellipsedict 8 dict def ellipsedict /mtrx matrix put /ellipse { ellipsedict begin /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc savematrix setmatrix end } def /mergeprocs { dup length 3 -1 roll dup length dup 5 1 roll 3 -1 roll add array cvx dup 3 -1 roll 0 exch putinterval dup 4 2 roll putinterval } bind def /dpi_x 300 def /dpi_y 300 def /conicto { /to_y exch def /to_x exch def /conic_cntrl_y exch def /conic_cntrl_x exch def currentpoint /p0_y exch def /p0_x exch def /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def /p2_x p1_x to_x p0_x sub 1 3 div mul add def /p2_y p1_y to_y p0_y sub 1 3 div mul add def p1_x p1_y p2_x p2_y to_x to_y curveto } bind def /start_ol { gsave 1.1 dpi_x div dup scale} bind def /end_ol { closepath fill grestore } bind def 28.346000 -28.346000 scale -0.300000 -11.100099 translate %%EndProlog 0.100000 slw [] 0 sd [] 0 sd 0 slc 0.000000 0.000000 0.000000 srgb n 1.850000 1.050000 m 1.850000 11.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.830000 0.980000 m 9.850000 11.050000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 1.850000 3.050000 m 9.050000 3.050000 l s 0 slj n 9.050000 3.450000 m 9.850000 3.050000 l 9.050000 2.650000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.850000 5.050000 m 2.650000 5.050000 l s 0 slj n 2.650000 4.650000 m 1.850000 5.050000 l 2.650000 5.450000 l f gsave 4.150000 1.900000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 4.590267 1.900000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 4.996667 1.900000 translate 0.035278 -0.035278 scale start_ol 3264 1792 moveto 1836 1792 lineto 1836 1408 lineto 2880 1408 lineto 2880 1324 lineto 2880 777 2429 381 1803 381 curveto 1455 381 1140 499 938 705 curveto 712 933 576 1315 576 1709 curveto 576 2494 1056 3011 1780 3011 curveto 2302 3011 2678 2757 2772 2339 curveto 3217 2339 lineto 3097 3008 2580 3392 1808 3392 curveto 1397 3392 1065 3290 802 3081 curveto 414 2770 192 2269 192 1687 curveto 192 693 824 0 1732 0 curveto 2188 0 2553 167 2880 524 curveto 2988 0 lineto 3264 0 lineto 3264 1792 lineto end_ol grestore gsave 5.470800 1.900000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 5.640133 1.900000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 6.046533 1.900000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 6.419067 1.900000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 6.825467 1.900000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 4.150000 2.700000 translate 0.035278 -0.035278 scale start_ol 2553 0 moveto 3262 2432 lineto 2829 2432 lineto 2350 538 lineto 1875 2432 lineto 1405 2432 lineto 945 538 lineto 452 2432 lineto 28 2432 lineto 728 0 lineto 1161 0 lineto 1627 1908 lineto 2115 0 lineto 2553 0 lineto end_ol grestore gsave 4.590267 2.700000 translate 0.035278 -0.035278 scale start_ol 1055 3392 moveto -37 0 lineto 217 0 lineto 1309 3392 lineto 1055 3392 lineto end_ol grestore gsave 4.759600 2.700000 translate 0.035278 -0.035278 scale start_ol 1244 2432 moveto 589 2432 192 1978 192 1216 curveto 192 454 584 0 1248 0 curveto 1903 0 2304 454 2304 1199 curveto 2304 1982 1916 2432 1244 2432 curveto 1248 2075 moveto 1669 2075 1920 1751 1920 1203 curveto 1920 685 1660 357 1248 357 curveto 831 357 576 681 576 1216 curveto 576 1747 831 2075 1248 2075 curveto end_ol grestore gsave 5.098267 2.700000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 5.267600 2.700000 translate 0.035278 -0.035278 scale start_ol 2139 1595 moveto 2121 1818 2070 1963 1979 2090 curveto 1814 2305 1527 2432 1193 2432 curveto 548 2432 128 1947 128 1194 curveto 128 463 539 0 1188 0 curveto 1759 0 2120 328 2166 888 curveto 1779 888 lineto 1714 534 1516 357 1189 357 curveto 765 357 512 673 512 1195 curveto 512 1747 761 2075 1180 2075 curveto 1503 2075 1706 1902 1752 1595 curveto 2139 1595 lineto end_ol grestore gsave 5.572400 2.700000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 5.775600 2.700000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 6.114267 2.700000 translate 0.035278 -0.035278 scale start_ol 2304 3392 moveto 1920 3392 lineto 1920 2057 lineto 1758 2302 1498 2432 1173 2432 curveto 541 2432 128 1960 128 1238 curveto 128 472 523 0 1162 0 curveto 1489 0 1716 124 1920 421 curveto 1920 0 lineto 2304 0 lineto 2304 3392 lineto 1228 2070 moveto 1652 2070 1920 1733 1920 1208 curveto 1920 699 1647 362 1232 362 curveto 799 362 512 703 512 1216 curveto 512 1729 799 2070 1228 2070 curveto end_ol grestore gsave 6.452933 2.700000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 6.791600 2.700000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 7.130267 2.700000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 7.299600 2.700000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 7.435067 2.700000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 7.773733 2.700000 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 7.909200 2.700000 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore gsave 5.350000 4.600000 translate 0.035278 -0.035278 scale start_ol 1536 768 moveto 1536 0 lineto 1920 0 lineto 1920 768 lineto 2404 768 lineto 2404 1152 lineto 1920 1152 lineto 1920 3287 lineto 1636 3287 lineto 158 1217 lineto 158 768 lineto 1536 768 lineto 1536 1152 moveto 513 1152 lineto 1536 2591 lineto 1536 1152 lineto end_ol grestore gsave 5.688667 4.600000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 6.027333 4.600000 translate 0.035278 -0.035278 scale start_ol 2396 3200 moveto 212 3200 lineto 212 2796 lineto 1977 2796 lineto 1198 1713 880 1047 636 0 curveto 1069 0 lineto 1249 1021 1659 1898 2396 2856 curveto 2396 3200 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 1.350000 1.050000 m 2.350000 1.050000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.330000 0.980000 m 10.330000 0.980000 l s gsave 0.300000 0.750000 translate 0.035278 -0.035278 scale start_ol 2560 3392 moveto 2560 1091 lineto 2560 651 2220 381 1659 381 curveto 1400 381 1190 439 1022 552 curveto 849 678 768 844 768 1091 curveto 768 3392 lineto 384 3392 lineto 384 1083 lineto 384 415 869 0 1659 0 curveto 2441 0 2944 424 2944 1083 curveto 2944 3392 lineto 2560 3392 lineto end_ol grestore gsave 0.740267 0.750000 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore gsave 1.045067 0.750000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 1.383733 0.750000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 1.586933 0.750000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 1.756267 0.750000 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 2.162667 0.750000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 2.501333 0.750000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 2.840000 0.750000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 3.178667 0.750000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 8.600000 0.700000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 9.040267 0.700000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 9.378933 0.700000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 9.717600 0.700000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 9.853067 0.700000 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore gsave 10.157867 0.700000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 10.327200 0.700000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 10.530400 0.700000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 10.869067 0.700000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 1.855000 8.010000 m 9.055000 8.010000 l s 0 slj n 9.055000 8.410000 m 9.855000 8.010000 l 9.055000 7.610000 l f gsave 4.255000 6.760000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 4.695267 6.760000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 5.101667 6.760000 translate 0.035278 -0.035278 scale start_ol 3264 1792 moveto 1836 1792 lineto 1836 1408 lineto 2880 1408 lineto 2880 1324 lineto 2880 777 2429 381 1803 381 curveto 1455 381 1140 499 938 705 curveto 712 933 576 1315 576 1709 curveto 576 2494 1056 3011 1780 3011 curveto 2302 3011 2678 2757 2772 2339 curveto 3217 2339 lineto 3097 3008 2580 3392 1808 3392 curveto 1397 3392 1065 3290 802 3081 curveto 414 2770 192 2269 192 1687 curveto 192 693 824 0 1732 0 curveto 2188 0 2553 167 2880 524 curveto 2988 0 lineto 3264 0 lineto 3264 1792 lineto end_ol grestore gsave 5.575800 6.760000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 5.745133 6.760000 translate 0.035278 -0.035278 scale start_ol 2816 2381 moveto 2816 3021 2350 3392 1551 3392 curveto 791 3392 320 3030 320 2447 curveto 320 2054 537 1807 979 1697 curveto 1814 1485 lineto 2238 1379 2432 1215 2432 964 curveto 2432 792 2340 615 2203 518 curveto 2076 430 1874 381 1614 381 curveto 1267 381 1029 464 875 649 curveto 757 789 704 943 704 1140 curveto 320 1140 lineto 320 840 377 644 503 465 curveto 721 161 1088 0 1572 0 curveto 1952 0 2262 88 2467 247 curveto 2681 420 2816 707 2816 985 curveto 2816 1382 2558 1674 2101 1793 curveto 1257 2010 lineto 852 2116 704 2239 704 2487 curveto 704 2814 1031 3030 1523 3030 curveto 2105 3030 2432 2798 2432 2381 curveto 2816 2381 lineto end_ol grestore gsave 6.151533 6.760000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 6.524067 6.760000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 6.930467 6.760000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 4.255000 7.560000 translate 0.035278 -0.035278 scale start_ol 2553 0 moveto 3262 2432 lineto 2829 2432 lineto 2350 538 lineto 1875 2432 lineto 1405 2432 lineto 945 538 lineto 452 2432 lineto 28 2432 lineto 728 0 lineto 1161 0 lineto 1627 1908 lineto 2115 0 lineto 2553 0 lineto end_ol grestore gsave 4.695267 7.560000 translate 0.035278 -0.035278 scale start_ol 1055 3392 moveto -37 0 lineto 217 0 lineto 1309 3392 lineto 1055 3392 lineto end_ol grestore gsave 4.864600 7.560000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 5.033933 7.560000 translate 0.035278 -0.035278 scale start_ol 2139 1595 moveto 2121 1818 2070 1963 1979 2090 curveto 1814 2305 1527 2432 1193 2432 curveto 548 2432 128 1947 128 1194 curveto 128 463 539 0 1188 0 curveto 1759 0 2120 328 2166 888 curveto 1779 888 lineto 1714 534 1516 357 1189 357 curveto 765 357 512 673 512 1195 curveto 512 1747 761 2075 1180 2075 curveto 1503 2075 1706 1902 1752 1595 curveto 2139 1595 lineto end_ol grestore gsave 5.338733 7.560000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 5.541933 7.560000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 5.880600 7.560000 translate 0.035278 -0.035278 scale start_ol 2304 3392 moveto 1920 3392 lineto 1920 2057 lineto 1758 2302 1498 2432 1173 2432 curveto 541 2432 128 1960 128 1238 curveto 128 472 523 0 1162 0 curveto 1489 0 1716 124 1920 421 curveto 1920 0 lineto 2304 0 lineto 2304 3392 lineto 1228 2070 moveto 1652 2070 1920 1733 1920 1208 curveto 1920 699 1647 362 1232 362 curveto 799 362 512 703 512 1216 curveto 512 1729 799 2070 1228 2070 curveto end_ol grestore gsave 6.219267 7.560000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 6.557933 7.560000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 6.896600 7.560000 translate 0.035278 -0.035278 scale start_ol 1164 2432 moveto 768 2432 lineto 768 3100 lineto 384 3100 lineto 384 2432 lineto 57 2432 lineto 57 2116 lineto 384 2116 lineto 384 378 lineto 384 137 550 0 850 0 curveto 942 0 1035 9 1164 33 curveto 1164 352 lineto 1113 339 1053 339 980 339 curveto 814 339 768 383 768 545 curveto 768 2116 lineto 1164 2116 lineto 1164 2432 lineto end_ol grestore gsave 7.065933 7.560000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 7.201400 7.560000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 7.540067 7.560000 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 7.675533 7.560000 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 9.805000 10.010000 m 2.605000 10.010000 l s 0 slj n 2.605000 9.610000 m 1.805000 10.010000 l 2.605000 10.410000 l f gsave 4.855000 9.610000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 5.193667 9.610000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 5.532333 9.610000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 5.871000 9.610000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 6.040333 9.610000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 6.514467 9.610000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore showpage kamailio-4.0.4/doc/sip/figures/registrar.png0000644000000000000000000007405112223032460017520 0ustar rootroot‰PNG  IHDRÐxò’YÕsBITÛáOà pHYsÐй‹çŸ IDATxœìÝw\÷ÿ8ð $„„°GØC@\à.E¡RT\Hë@qá®UÀQ'h[q¶u£Ö­EÅY+‚ ( "È¥ Ya“ûýq¿Þ7ŸŒã‚„äõüÃÇå÷½ïõ¾Dî•÷ݽ‚¢( M*²ŠH$@IQ(Å €òuuuccã‘#GnÙ²¥¸¸¸Ó7„tƒ/pкìðŒ¢(Š¢\.7333,,¬¶¶ÖÙÙùòåËdÖ… =¸h(' ¥K¿ü]³9‘[yñâÅ·ß~çääÔÕ;¥2À"ܸqcذaÆ »uëÿ»EEEsæÌ111¡ÓéãeeeK—.e2™l6{Ù²eõõõÈ#øù¬&ÿpÁæ(JKKKHHˆ™™™ššZÏž=<(Q_ Ñ ÅUÆÔÖÖhkkkhhŒ=:==»âVüüùó?þheeE£Ñ´µµ¿ýöÛ›7oâk=|øpÈ!êêêÖÖÖÇ—¨§€n @)|ùcbb ¯]»VWWWWWwõêUCCø¸8ìÝ¢¢"33³ß~ûÃá466>~üxâĉøºfff'Ožlhh(..ö÷÷_¾|¹¸Íá%Ä›CdÞ¼y;vì())innNLLìÙ³ç‘#G$êTff¦­­m‚$®}ú¤££ƒ-p8333á¯\¹’““ƒ¯XTTôå›ë×®]swwï@Ä•9žÇ`/ñš+ÚÚÚÞ¸q£©©)+++111<<üêÕ«§OŸFDWW7;;[WW÷ ú è¦`„AÆ »{÷.É­[·† †-3æÌ™3"WljjRSSÃ_ž>~õêÕ†††W®\ÁëH$AeA¦M›vüøñšššêêê#GްÙlü.‚GŒqþüù’’’ÖÖÖòòòŸþ¿B633ÓÂÂâÒ¥KØ ;÷ïß÷öö÷1ä $@Içßׯ_2dƒÁ`0C† ¹qãÿº3gÎ400 ÑhÎÎΗ.]ÂÊ?~üèëëËd2uuuçÍ›‡]€½eii‰Ío@fs’&555CCC77·ÐÐÐââbþ:IPAêêꀀ===&“éååõòåK2[‰õõõÕÓÓ£R©ÖÖÖÁÁÁ555øŠ/^¼ðööf±X cĈ{ ¿`®@H\郄R ¤H$:H8 up@ê á€ÔA©ƒ„R ¤HUÖþO[[[rrrEEEIII~~~UUUEEEYYY}}}mmmUUUkkksssSS…B¡P(******سU±'ÄêèèhjjZXXëëë;88XYY™™™©¨À¯ €,ÁÓbE333“““_½z•žžþþýûœœilˆJ¥:99õïß¿wïÞ...ƒ ÒÓӓƆ@H8èj‰‰‰?¾qãFBBÇãKEEÅÁÁÁÔÔ´Gæææ:::ffffff ƒÅbéêêR©T¦®®Ž¢(Š¢<EÑÚÚÚ†††æææòòòªª*.—[XXXTTôñãǼ¼¼?òo…B¡ôîÝÛËËËÍÍÍÛÛ[]]½kw@AÂ@)--ˆˆ8zôhuu5^hmmíááѳgϾ}ûöêÕËÖÖV›niiIKKËÎÎ~þüy||ü‹/ðD‡B¡|õÕW .üî»ïX,–4¶$HÇûûï¿÷ïß•¨««ûúúŽ?~Ô¨QFFF2 )99ùÉ“'ׯ_OHHhkkC„Á`¬Y³fÍš5v¤¤(!!aþüùÙÙÙ‚°X¬¹sç~ûí·&L P(²íÿ«­­=þü7nÞ¼‰ ˆªªêŠ+vîÜ©¦¦&ëÐ ¤EÑ_~ùeýúõ(ŠZYY­[·nÞ¼yt:]Öq‰UYY¢h=É:œv (zõêU[[ÛßÿF£ùøøÈ:"€BQA„@÷&ëÿ&”F¥R8`hhèíí}íÚµ––YÇ%¨ººz×®]ººº¾¾¾%%%ýúõ{ñâEÿþýe@¡ÀÒ¢¢¢²~ýúœœœE‹Ñh´;wîLžlgg§¦¦fooìØ1¼œB¡œ={vÈ!, ¿3ðäÉ“¶¶¶XåÇ‹ ?³@¡PÎ;çâ⢮®®­­ýý÷ßüøQ¢ÞDHÐ)‡³lÙ2&“idd´dÉ.—‹G%0oÿV»¢Ã½`±X¡¡¡ø-Å&®2­½½½ššš­­íÉ“'ù7*n­7oÞx{{kkkÓéô^¸pOŠûæPXøÏnT^ØÚÚš=qâD¬äîÝ»ºººW¯^mllLKK³³³»víöÖÍ›7ÍÍÍÿý÷ßÆÆÆììì3f`å§OŸ¶²²JHHhjjzúô©••ÕÙ³gñ 9::>yò¤¹¹+‰ŠŠ²³³KJJÂ*c#ÉÂò‰ H¿~ýâã㛚š8μyóðhÅ­"€ BqBQÔÜÜüäÉ“ %%%sæÌY¶l™¸­à%Ä»âKzÁápôõõ;qe[[ÛÄÄĦ¦¦ÄÄÄ=zDEEµ»V¯^½ÅbÅÆÆÖÕÕ¡(ÚÜÜÌ™KÇ|I/?~<`À€ÄֱެqçΛ;wî 0`ÇŽXy§sÈ”ÄÎÑÑ1%%EQWW׸¸8‘)ÌW_}uóæMáòÞ½{yçÎþó•;vJ¥Ýø‰Ë "×)ÖÐЀ¿¼wïÞ8…B7V!Ñ® ß‹ººº¾}ûž?¾±TFÄŸR!X‹_~~>“ÉÄ– ¾9âº)ntgr6?át.ÉŽððð7¢(zÿþýAƒ=yò¤±±±®®îþýûÞÞÞX[·nYZZÆÄÄ455åææÎš5 +?}út=ž>}ÚÜÜœ˜˜hccÃ¥¤À†._¾Œ]4ŠWî‚„ƒ Bq8p`HHHeee}}}tt´‰‰ Þ¸……Ńøëø[íŠv{ÑÔÔTXXxêÔ©^½z-^¼¯ Ql•Á?‹¤¤¤=z\¹r¥Ýµ¼½½•u,›,²¸¸ØÝÝ}æÌ™k¡Ó£’GµPmjjzöìÙ×_ýÍ7ߤ¤¤tJ³Ò  ‚ܹs§OŸ>Øã Ž9‚¶µµ­_¿ÞÈȈÉdΘ1ƒËåbå eïÞ½–––T*U¸©S§NÍ™3göìÙØ|ÿ˜†††9sæhhhÿúë¯ø˜Á&:dmm­¦¦æââòêÕ+ä¿ßµ EVƒÚÚÚkÖ¬ÉÌÌ$ˆ\\Oñá]-Ü/=\PPàã㣩©©®®>vìØòòr‘Õ:]ccãÂ… µµµµµµ-ZÔØØˆ•·´´¬ZµÊÐÐPWW7""+¤È®7.ò£IEEÅÔÔtùòå[·nݶm›DaˆÛ¥‚ìÚµ‹Ífkhh455×'ÿ@™={vhhhmmí£Gž={††……=þ<55•ÃáÐéôµk×âõ“““SSS[[[ÚijjºtéÒ¬Y³üýý¯\¹‚ÿ½ ùüùsaaá«W¯=z„×'ØDLLL|||UU•Ï‚ ÿ~cƒ ÒÙ í¨®®þå—_°Ñ{q‘‹ë)NxW‹ìÿž0aB```yyyyy¹££ãÊ•+EVët7nüðáCnnnNNNaaá¦M›°òÐÐÐׯ_§¦¦æçç¿{÷+$ÁG&®qDÔGOì»ï¾KHH( ‚]úèÑ£W¯^”––nÞ¼™¸¾ÿqP…xÐPTrúå”Ó°ù)êÃÛ„?¼ÄÒÒrïÞ½EEEüïZYY½~ý[.++c³ÙøZ>|¹‰Ë—/{xx`Ëß~ûí¥K—°e33³·oßb˹¹¹øv 6QQQ-s¹\*•*® ]€ÿØfjjZPP€Š\\O‰wµ@¿ö0—Ë544l·Zï[SSÓœœl9++ËÔÔ[677ÏÎÎ&hJ H‘[׸¸ž Ô––‘Õ ¨–››‹-ggg›™™×'ÿЭÉé—SNÃæ§¨ …BikkÃ_¶¶¶ª¨¨`ËÉÉÉ&L000°°°¸zõ*VH¥RUUUUUUUTTÿ.»C ÿv?þäÉ“Øò™3gƇ-«ªª¶¶¶bË---x $7!|ØîJØFy<^nn®››[tt4*>rq=ÅDîjqýÅ<}úÔÍÍMCCûÏEæƒèp7ù ô?¨«ªª¶´´T&$þR\ãÄ»Bdá‡ð €dÕDF%®>ùÿ8p€nMN¿œr6?EM8ÌÌÌø˜fee™›› Ô¹}û¶±±1¶laañþý{ávÄ}¾Gàb*•ZVV†¢¨©©©Èßý$7ÑLQQ‘‰‰Imm­¸ÈÅõT8rþ]M|”577¿páÂçÏŸy<^uuµ”ö†È‘?÷EŽp ’„CdãH880yòd‰Â ¨ÆþD\}\»ÿqàŽ®wx s3gÎ ÊÏÏokkËÏÏ œ5kö–ŸŸ_fffss3Çkkkà —,Y²pá·oß¶¶¶fdd̘1ƒ¸ý³gÏΜ9“ÿììÙ³Ï;‡ ÈôéÓƒƒƒ+**>~üȾ\ÒMèèèdggw||1KKËáÇŸ;wN\äâz髉ûÕÐР¦¦Æ`0 -Z$~‰äçç·råÊ?–——úùùaåsæÌY¾|yqqqUUU`` qâº&®qòx<^iiéÁƒCCCCBB$ ƒ`—a×jâw$‰«/Á´[þ“(ißYíËKœÝJ7ür’!§aóSÔަ¦¦Ÿ~úÉÌÌLUUÕÌÌlݺuÍÍÍØ[çÏŸwpp R©}úôù矰¶¶¶°°0+++6`À€Ë—/cåâ>ßþýûÇÆÆò—ÄÅÅõïßEQ.—ëïïÏ`0ŒŒŒÂÃÃi4šD›À_nß¾ÅbuñL`swïÞ8p ¸ÈÅõoDä®è—À£££mmm©TªÍü!®Ú—w“Š¢ ,ÐÒÒÒÒÒZ°`ACCV³¹¹900POOOWWwÏž=ÄAŠëš¸ÆÅ}ô¡ªªªš˜˜L:õåË—ø[$à إ»víÂn0™;w.•¸úäÿãPPÅ~y ïk‘“„È|æNWRR²{÷î7n¼{÷ŽÉd>|åÊ•žžžä[ ¹Oo×uq_ÎnNNÃæºeË–ÐÐP,ó+++ËÛÛ;??_ÖHòô´KÙO©Ü¿ßÅÅ…ÉdÞ¼y³®®.77wΜ9+V¬øé§Ÿd@ѬX±¢²²²¬¬lÍš5>>>²GŠ”§§€¼Ž'íÎõ1}úôÚÚZ¬œB¡DDDXXX`¬¶Ûÿ)"g ¨ƒ-äççOœ8›–dôèÑeeex‘“¨øùù]¹reûöí4ÍÀÀ`Ú´iÉÉÉñññ'Ož$Œðl*âv‚Hü¿,Y²ï Àkhh˜={¶ðD:ü&Õt½{÷¶··×ÒÒÚºu«¬Ã‘"åé) ¯ã ÇìÙ³·lÙ"<×GJJ 6׃ÁàŸÁæùóçiii<L;ü=z”žžžŸŸ_ZZJ<º;qâÄàààòòr‡Ó»wïàà`ü-l•ÏŸ?óO¢äææ–””dgg§©©¹uëV …Âb±öîÝ»gÏ2ÁðŸ`kw'Û´i>ñK~~>>ÅŠÀ ©©©)**7‘¿vw)@&–-[ÆápjkkÏž=‹MWTÊÓS TünÄ…û÷ïžëãÍ›7Ø2ÉIrD¶ƒðÂEÞŸÓnœ\.×ÀÀ¯ rsssl»}ûöÝ¿]]–d (ÚÜܬ¦¦F2v‚pœ¿ðßÅ¿ÇÌÌÌòòò°eþÛÌDî‘»TN‰ûrvsr6?E½h +D ‡ŠŠ >û†Ì$9üÙ ™¹Yˆg€AÄÌ@ò?}ø¯²¤“®Ðh4læUUU.—‹¢(6ß;Š¢ÙÙÙVVV$ƒh_¢@râqéˆì—È]*§äôÈ-§aóƒ„йˆN©˜››¿~ýš¿$==ÝÒÒ[¼téR¬ÐÄĤ¸¸o]øŠ0‘íð+((ÀŒ šš2eʲeËJJJx<^MM ÚÞ=†††ïß¿G¤wïÞ§OŸnhhÀ®ÛÈËË[°`ÈS!d‚‘h'°Ùl¼Íüü|6›-®ZQQ¶\XXHܯvw)ÐňŽ¥K—Ο??11±©©©©©)))iÁ‚K–,ÁÞ9sfvv66×þÔœ%K–,Z´(//¯µµ5==}úôéíF ²~ÁÁÁؼ(AAAüÏD¾p²¡¡A]]]]]½  €ÌÓn<==ÏŸ? ÈáÇýõWCCC‡Ãd2gÍšµlÙ2‘‡j‘ÁèêêfeeáuÈì<øiÓ¦‘™øeúôé«V­"˜H‡_»»€ÌÕ××—v6×§œAÅÿòx¼ýû÷÷íÛ—N§ÓétìB‡½{öìY{{{*•êäät÷î]¬®¾}û^¼x+Ù>A;ßY ‘3ÔÁHÎ ƒ¿ÌÌÌÔÓÓKJJÉ`ÂÃÃùgS!³ðåúúú€€MMMMMM‚‰_¸\îÌ™3 ›Í Ã'Ò§È]*§Ä}9»99 ›œRð…f•—Ë-騪ª*Y‡ßÕ>Üá/Õ¢E‹d¾Äˆ&þêÚ,‹ÇãÑh4|:UIEEE-Z´(((ÈÏÏÏÒÒ²¬¬ìòåËOž<¹råJ‚éJ5‘NwþrÓ°ùÁÄ_¾äÑrýMÈ‘#G/^ܱu-Zô%`ytæÌqs>Õ××WWW3™Lmmm‘üýýùåiF×ù¨íWéÞÒÒÒˆ/ì æëëÛ·oß]»v¹»»s8]]ÝáÇ㧺•àààÍ›7777¯Y³æ»ï¾“u8(‚¤AòËÎÂd2MMME¾U___UUÅd2uttDVwdU`þþþþþþ"ß:zôè¢E‹fΜ©Hs)É}ÂáêêºsçÎ/iÁÞÞþرcD;lp IDATôØÙÙõêÕ«¾¾ÞÇÇ'44TÖဠ‚#èñãÇ,Xàçç'o4t÷„£Ý ] "ë_0Ë–-[¶l™ €Ã&M&së"@tØLÙŸ¥  (äHR#QÈü R èê„CÒ[ËÄÕÿ’[Ôt1H8$#ÊN¢,R"ÀA”:á4'(ÙüÂÌ´¤¤dåÊ•öööt:]WW×ÛÛûþýûu(ÿ×Ƚ{÷¼¼¼ †ÁìÙ³+**„ëܺukäÈ‘êêêúúúÓ§O/))×ZEE………Á˜?¼Eѵk×êêêêéé­_¿ž·¼|ùr̘1,ËÂÂ{Šp}qÁä‹Bþd—…Ü]Jpt[÷ïßwqqa2™7oÞ¬««ËÍÍ3gΊ+æ¤Ã&‹%hg÷îÝÁÁÁoÞ¼ÑÑÑù –={ö¬ZµŠÃá:::N™2E\k¡¡¡ÄsòOa‹9räñãÇ™™™¯_¿Ž‹‹;~ü8Vž““ããã³xñâòòò§OŸ&$$ˆ¬O°¹îŒN§#RVV&ë@:ŽÃá ÿu€/#€èY*‚ìÞ½ÛÜܛһµµuݺu††† cÚ´iØãXQÍËË›0a‹Å¢Óé^^^¥¥¥Xyssspp°ŽŽÎîÝ»ñ6ÿüóOì9#ÎÎÎ/_¾$nA;wb/™7o^cc#^Ž-ˆ‹ŠŸÈÆv‚€Û·o;99Ñh4++«Ã‡ lT\TuÚíšÈ]‘ŸŸo``ðèÑ#jkk‡)ü1 Ç/ŒËåÒétâ:õõõêêê"ßzóæµµu}}="”X‡1|øð˜˜l9&&ÆÕÕ[öóó;yòd»õÅ}@ÝܤI“Ù³g¬é ÆÆF===AÒÒÒd‹ÓoiÞñÝwßÉ:ù‰ ÈܹseHgjg„ãùóçiiiXN–’’’ššÊáp þôö‰'———s8œÞ½{cå¡¡¡ééé©©©ùùùø£Õ‰‰‰‰ÿüù³þLWq òèÑ£ôôôüüüÒÒRáÇ:ˆ‹ŠŸÈÆÑÿ=X ˜={ö–-[jkk=zôìÙ3á ÄQµ»u‚]äææ–””dgg§©©¹uëV …Âb±öîÝ»gÏ‚m¸wïÞðáà *p¹Ü}ûö5J仫W¯ e0-èëë«©©988„‡‡ãO·ÉÈÈ4h¶|055ÕÐИ:uêçÏŸEÖ'Ý¿î{Dð† nݺ%ëX$V__?iÒ¤ÊÊJ{{ûÈ:  `„´3ÂñáÃü¥••Õ›7o°å²²26›-¼ —Ë500À–ÍÍͳ²²„Û¬¨¨À+S©TâFÉÍÍÅ–³³³ÍÍÍñròQ4.®š……Åþýû‹ŠŠ‚'ŽJde‚­‹ÜæææØv±ÇóÖÕÕaIŠ¢ÍÍÍjjjd6$ --ÍÖÖ6;;[\ì;`dd„÷‹ßýû÷ ÐÖÖÖîæZ[[_¾|éáá„•¨¨¨`+¢(ÚÖÖ¦ªªŠ-«ªªÎ;·¢¢âÓ§O³fÍš6mšÈú⾜Ý\[[ÛÒ¥KQQQY¿~}ss³¬#"«  `ðàÁ‚°Ùì/^È:ù §ßÒ.víÚ5A&Mš$ë@ävYÛœ9sdHgj'áàI¥þÏ<èØyEŸ>}êææ¦¡¡!P®ªªÚÒÒBÜ&þR\#‚´¶¶bË---øQ_Q\Tü·_’““'L˜````aaqõêUú⢒´k"ëÓh4l¿©ªªr¹\E¹\.önvv¶•••¸ ‰gmmýüùsâj555Û¶msss~«ÿþ÷ïß'¹9EKKKutt°e--­êêjl¹ªªŠ¿¼²²[þôé‹ÅY_~ÿ”óx¼5kÖ`ñ›ššFFF ÿwèVòóóg̘]ªfaaAžòû-íJׯ_Gdâĉ²D>({ÂaaaQ\\,\ÍÜÜüÂ… UUU<¯¦¦_ËÜÜ\øo–¸£¬¸FøÇrrr„G8ÄEE&B2#nß¾mll,P_\T’vMd}SSÓüü|EûôéóçŸÖ××ÿþû}ûÖÍÍí?þ·!‘.\¸`ffÖn¶©¯¯y"„¸‡£¥¥…-‹»†ã믿æO8455EÖ—÷?åÿüó³³3Ö 6›½bÅŠ¤¤$Yõ?>þ|ôèQwwwlÄ[]]}õêÕŸ>}’u\òDÞ¿¥]ãÆ‚Œ?^ÖȇS§N!2{ölYÒ™$H8vìØáííýöíÛ–––W¯^ácàúúú×®]kllÌËË›:u*¾Öƽ¼¼Þ¿_UUµjÕ*‘mâ/Å5‚}A± ¼½½×®]+°¢¸¨ø‰k\WW?#`ÆŒYYYMMM7oÞ>#.*:ívM䮘={öŽ;PMHHèÑ£‡††ÆæÍ›™LæðáÃ/\¸ ªðg‡—ìÞ½ÛÒÒ233“`­™3gfdd´´´|øðaåÊ•îîî-„=}úôôôô–––¼¼¼ñãÇ`åþùçðáÃ?|øPRR2lذ£GbåǨ¬¬¬¬¬œ;w®ŸŸŸÈú ð§œÇã9s¦wïÞxºfoo¿|ùòË—/þüY&!µ¶¶&''ÿöÛo'NÄò ATTTüýý9ŽLB’kû–âß‹5jÔ¨ŒŒ ráü>--ÍÛÛ[KKK]]ÝÍÍMx𵸸øûï¿×ÑÑ¡Óé®®®ÑÑÑ"DÄÜoIØ®gòööî”Öž²'mmmaaaØ]}ûö½xñ"VmkkK¥Rmllþøã|­æææÀÀ@===]]]ür}q‡+q ²k×.ì~¹sç644¬(.*~âg±X"û~öìY{{{*•êäät÷î]Š‹ •ptMä®ÈÌÌÔÓÓ#ó;XÜ_%á?¸ÚÚZ:—.]êׯ•J522š3gÿñ†Ì·B¸33³àààúúz¬;³ £££££³víZ‡¯»yóf&“9eÊ|´C ~Çþ”wO)))+W®455åÿDzõêõÃ?ìÛ·ïÞ½{ïß¿—Ò¦›ššÒÒÒÎ;·víÚÑ£G«©©á¨ªªzzzFFFâg²€¤:ö-ÅW©ªªÚ¶mۀʼ~ýšÍfŸÙ."ôÔì¨SYYi`` PAà€tåʕ͛7»ºº644x{{ÿú믧ðÄ К¸À¾Ìà áèˆv³ÎÆÆÆ®‰(55µÑ£G·[ ÛPSS#žŠ tsØ‰í¼¼¼€€€ôôtkkk¼\¸²žž^EE›ÍÆ+'‘fffØÓÊÊÊÂÃçNO€D} H8$¢#ð,äššš¶¶6d €B¡ØÙÙ9sfñâÅuuu5GŽM²Yccã°°°äää.Œ$H8$ c–––Ç?wîAsçÎÕÔÔ477?|øP¸Îøñã>|ØØØXQQ±sçÎ/¿ü‚L`$AÂ!á ‹-:rä¶Lù_Xa¿~ý®_¿~âÄ SSS]]ÝlîN~K—. ÑÑѱµµ}ñâÅùóç‰7*rC} …<‚‰À5 ‡ÞÑ£Gc—ï’‡zÿþ}‚¦Æ7nÜ8’[$¸€Cd`_F8$¢ùŒp:H8$¤‰ÀÐ y•…Ü]ÿw Ì @J`„À©ƒ„C" ;¡`]ÐÝ@Â`„9'ë(€òòðð ù „„C" 9 rlË–-[¶l‘u@yÅÅÅyxxI;ò $ ò*ެcJLÚ#QÈü ä>¶ƒ ; N; áp —ø6`tâÒH8$#€îB`T9@·"œv@ á@þiÀ è†øÓ…üÉ.= ¹»ài±È‘ã[¶lqwwïòXÀÿ“5‹ƒeÃÆ C`„C¹ÁrFÜ` r€î’r„ä Áåp%‡ ¡J‰Ìžqww½té ‡rƒ„yB<Œƒ [ÁRØØXwww¸hT"0±vÇ0`tü©V¢GP H8d0`È–pª‰(d~ rƒäè r™—j` áp È]À èbÄ©‰À@f$·€AÐ5ȤH8Lü€|ˆ.TÈŸA@ŽˆüZŠßU‰(äî‚R#R ‡D`„èH8$¤‰ÀÐ y„€ÔÁ‡D2?ƒ„€ÔA á u%%%%S¦LÑÕÕUWWÿúë¯oܸ•c¿û¥AJ- 4K~+0Ât„DGÐiÓ¦õìÙóíÛ·ÕÕÕ[·nýã?¤]Q°BRp: …B¡PP%sÐMNNÞ´i“¾¾>Nÿæ›oîܹƒü—²`í`Õ.\¨­­­­­½hÑ¢ÆÆF|[{÷îµ´´¤R©‚´µµ­_¿ÞÈȈÉdΘ1ƒËå’ X\ã---«V­244ÔÕÕˆˆÀ |||455ÕÕÕÇŽ[^^.2`2‘:tÈÚÚzâĉ‚ÔÔÔ U^@ +?«2pàÀŸþ¹  €¿ËTøS–7~øð!777''§°°pÓ¦MxåäääÔÔÔÖÖVAž?žššÊápètúÚµkÉD+®ñÐÐÐׯ_§¦¦æçç¿{÷+œ0aB```yyyyy¹££ãÊ•+EÜnã‚ÄÄÄÄÇÇ_¹rAׯ_“ Un ¹ÿ‹»øH¢Ñh‚477·[³¸¸8 ÀÄÄDGGgÆŒ%%%X¹À~655ÍÉÉÁ–³²²LMMñj>|À«YYY½~ý[.++c³ÙÂ[þÅ5nnnžM<—Ë544Ù,þ’ òŠŠ EïÞ½‹ >&¤(¨rŸR@®)ä•er>’ÔÕÕ›ššét:ÉUÊÊÊÂÃÃSRRâãã‘ÿÀø»T*µ©©IUUAÖÖVƒÑÒÒ"\F£¡ÿìy<…BeX… q*•ÚØØˆ¬Á%&&þôÓO©©©Øù|Íâ/ÛüŸþ3f ¢Xß+8¥ +t 3366 KNNù.›ÍÆO»äçç³Ùl‘ÕLLL [[[ÛÚÚ°œƒÌ¦Å5nbb’ŸŸ/PyÊ”)Ë–-+..æñxÕÕÕíö±ÝÈ¥w?Ž A +¿†cüøñ>lll¬¨¨Ø¹s瀰rììl¼šŸŸßÊ•+?~üX^^èçç'²µ%K–,\¸ðíÛ·­­­3fÌ ­¸ÆçÌ™³|ùòââ⪪ªÀÀ@¬°¡¡AMMÁ`.Z´oD `I#W428è$ð¿¸;€O$‹… Hmmm»5oÞ¼éææF§Óµ´´Æÿöí[¬|ûöíX#ØË††† hiiiii-X° ¡¡+ø8ÚÚÚ¬¬¬h4Ú€._¾,¼Eá#£¸Æ›››õôôtuu÷ìÙƒFGGÛÚÚR©Tì&^‘“üÞ½{Š÷½‚k8cpõ@wŸIÚÚÚ555ÕÕÕZZZ²Ž¥»û÷ß½¼¼¾ýö[<óPpJ(”ÇÏœ9ÓÈÈH]]ÝÙÙùìÙ³­~ðàÁ>}úÐét===Ÿ˜˜¬œ"¾î‹/üüüLMMÕÔÔLLL¦OŸþâÅ þÆù×ÒÔÔ:tè‰'ÄU¹ äÌnNžBf±p…2bĈòòò7nÔÖÖFFFFDD;vŒäºaaa—.]:sæL]]]bb"N÷ôôÄÞâ~‰ Èùóç½¼¼†š˜˜ÈårŸ={öõ×_3æüùóü›À×*))ùùçŸÃÃÃ÷íÛ'²‚ð&w yèäS4ÈÔºuëx<þòÍ›7¶¶¶$×555-,,ä/Ù²e‹p5áÿ5ÙÙÙéééåøÝöÂ+>{öŒ?¼ü„ÿÅÝ| $éëë#ÿÍ3ˆÝ¿AQ£FÉ:Î#@¡„‡‡óŸƒ°´´|ÿþ=Éu?~ühllÌ_²yóf2+îÝ»÷ÇìÓ§@¹££ãòåË÷îÝ+nÅ~ýú‘ y§T”$@‘ݸq£wïÞ$+»¸¸œ>}º[‰‰‰ùþûïE¾õÝwßa¿TDzõê•‘‘Q¶€<‚„ƒ<…<ýDm¿ òéÓ§O7ntè~ág»ú÷“³aÃô¿iFŽYRRÒ‰Qñߥ2fÌ‹µ|ùrþ "O|vbÈBþj'ÏÃÃ#..Žde…ÜWpT\\ìååµiÓ&òÙ†F£?~Û¶m/^,))qvvž?~»kYZZæåå‰|+??߉çUUU/^ÌËË“ôÆ]ä—’pÄÅÅyxxH”v(H8€¢ùðჷ·wDD„——×—´C£Ñ¶oßNæOƒ§§çßÿ-ò­¿ÿþ[dÒ£­­=uêÔ?ÿüsëÖ­ ö#q”<áÀL;`„€îŽÃáŒ3fçÎ’Žm ²|ùòÖÖVÖ°ùˆ8pàõë×å™™™ ·âôéÓ544.]º$i¨È#H8pÊ9Ú P(cƌٰaƒ···¸ w~]K8^¿~-Åçƒucdv‘»»{lllll쀤üiÈœRÐòW{gÁ¯ÞÀ^*侂„@W€ÛbEH5$º$R á:œòŒjðƒ„@WPÈ_í’"™j(侂„@WPòåÕà €® ä Gll,ùÊ 9Âópè JžpH8tH8ȃ ƒò ȃ„@W€ò29ƒ„@W€„CÉA +@ÂAŒp ‡’ƒ„@WPÈ_íR¢û ]F8ȃ„è H8”$º$äÁ¸wïž——ƒÁÐ××÷÷÷///ÇÊQýé§ŸtuuõôôÖ¯_ŸÄ• ¸}ûvß¾}étº““ÓÍ›7ñrìh‡IOOïÕ«W§ô% tw»wﮨ¨xóæ¶¶¶ŸŸV~äÈ‘øøøÌÌÌׯ_ÇÅÅ?~œ¸œ_bbâ‚ öïß_SSsàÀE‹%%% ÔyôèÑäÉ“ÿúë¯Né…Bþj—ÅÜW(@nÁÿâî ‹?.—K§Ó±åáÇÇÄÄ`Ë111®®®Äåü&MštâÄ üåñãÇ}||°e¬;QQQvvv™™™¹‚ W¯^í¬XJJ ‚ ...²¤3ÁÈ“ÿýwøðáØrFFÆ Aƒ°åfdd—óKHH7nþrܸq øËÇoذáþýû½{÷î¬Èá” y 9ÂA•uÈJIIYµjÕÝ»w±—uuu, [ÖÔÔ¬­­%.ç÷ùóg}}}ü¥ÁçÏŸñ—?üðÃÇ­¬¬:1xH8”Œp€|ˆýî»ï.^¼hgg‡•°X¬ºº:l¹¶¶VSS“¸œŸ®®î§OŸð—Ÿ>}ÒÕÕÅ_=ztÚ´i¯_¿îÄø!á O!G8 á@ŽagFeè /^œ5kVTTÔÀñB''§çÏŸcË)))NNNÄåü¾úê«Û·oã/oݺõÕW_á/vîÜ9jÔ(ìb‚N¡Q@œR€î.""bß¾}ÿþû¯À³gÏÞ´iÓßÿ¢èÆçÏŸO\ÎoíÚµß}÷•••««kBB† ¢¢¢ø+Ìš5KMMmôèÑׯ_wuuýò^Ày ™œAÂÝݪU«qttÄKjkkY,ÖâÅ‹óòò°òÅ‹㉅¸r~_}õÕ‘#G–/_ž““cggwøðaüZTÜ´iÓÔÔÔ&L˜PYYùå½€„CÉQ,€.¦?F¥aöìÙýõ×éÓ§gÍš%ëXº»W¯^õïß¿_¿~/_¾”u,®áÐ`„CÉA +ÀPy ¹¯ á€.çáá!ë(dF8”\4 ]$..nË–-qqq²D6 á O!G8 á©SòT ‡’ƒ„¤R œBþj—…ÜWpÐ¥üüürsseEûÜÝÝûí7YG!ß Õ#JºTbbbYY™¹¹¹¬!RPP`aa!ë(ä¤"AÂAŒp:••U7<ð÷÷—uò R p(9H8 Hšj`?a•$d(äÌàë(ØA tww÷ØØØØØXwww2õQå³råJF8ÈD$M;” \áä á©©©?üðƒŽŽŽ¤'})|455‡zâÄ qøñ×yóæÍÂ… íìì ƒÁpppˆço„¿þÁƒûôéC§Óõôô|||bbbˆ·…­NI» [´C$H8ÈSȸhTÌš5kÊ”) }úô‘t]üûZSSX[[(\A¤‹/®_¿~íÚµ›6m255­©©yðàÁöíÛ###E®v÷îÝ3gÎ899„„„xzzb5ùëS(áÕ‰#i·#òëÖ­[ÕÕÕ²ÁÌÌläÈ‘²Añ¸»»»»»Ã}+8H8”$r ##ãËÑÒÒòöö622š>}:ÉãtVVÖÚµkÌḬ̀}}ý)S¦øúú®^½Zä*¿ÿþû“'O¬¬¬éÙ³çÅ‹œœ¾}Zò¸$&QGè&”<퀄ƒ<…á€S*Ê¢®®.!!aÅŠëÖ­ã/y!*ö-/..¶´´”h+‡öññ‰ŒŒôôô´··2dˆƒƒÉu "á'®#È ì§Ç_ IDAT$‹¬£H8”Œp(8þ›;ÆŒÃb±–/_Î_AäíòüïŠlMÜý2ýû÷ÏÉÉÙ°aŠ¢7oÞtss9rdII ™P‰#i·#€nòr„‡¹«ªª.^¼˜——wöìY’ëš›› \'!œ£ÑhãÇß¶mÛÅ‹KJJœçÏŸßñmºt yäÁ)e¡­­=uêÔ¶¶¶ÐÐÐ3f™ÒÃÝÝýÆäω£ÑhÛ·o700èp Â:Ð@w#ä)dr¦ì#Êv¸š>}º††Æ¥K—ÈT ܽ{w~~>ùö—/_ÞÚÚÊ_Âápôõõ%‹’‰:è áPr$›¿ð@^\\dooO§ÓuuuÇŽûÏ?ÿHÔ¾òd’~.¡¡¡¡¡¡dþÏ÷éÓgÇŽ®®®{öìÉÍÍmjjjii)((øý÷ßÅ­rðàÁÁƒß¼y“Ëå666ÆÇÇO›6mÅŠä#$I¢ŽºH8ÈSö‰zþ%»éÞ½{... ãúõëuuu¹¹¹sçÎ  ê”öåŽÈ)À;ÜÚĉ544Μ9#Ð¸È BnݺõüùsMMMqãÆ½zõ*11QdãOžräVÈÿ`‹]»v±Ùl €€€¦¦&áðÊ>>>šššêêêcÇŽ-//Ç+:tÈÚÚZMMÍÅÅåÕ«Wxý™3gFEE…‡‡;::Òh4ƒiÓ¦%'''''?~œL0üÇf¬Z[[Ûúõ댌˜LæŒ3¸\.ÁNill\¸p¡¶¶¶¶¶ö¢E‹ñmíÝ»×ÒÒ’J¥"ÒÐÐ0gÎ ccã_ýUzc*ÝH"°¢ÈòçÏŸÏž=[\Ëk¹¸¸œ={¶¸¸¸¹¹™Ëåfff>|xèС"·2|øðãǶ´´ÔÔÔ$$$Ì;—L`Ä‘´Û@÷§¿Ú¥D!÷•è„cöìÙ¡¡¡µµµ=zöì™p…G½zõª   ´´ŸJRäw„ ååååååŽŽŽØã‰1111ñññUUU>>> ,À wîÜôõ×_'%%ÙÛÛkiiíØ±ƒB¡°X¬={öìÛ·L0èïÀ?­°°°çÏŸ§¦¦r8:Nü›{ãÆ>|ÈÍÍÍÉÉ),,Ü´iþVrrrjj*v™BHHÈçÏŸ _½zõèÑ#‚À‡’p0™Ì’’‡ciiyôèQá ûöíc³ÙFFFûöí#¾;ñõë× CKKkÛ¶m÷îÝÃß:tè¹¹9“É\½zuZZVxçÎY³f!²pá   ²²2---ì-—ìììŽsüøñ½{÷š››kjjîܹóÊ•+1_¸païÞ½FFFl6ûÀçÏŸÇßÚ½{7~ÏÅ… öìÙchhhdd$nªoH8ÈS¢Ž+W®ÄÄÄ 8ÐÒÒòÚµkÂlll°…=zp8lYä®ILL1b‹Å¢P(ø[øÍ L&¿µ¡¬¬ÌÔÔAÌÌÌ€€&“‰ÏâPPP`bbB2%%%ýû÷§R©ªªªÆÆÆø™‘8N=°e[[[þÊü”••Y[[cËø‘ áPr¢çá}º……Eiié¥K—E^éÙn0]#++ËÛÛ[¢I9r²±±¡Óé¿ýö^rîܹî0ÂÁƒ¿¿¿‡‡ÇÕ«We’QÈcƒ4>|xÉ’%‹/>tè¬céîÞ½{geeeii)ð@+¹&•©ÍSSS‰/ì æëëûäÉ“üüü‘#Gjhh <8>>¿o¶[Y±bEeeeYYÙš5k„¯t€ƒò2‹•ÊÃÛ\]]wîÜù%-ØÛÛ;v¬³â‘‡Þ½{×××Oœ8qëÖ­²º/…<ˆò:’p´ûuÁ§æì²ýî.[¶lÙ²e2 @êëëkkkkjj„ÿ­©©ihhàñx(ŠbÿòÃJÔÕÕ5ø°X,þ— ÃÎÎNyžtÀÁy ™œÁãé•Quuõ1JKKEÎUß¹tttŒÙl6þ¯±±±¡¡¡³³³ªªª´t=H8”$ ®¦¦æÍ›7ÙÙÙ999ÙÙÙÙÙÙùùùÄ’Á¦…ÕÔÔù/“ÉTQQ¡P(Ø¿ü°’ÆÆÆºº:®¬°©©)77·ªªªªª*++KdÆÆÆØ-Bø¿vvvšššÒÙC€. y0º»¦¦¦{÷îá¹Evv¶È©WY,–©xÒž EÑÊÊJ‡SVV†ý‹/TVV¦¤¤`%ñññük;88ôïßßÅÅÅÅÅÅÑÑB/p(9H8ä[eeåóÿ<{ö¬¸¸X “ÉtàÓ³gO+++üq02A¡Pôõõõõõ…ßåñx%%%¹¹¹oß¾}ûö-¶——‡e!>Ī1Œ¾}ûbɇ³³sß¾}étz×ö …üÕ.% ¹¯ á3\.O/ž?žŸŸÏÿ¤ÑhcÆŒ±¶¶Æ3 sssùºBSEEÅÂÂÂÂÂâ›o¾á/ÿþ}NNγgÏRSSÓÒÒòòò’“““““±w)Š‹‹‹»»»»»ûÈ‘#áü ÝŒp(9H8ä@KKKRRÒƒbbb›››ñ·˜L¦³³ó AƒÅÿ7Z[[cIÆ7ß|ó%SÅ+&“‰'­­­>LHH8þ|VVÖéÓ§OŸ>­®®>jÔ¨I“&M˜0¡Ý‹=ô!$$ÄÊÊÊßß_«©©É<‰üøñ£lÊ@!¢€•••——×*Q(###;;»Þ½{/\¸pÈ!2 #))éÚµk.\(,,ÄJ´´´Æ7yòä±cÇ’íqppX³fÍš5k8ÎÍ›7¯_¿~ÿþýèèèèèèÕ«W?~îܹcÇŽ¥Rÿï?ÅñãÇñKt›››}}}ãã㜜dÔä$Jî‹EwìØÖÐÐÐYIŠ¢‡Ãá$$$;v¬W¯^çÎsvvîš­s8œ¿þú+22233+144œ8q¢¯¯ï¨Q£`‰.Ãf³çÏŸ?þü¶¶¶èèècÇŽýóÏ?QQQQQQl6{æÌ™sçÎíÛ·o[[Û‘#GøW¬ªª7nÜÓ§OMLLd<ò ò`„ãäåå}º¬¬ Aæãã3oÞD$¢¿Ú¥D!÷UG~^§¦¦Ž5ª¶¶vìØ±ïß¿——lC…BYºtiqqqÏž=ÓÓÓ½¼¼jjj:¥å¶¶¶S§N¹ººöîÝû—_~)++sttüõ×_ß¿õêÕ‰'ªÛŸÏ÷ûý|Gͼçs~µ˜··wß¾}Ûö Ð!àèáZp„„„\¿~F£ul´QQQvóæÍÐÐPö ›¼§M›6mÚ´‰'và¨=eee???WW×uëÖMš4IEEEðscccäääÄ]S€.‡àztÀQ[[{éÒ% …²dÉ’ÖÞ£¾¾>??¿¨¨èßÿýðá…B¹wïÞ½{÷‚˜9sæ¾}ûŒŒŒZ{M--­?ÿüÓÃÃÃÓÓ3<<|þüùáááÓ¦Mc2™}ûöÕÒÒÒÒÒjí7N …âæææíí}öìÙ¯A\¼x±µÕ†Îo×®]»víw-º!=œ GZZZ}}½‰‰Iÿþý<…Åb=~ü˜\F‰wís*•ú×_ýðí¨,Áƒß»wÏßßßÓÓóÌ™3d ›¡¡á¢E‹~øá/8{ölooïvNŽh•ÔÔÔôôtñÖ 6 8×£[8Èí?LMM,Ÿœœœ@ÀÐÑpôp‚åååAÒËàîîÞÔÔäååõÛo¿±ç³ººº²X¬C‡ÙÙÙq–g0«W¯Þ¸qc]]Ý„ îܹSQQQ[[wàÀèééùûûÿûï¿ùùùGýöÛokkk/^¼iÓ&®èoƌ۷og2™óçÏgwÇHJJþöÛoîîî«V­újýõõõ ‚dÝ'Áõèr‰î¦¦&þÅÞ¼yóüùsyyy®µ³6nÜXSSãääD.ÆV__?~üøãÇKII…††Þ¿ßÓÓóǃ ¢P(ä’sçÎ%»Kêëë·lÙxþüyIIÉýû÷/\¸ëÿcëÖ­ãÇ///÷ööæLß·oŸ¬¬l|||NNŽ OÚÐÐðµ—Z¡[~ˆ‚à 8È Nù‹ˆˆ bÖ¬Y222ìĘ˜˜K—.ÉÈÈøûûs•÷öö~ò䉄„„®®®•••¤¤$¹÷lVV‹Å’••µ²²"ûAȆ‡ÊÊÊÜÜÜ ܸqC^^þâÅ‹¼«‚P©ÔÀÀÀäädv¢œœÜôéÓÙ5ä€0 …CpÝ28t– ¹+lHHHVVŸbwïÞ%‚kˆÆêÕ«Y,Ö–-[úöíË™þèÑ#??? …Âd2ß½{7uêÔÈÈÈÝ»wŸ:uŠJ¥^»vÍÞÞþáÇ£GÎÎÎ&âàÁƒNNNÿý÷.^¼8mÚ4__ßÉ“'[XX°/kllìáááçç·~ýúG±ÓÉ ½wïÞe0|!66–@ÀÐÑpôp‚d DZcÇ)¬­­Í>ŽŠŠzõꕦ¦æ–-[8Ë0ŒE‹‘á›®®nQQÑÓ§OÝÜÜ.^¼XRR2k֬ɓ'ѯ_¿û÷ï[[[¯^½ÚÓÓóÆ«V­b2™>>>?þøc@@À‚ ^¿~͹Ì×¶mÛŽ;“””ÄŽEÈ•LCCCCCC¿ú8:ÁõèŽï¿ÿžìÔĈ#ØÇd7Êš5k¸v'¹~ýúǵ´´ŠŠŠ X,•J%§·P©Ôëׯs. nnncfföèÑ£yóæ1™LEEÅ/^¨¨¨èèèdffFFFÚÛÛ³Ë+**º»»|HOO§Ñh¼«˜çåå‘‘‘QZZúùóg{{û¢¢"Á¯üóÏ?8055•Á`Ì™3GYY™}MNŽŽŽT*õÉ“'¥¥¥í{h/=œŽÐÐP‹5iÒ¤^½zqe‘ÁAUU•†††´´ôû÷ï.\(ø•}}}cbb‚9rd]]ÝÓ§O ‚øðáW1%%%kkkƒÖ®'€vCÀ!¸nÙÂ!è Ñ6HHH ‚k,'©¾¾ž )))rqYYYÞõ»øpww?þüû÷ï ‚°±±‰ˆˆ G†rqppˆŠŠŠ_°`AÛž@Øz÷î-ö‘F†††â­ôÝòC'Ä€ƒÏ~oä `vvv÷ïß§P(ÁÁÁ‚ìÏÖ§OŸððð±cÇ~þüyÛ¶m䲤œëp°‘wóæMŸ@øš ʺ´p®[gB 8ÈyÞa‰ÿ‚ Nœ8¡  0iÒ¤ØØX+++¯\TTäìì|äÈ‘ÇûøøŒ3†óšœÈ»óŽ'1&“™–––žžž––6{öl´¸[MM ÿÕoÛ€F£ñÎ-çú0tÒØIä²~†€£‡ÖMQQQUU•’’R³Ì:” ˆØØØëׯËÈÈDEEMž<ùçŸöõõýê•+**ÒÓÓÝÝÝŸ>}Z^^žœœL¡P Ä[ØÐÐPJJª°°°¦¦FNN®ýÏ% ÒÒÒ´´4v„ñòåËššvîîÝ»EVèüþþûï~ýúuàß½{7mÚ´fCpN'ÃO†þäòß9¾¼­:Á¡…£ÈæÁƒ7›k``0|øð/^„„„ 0`Ú´i ;vìÐÔÔä¿h]]““Szzº””TII‰½½½»»{}}½­­m³‘ •Jíׯ_fffVV–¹¹y‡<¯ÆÆÆÌÌÌ´ÿ¤§§ççç·T¸_¿~-½,Ð3õîÝûûï¿ïÀ ’èù‹OŽ¿cy§Ñ¹ñ•ëÝ.»·»oïÀ 4 G'¬€#33“h9à ÂÍÍíÅ‹‡ÎÌ̬®®4hЛ7oÖ®];pàÀf»´+**¾|ùB¥RÉÍcŒŒÞ¼ySPPpâÄ ‚ \]][º‘±±qfffff¦ŽÅ‹_¸pAð?¡)S¦£ bEEE‘‘‘ÙÙÙä†PEEgÓI^^^AAaØaß\û&Ã&£Ïž>?LÿA,Õƒž¦[~k’nùZ kZ,p4;€ƒ´dÉO6ü’›Ájii5Û3RZZ:fÌ;;»^½z…‡‡ËÊʾyó†B¡,X°àíÛ·ªªª³gÏnéFä…7n400°U1Ž®«ºº:,,ÌËËËÔÔT[[{ñâÅçÎ{úôinn®¸«Ö¼—/_Êñ ÿ6¥¥¥·ÏÞ>tÕÐßL3Ð1wM¡G@ G'¬ŽÂÂB‚ øì÷F£Ñ>¼|ùòÜÜ\ssó””%%¥ˆˆ===®’555S¦Lyýú5AŽŽŽ111ÁÁÁNNN^^^GŽ!"00PZZº¥‘üôéS‡</YYÙàà`WW×óçϵ0F³¶¶RM@ Æ‹/""""""ž={FÎè&B^^~ܸqöööööövNi¯^½x‡'ûøøÒÒÒ»Vîu CÀ!¸nÙÂ!¬€ƒŒù”Y¶lÙÍ›7oß¾]__offöçŸ6;‡vÉ’%qqq4MAA!55u„ ÏŸ?ÿçŸ6mÚT[[»páB>ÍA›½ñ‰HÚOJJêìÙ³ÊÊÊGå_rܸq222« t”ÜÜ\²Ç$::š½R­¤¤¤¥¥¥½½ý˜1c„úK%,+(èÀŒ«Ä]èpôp 8ÈÕºº:þÅy}^‘¨IDATNœ8abbòúõk===•fË,Y²äÆÕÕÕ555 eãÆÉÉÉ«V­*..611!ÇpðAÖAØóGŽQQQÙ±cŸbt:½wïÞãÇ·µµ0aÂÀ…Z+h•²²²èèh2ÎÈÉÉa§‘-666-ý–v9,+8ø÷ â>ØÛû*+w“ç‚Î ‡àÐÂÑ ä<»ý¹%ššš¯_¿ž:uj||¼………§§çÆ•””8ËL:õĉ®®®,kÏž=‰‰‰‹/nhh°±± ùêt>Ñ$___UUU//¯fK(JSSÓ—/_®]»víÚ5‚ tuu'ü‡·/ D ¡¡áéÓ§‘‘‘IIIìõ0TUU'L˜ÐÉ{LÚãáÃû“'ÇÅ01y?sæÞ5k~w ûë–¢ 81·p¡®®ãæævþüù]»víß¿ùòå¶¶¶4,3kÖ¬èè茌Œ­[·’]$nnnÇdÁ"²"[f`ýúõÊÊÊË—/'ëÉÉÜÜ<...''':::::úÁƒùùùçÎ;wîA #›f§øB‡`±Xoß¾MHHHLLLHHHIIaÿ–öêÕ‹–agggaa!ÈÂY‚N§'%%uàsrrttt‚hllli#¡ñゃ“ ât.ü¥ïдp®[gâ8‚–– rwwß²e˃‚PTT$ ÐétváI“&íÞ½[ð9®¢lá -]ºTIIÉÅÅ…«‡œŸbddddd´|ùr‹•‘‘óöíÛ·oßR(”¡C‡’ÁǸqã¸Ú{  >}úÄŽ0ž?^VVÆ™kbbâàà`oo?nÜ8Q®Ç6yòdÿRdii©¥¥E„‘‘9Úš“A eÖ¬ŸÀ=œ°ò›3PøªÑ£GGGGGFFÆÄÄÄÄÄÄÆÆ²O—’’úî»ï¬­­§L™baaѪšñÉŒ3îܹ3}úôªª*v"ׄX …bbbbbb²nÝ:ƒ‘””D¶|ÄÆÆ’ ˆ>|XRRrĈd³Çøñ㥤¤Dù]WEEERR;ÈàZ‡MGGgäÈ‘£F9räˆ#”••ÅUOŸÉ\í¤¡¡¡¡¡ÑR.…BY¼x£n À ‡àÐÂÑ ä’_ÉÉÉ­=ÑÎÎÎÎÎŽ ˆ†††êêj2Q^^¾ÍŸµdšÿ"T¶¶¶ä’í%%%AôîÝ{Ô¨Q-–””5jÔ¨Q£~ùå—úúú¸¸82øHHHˆß³gAººº¦¦¦ffffff¦¦¦”””Ý#ubõõõ©©©dx‘˜˜˜™™Éù¦¦¬¬>>§OŸnmΞ=»sçN---mmí]»v9s†L÷ññÙ¶mÛÌ™3åääôõõÙWn©<´HÇpDDDŒ3†OêêêÇÛÚÚ6›»yóæ5kÖ|óÍ7œ‰\Ý+jjjÒÒÒß|óÍž={Ø›AdddŒ1‚<¶°°ÈÈÈ £££‹ŠŠ´µµi4š³³sYYÿòÐ6¢ 8’’’~úé§¿ÿþ»¥ E^^ÞÏÏïðáü¹ ©©©?ýôŸ[°X¬’’’ÚÚÚ«W¯FDDlذL¯ªª’——'*++Éã/_¾dff¦§§çççËÉÉ­^½šù.$..nÙ²e†††RRRÊÊÊÖÖÖçÏŸðÜÇ/X°@CCCFFfذa.\à*——7sæLEEE%%¥Y³fqíÆ?—WJJŠ‹‹‹¶¶¶´´´––Ö¼yóRRR8 ðÙIOO×ÓÓ;tè€Ob'¢€ãÁƒ³fͺråJÿþý[*Ãb±***Ö¬YãêêÊ›»~ýú“'OJJJòžÅ•"))ijjzñâEvWˆ¼¼|UUy\YY©  @Óh´C‡©©©©¨¨øùùݹs‡ù.díڵÆ «®®ÎÏÏß¾}»¿¿ÿ¶mÛ9wܸqÅÅÅ¡¡¡•••§OŸöóó;qâ;·ªªÊÆÆÆÂÂ"//ïýû÷æææ666ÕÕÕ‚äòºté’ƒƒƒ¥¥e\\\uuubbâØ±c/]ºÔÒ)±±±'N|xKe-Zôúõ릦¦OŸ>ýúë¯ß~û-o®é'ì† Î2.../_¾ljjÊÉÉquueÏvY¼xñ¯¿þúéÓ§ÂÂBooï¥K—’éK—.õôô,---//߸qãÔ©Sù—ïÒ¤¤¤xÛ‡šµgÏÎWU__Ÿ³[äöíÛK–,á,¿téÒ[·n ’ËåðáÃkÖ¬111áJ766^»v-oÏZPP——Wdd¤••• G¬ÃÁyÀë§Ÿ~úðჱ±1{y vo›““Óܹseee‡ VRRrùòå¶ÕgÆŒ...²²²ÖÖÖƒ :r䙾råJ+++ccã!C†X[[»¹¹‘éË–-ÓÕÕ8p ŽŽNà_¾‹ª­­‹‹›;w.{J«„††<˜ýã«W¯LMM9 ˜™™½zõJ\.QQQ-5TÌš5+22’3åðáÃGމ‰‰á5 ]µ='·´gº eæÌ™ÓìÚ­½¾³³³³³3oy …²ÿþýû÷ó¦ïܹsçΖïr8£@Çpp*))ñöö>~ü8;¥¬¬LUU•³ŒššZii© ¹\>|ø`ddÔlV¿~ý8›U‚ƒƒ/^¼øàÁÖÚG€ÎK›wgdTyyù7rrrvìØÑªÓ‹ŠŠf̘qüøq;;;!ÕP@666 åÞ½{â­´ŽîOIIiúôéW®\iÕšiùùù¿þú+W´¡¢¢ÂÕbQZZÊnÕàŸËE__?''§Ù¬ÜÜ\===öªªªaaa{öìᣠ]ŽžbøðáÍndÓ¬ÂÂÂÉ“'ûùù988pe 2$--3%55ÕØØX\.vvvÁÁÁÍfs:ªªªááádoy]Žž"..nРA‚”ü÷ß÷îÝÛlOÊÔ©Sƒ‚‚8SΞ=ëää$H.—õë×9räåË—\é¯^½:zôèúõë¹ÒÕÔÔîß¿ïïïìØ1A:Ý“ƒƒCHHHqq1ƒÁ 'þ,Z´hÏž=ì|2qttܲeËäÉ“›Í]¾|ù“'OöìÙS^^^^^¾k×®„„„eË– ’ËeÀ€þþþ&Løã?>~üØØØXPP`kkûÇ 0€÷uuõÈÈÈÀÀ@??¿V¼ n8º§Í›7ËÈÈ :ôÚµkÿüóϤI“9—\kœòÿÊËËÉ\…èèè„„}}}}}ý¤¤¤èèhöäþ¹¼\\\îß¿ÿìÙ³Q£FÑh4 ‹Çß½{·Ù]…IêêêQQQgΜٵkWk^§vM‹…NËÆÆÆÆÆ†O–¦óÏ"ܸq£m¹¼ÌÍÍù¯¼Â[uuu®‘"ÐÉ¡…„pcïCÐQpˆ”¹¹y]]¸kñ...-­žÐ68D‡N§¿|ùò«£d:ƒOŸ>ñ®úÐf8DÇÍÍmìØ±â®…@lmm£££Ñ·€ˆÐéôÚÚZeeeqWD T*ÕÊÊjÑ¢Eâ®t8DÄÍÍmܸq| HKK«««·”«££#²,’žž^}}=:V C àAš7”””ÌÍÍ[ʵ¶¶Y:V £ à…¯6otN’’’èX€€@èºÖè .èX€€@èºhó:V ýpW—nÞ ¡cÚ›·oóFqqñ¯¿þ*®úbÀ€\)zzz¯_¿Þ¹sg'¯9tZ8„ˆ·yC[[{þüùb¬’€¸Rlmm¯^½ºeË ´Œ@«QºÄ*Ë]Ôœ9sFŽÙ¥ûS8}üø177÷Â… â®t=ø¦ ,Ý`ôÌX€6CÀ ,]}rJ³0cÚ€Pt¿æ f¬@Û àŠnÙ¼ABÇ ´€Ž×]›7ØÐ±­…Y*¯›MNif¬@« … ƒuûæ :V Upt°n}ZWW÷îÝ»uëÖ9r„ø¯‡õvyww÷I“&r&Ž1bôèÑ¥¥¥/^¼ Óé¿üò ÿò­Àáãü[«¬¬¼wïÞÀýýýÉ”9sæ9r„] ,,ÌÎÎŽ<ž?þüÁy)???öÕØRRRµµµ_½5;娱cü+L§Óµ´´/À:wD·'bøðáIIIäqŸ>}âããûöíKþX]]­««[VVFf%''kii±O,,,ÔÑÑ!ÿrÙã3†nmm½yófv« ç­¹þÌ)JAA¶¶6gbmmí¶mÛ®^½šŸŸßÔÔD„„„ƒÁh©<@« K@DØa~yyù•+Wrrr.\¸@f•””°GZÈËËWTTY_¾|áŠ!xC ‚ ®^½š­¯¯oll¼dÉ’7nðÿ.ѧO®ÌÌÌëׯ———³X¬ÆÆF&“ɧ<@« …@x›.]ºäããóæÍ …¢¡¡‘™™©¢¢Â{¢€-¤úúú7oÞÄÅÅýéÓ§õõõUUUáááK–,ñôôw¥Z-]ppðï¿ÿž––&))ibb²víÚ… Š»R­ƒ€„]* t8@èp€Ð!à¡CÀB‡€„ÝÿSAË,¢½7IEND®B`‚kamailio-4.0.4/doc/sip/figures/redirect.eps0000644000000000000000000013512412223032460017321 0ustar rootroot%!PS-Adobe-2.0 EPSF-2.0 %%Title: redirect.dia %%Creator: Dia v0.91 %%CreationDate: Tue Jul 15 14:59:36 2003 %%For: janakj %%Orientation: Portrait %%Magnification: 1.0000 %%BoundingBox: 0 0 587 419 %%BeginSetup %%EndSetup %%EndComments %%BeginProlog [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def /cp {closepath} bind def /c {curveto} bind def /f {fill} bind def /a {arc} bind def /ef {eofill} bind def /ex {exch} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth pop} bind def /tr {translate} bind def /ellipsedict 8 dict def ellipsedict /mtrx matrix put /ellipse { ellipsedict begin /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc savematrix setmatrix end } def /mergeprocs { dup length 3 -1 roll dup length dup 5 1 roll 3 -1 roll add array cvx dup 3 -1 roll 0 exch putinterval dup 4 2 roll putinterval } bind def /dpi_x 300 def /dpi_y 300 def /conicto { /to_y exch def /to_x exch def /conic_cntrl_y exch def /conic_cntrl_x exch def currentpoint /p0_y exch def /p0_x exch def /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def /p2_x p1_x to_x p0_x sub 1 3 div mul add def /p2_y p1_y to_y p0_y sub 1 3 div mul add def p1_x p1_y p2_x p2_y to_x to_y curveto } bind def /start_ol { gsave 1.1 dpi_x div dup scale} bind def /end_ol { closepath fill grestore } bind def 28.346000 -28.346000 scale -8.100000 -15.200000 translate %%EndProlog 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 8.972810 11.000000 m 9.324087 11.000000 11.431750 11.000000 11.783028 11.000000 c 12.134305 11.000000 12.520710 11.351277 12.485582 12.053831 c 12.134305 12.053831 12.134305 12.053831 11.783028 12.053831 c 11.800591 11.386405 10.729196 11.702554 10.377919 11.702554 c 10.026642 11.702554 8.972810 11.368841 8.972810 12.053831 c 8.621533 12.053831 8.621533 12.053831 8.270256 12.053831 c 8.200000 11.351277 8.621533 11.000000 8.972810 11.000000 c f 0.000000 0.000000 0.000000 srgb n 8.972810 11.000000 m 9.324087 11.000000 11.431750 11.000000 11.783028 11.000000 c 12.134305 11.000000 12.520710 11.351277 12.485582 12.053831 c 12.134305 12.053831 12.134305 12.053831 11.783028 12.053831 c 11.800591 11.386405 10.729196 11.702554 10.377919 11.702554 c 10.026642 11.702554 8.972810 11.368841 8.972810 12.053831 c 8.621533 12.053831 8.621533 12.053831 8.270256 12.053831 c 8.200000 11.351277 8.621533 11.000000 8.972810 11.000000 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 8.972810 11.000000 m 9.324087 11.000000 11.431750 11.000000 11.783028 11.000000 c 12.134305 11.000000 12.520710 11.351277 12.485582 12.053831 c 12.134305 12.053831 12.134305 12.053831 11.783028 12.053831 c 11.800591 11.386405 10.729196 11.702554 10.377919 11.702554 c 10.026642 11.702554 8.972810 11.368841 8.972810 12.053831 c 8.621533 12.053831 8.621533 12.053831 8.270256 12.053831 c 8.200000 11.351277 8.621533 11.000000 8.972810 11.000000 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 10.377919 11.351277 m 11.783028 11.351277 11.783028 13.458940 11.431750 13.458940 c 11.080473 13.458940 9.675364 13.458940 9.324087 13.458940 c 8.972810 13.458940 8.972810 11.351277 10.377919 11.351277 c f 0.000000 0.000000 0.000000 srgb n 10.377919 11.351277 m 11.783028 11.351277 11.783028 13.458940 11.431750 13.458940 c 11.080473 13.458940 9.675364 13.458940 9.324087 13.458940 c 8.972810 13.458940 8.972810 11.351277 10.377919 11.351277 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 10.377919 11.351277 m 11.783028 11.351277 11.783028 13.458940 11.431750 13.458940 c 11.080473 13.458940 9.675364 13.458940 9.324087 13.458940 c 8.972810 13.458940 8.972810 11.351277 10.377919 11.351277 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 10.377919 12.405109 0.702554 0.702554 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 10.377919 12.405109 0.702554 0.702554 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 10.377919 12.405109 0.702554 0.702554 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 9.956386 12.712476 0.087819 0.096601 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 9.956386 12.712476 0.087819 0.096601 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 9.956386 12.712476 0.087819 0.096601 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 9.814119 12.378763 0.087819 0.096601 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 9.814119 12.378763 0.087819 0.096601 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 9.814119 12.378763 0.087819 0.096601 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 9.919502 12.062613 0.087819 0.096601 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 9.919502 12.062613 0.087819 0.096601 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 9.919502 12.062613 0.087819 0.096601 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 10.235652 11.904539 0.087819 0.096601 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 10.235652 11.904539 0.087819 0.096601 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 10.235652 11.904539 0.087819 0.096601 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 10.551801 11.922102 0.087819 0.096601 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 10.551801 11.922102 0.087819 0.096601 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 10.551801 11.922102 0.087819 0.096601 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 10.797695 12.132869 0.087819 0.096601 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 10.797695 12.132869 0.087819 0.096601 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 10.797695 12.132869 0.087819 0.096601 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 10.903078 12.378763 0.087819 0.096601 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 10.903078 12.378763 0.087819 0.096601 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 10.903078 12.378763 0.087819 0.096601 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 10.815259 12.677348 0.087819 0.096601 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 10.815259 12.677348 0.087819 0.096601 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 10.815259 12.677348 0.087819 0.096601 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 10.569365 12.870551 0.087819 0.096601 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 10.569365 12.870551 0.087819 0.096601 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 10.569365 12.870551 0.087819 0.096601 0 360 ellipse cp s 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0.080000 slw 0 slc 0 slj [] 0 sd 0.701961 0.701961 0.701961 srgb n 17.741387 1.650001 m 17.741387 6.799521 l 19.948324 6.799521 l 19.948324 1.650001 l f 0.000000 0.000000 0.000000 srgb n 17.741387 1.650001 m 17.741387 6.799521 l 19.948324 6.799521 l 19.948324 1.650001 l cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 17.962081 1.958972 m 17.962081 2.547489 l 19.727630 2.547489 l 19.727630 1.958972 l cp s 0 slc 0 slj [] 0 sd n 17.962081 2.547489 m 17.962081 3.136006 l 19.727630 3.136006 l 19.727630 2.547489 l cp s 0 slc 0 slj [] 0 sd n 17.962081 3.136006 m 17.962081 3.724522 l 19.727630 3.724522 l 19.727630 3.136006 l cp s 0 slc 0 slj [] 0 sd n 17.962081 3.724522 m 17.962081 4.313039 l 19.727630 4.313039 l 19.727630 3.724522 l cp s 0 slc 0 slj [] 0 sd n 17.962081 4.430742 m 17.962081 4.783852 l 19.065549 4.783852 l 19.065549 4.430742 l cp s 0 slc 0 slj [] 0 sd 0.000000 1.000000 0.000000 srgb n 19.617283 4.489594 0.077243 0.077243 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 19.617283 4.489594 0.077243 0.077243 0 360 ellipse cp s 0 slc 0 slj [] 0 sd 1.000000 1.000000 0.000000 srgb n 19.617283 4.725000 0.077243 0.077243 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 19.617283 4.725000 0.077243 0.077243 0 360 ellipse cp s 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 19.175896 4.548445 m 19.175896 4.783852 l 19.440728 4.783852 l 19.440728 4.548445 l f 0.000000 0.000000 0.000000 srgb n 19.175896 4.548445 m 19.175896 4.783852 l 19.440728 4.783852 l 19.440728 4.548445 l cp s 0 slc 0 slj [] 0 sd n 18.109210 5.254665 m 18.109210 6.542045 l s 0 slc 0 slj [] 0 sd n 18.477033 5.254665 m 18.477033 6.542045 l s 0 slc 0 slj [] 0 sd n 18.844855 5.254665 m 18.844855 6.542045 l s 0 slc 0 slj [] 0 sd n 19.212678 5.254665 m 19.212678 6.542045 l s 0 slc 0 slj [] 0 sd n 19.580501 5.254665 m 19.580501 6.542045 l s 0 slc 0 slj [] 0 sd n 19.948324 5.254665 m 19.948324 6.542045 l s 0 slc 0 slj [] 0 sd 0.600000 0.600000 0.600000 srgb n 17.299999 7.240909 m 17.741387 6.358134 l 17.741387 6.799521 l 19.948324 6.799521 l 19.948324 6.358134 l 20.536841 7.240909 l f 0.000000 0.000000 0.000000 srgb n 17.299999 7.240909 m 17.741387 6.358134 l 17.741387 6.799521 l 19.948324 6.799521 l 19.948324 6.358134 l 20.536841 7.240909 l cp s 0.100000 slw [] 0 sd [] 0 sd 0 slc 0 slj 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 24.967252 11.000000 m 25.316004 11.000000 27.408512 11.000000 27.757263 11.000000 c 28.106014 11.000000 28.489641 11.348752 28.454766 12.046254 c 28.106014 12.046254 28.106014 12.046254 27.757263 12.046254 c 27.774700 11.383627 26.711009 11.697503 26.362258 11.697503 c 26.013506 11.697503 24.967252 11.366189 24.967252 12.046254 c 24.618501 12.046254 24.618501 12.046254 24.269750 12.046254 c 24.199999 11.348752 24.618501 11.000000 24.967252 11.000000 c f 0.000000 0.000000 0.000000 srgb n 24.967252 11.000000 m 25.316004 11.000000 27.408512 11.000000 27.757263 11.000000 c 28.106014 11.000000 28.489641 11.348752 28.454766 12.046254 c 28.106014 12.046254 28.106014 12.046254 27.757263 12.046254 c 27.774700 11.383627 26.711009 11.697503 26.362258 11.697503 c 26.013506 11.697503 24.967252 11.366189 24.967252 12.046254 c 24.618501 12.046254 24.618501 12.046254 24.269750 12.046254 c 24.199999 11.348752 24.618501 11.000000 24.967252 11.000000 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 24.967252 11.000000 m 25.316004 11.000000 27.408512 11.000000 27.757263 11.000000 c 28.106014 11.000000 28.489641 11.348752 28.454766 12.046254 c 28.106014 12.046254 28.106014 12.046254 27.757263 12.046254 c 27.774700 11.383627 26.711009 11.697503 26.362258 11.697503 c 26.013506 11.697503 24.967252 11.366189 24.967252 12.046254 c 24.618501 12.046254 24.618501 12.046254 24.269750 12.046254 c 24.199999 11.348752 24.618501 11.000000 24.967252 11.000000 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.362258 11.348752 m 27.757263 11.348752 27.757263 13.441260 27.408512 13.441260 c 27.059760 13.441260 25.664755 13.441260 25.316004 13.441260 c 24.967252 13.441260 24.967252 11.348752 26.362258 11.348752 c f 0.000000 0.000000 0.000000 srgb n 26.362258 11.348752 m 27.757263 11.348752 27.757263 13.441260 27.408512 13.441260 c 27.059760 13.441260 25.664755 13.441260 25.316004 13.441260 c 24.967252 13.441260 24.967252 11.348752 26.362258 11.348752 c s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.362258 11.348752 m 27.757263 11.348752 27.757263 13.441260 27.408512 13.441260 c 27.059760 13.441260 25.664755 13.441260 25.316004 13.441260 c 24.967252 13.441260 24.967252 11.348752 26.362258 11.348752 c s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.362258 12.395006 0.697503 0.697503 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.362258 12.395006 0.697503 0.697503 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.362258 12.395006 0.697503 0.697503 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 25.943756 12.700163 0.087188 0.095907 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 25.943756 12.700163 0.087188 0.095907 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 25.943756 12.700163 0.087188 0.095907 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 25.802512 12.368849 0.087188 0.095907 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 25.802512 12.368849 0.087188 0.095907 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 25.802512 12.368849 0.087188 0.095907 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 25.907137 12.054973 0.087188 0.095907 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 25.907137 12.054973 0.087188 0.095907 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 25.907137 12.054973 0.087188 0.095907 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.221013 11.898035 0.087188 0.095907 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.221013 11.898035 0.087188 0.095907 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.221013 11.898035 0.087188 0.095907 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.534889 11.915473 0.087188 0.095907 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.534889 11.915473 0.087188 0.095907 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.534889 11.915473 0.087188 0.095907 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.779015 12.124723 0.087188 0.095907 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.779015 12.124723 0.087188 0.095907 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.779015 12.124723 0.087188 0.095907 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.883641 12.368849 0.087188 0.095907 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.883641 12.368849 0.087188 0.095907 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.883641 12.368849 0.087188 0.095907 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.796453 12.665288 0.087188 0.095907 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.796453 12.665288 0.087188 0.095907 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.796453 12.665288 0.087188 0.095907 0 360 ellipse cp s 0.100000 slw 0 slc 0 slj [] 0 sd 1.000000 1.000000 1.000000 srgb n 26.552327 12.857101 0.087188 0.095907 0 360 ellipse f 0.000000 0.000000 0.000000 srgb n 26.552327 12.857101 0.087188 0.095907 0 360 ellipse cp s 0.010000 slw 0 slc 0 slj [] 0 sd n 26.552327 12.857101 0.087188 0.095907 0 360 ellipse cp s 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 10.500000 10.450000 m 12.900000 5.850000 16.914700 5.650000 16.400000 5.650000 c s 0 slj n 16.400000 6.050000 m 17.200000 5.650000 l 16.400000 5.250000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 6.590814 -4.113690 16.989040 16.989040 44.056805 65.644951 ellipse s 0 slj n 13.453648 10.989993 m 12.850000 11.650000 l 13.740194 11.736914 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 14.000000 12.600000 m 22.850000 12.600000 l s 0 slj n 22.850000 13.000000 m 23.650000 12.600000 l 22.850000 12.200000 l f gsave 16.300000 1.300000 translate 0.035278 -0.035278 scale start_ol 1152 1984 moveto 2731 1984 lineto 3277 1984 3520 1731 3520 1163 curveto 3520 752 lineto 3520 468 3557 190 3616 0 curveto 4160 0 lineto 4160 145 lineto 4160 297 4160 461 4160 1074 curveto 4160 1830 4024 2057 3461 2271 curveto 3959 2524 4160 2847 4160 3373 curveto 4160 4171 3653 4608 2734 4608 curveto 576 4608 lineto 576 0 lineto 1152 0 lineto 1152 1984 lineto 1152 2496 moveto 1152 4087 lineto 2600 4087 lineto 2934 4087 3127 4036 3275 3910 curveto 3436 3776 3520 3567 3520 3288 curveto 3520 2743 3237 2496 2600 2496 curveto 1152 2496 lineto end_ol grestore gsave 16.901133 1.300000 translate 0.035278 -0.035278 scale start_ol 3250 1472 moveto 3250 1959 3212 2251 3117 2488 curveto 2902 3012 2395 3328 1774 3328 curveto 849 3328 253 2650 253 1610 curveto 253 614 830 0 1761 0 curveto 2522 0 3048 397 3181 1062 curveto 2648 1062 lineto 2503 685 2205 489 1780 489 curveto 1445 489 1159 620 982 860 curveto 855 1024 811 1188 805 1472 curveto 3250 1472 lineto 817 1920 moveto 862 2477 1236 2839 1768 2839 curveto 2287 2839 2686 2448 2686 1954 curveto 2686 1943 2686 1932 2680 1920 curveto 817 1920 lineto end_ol grestore gsave 17.366800 1.300000 translate 0.035278 -0.035278 scale start_ol 3136 4608 moveto 2624 4608 lineto 2624 2816 lineto 2403 3151 2051 3328 1610 3328 curveto 753 3328 192 2683 192 1694 curveto 192 645 728 0 1596 0 curveto 2039 0 2347 169 2624 576 curveto 2624 0 lineto 3136 0 lineto 3136 4608 lineto 1680 2833 moveto 2258 2833 2624 2372 2624 1652 curveto 2624 956 2252 495 1686 495 curveto 1096 495 704 961 704 1664 curveto 704 2367 1096 2833 1680 2833 curveto end_ol grestore gsave 17.832467 1.300000 translate 0.035278 -0.035278 scale start_ol 960 3309 moveto 448 3309 lineto 448 0 lineto 960 0 lineto 960 3309 lineto 960 4608 moveto 448 4608 lineto 448 3941 lineto 960 3941 lineto 960 4608 lineto end_ol grestore gsave 18.018733 1.300000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1728 lineto 960 2204 1080 2515 1334 2699 curveto 1498 2820 1657 2858 2024 2864 curveto 2024 3328 lineto 1939 3328 1896 3328 1829 3328 curveto 1501 3328 1252 3153 960 2725 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 18.298133 1.300000 translate 0.035278 -0.035278 scale start_ol 3250 1472 moveto 3250 1959 3212 2251 3117 2488 curveto 2902 3012 2395 3328 1774 3328 curveto 849 3328 253 2650 253 1610 curveto 253 614 830 0 1761 0 curveto 2522 0 3048 397 3181 1062 curveto 2648 1062 lineto 2503 685 2205 489 1780 489 curveto 1445 489 1159 620 982 860 curveto 855 1024 811 1188 805 1472 curveto 3250 1472 lineto 817 1920 moveto 862 2477 1236 2839 1768 2839 curveto 2287 2839 2686 2448 2686 1954 curveto 2686 1943 2686 1932 2680 1920 curveto 817 1920 lineto end_ol grestore gsave 18.763800 1.300000 translate 0.035278 -0.035278 scale start_ol 2941 2182 moveto 2916 2488 2847 2686 2722 2860 curveto 2497 3154 2104 3328 1648 3328 curveto 767 3328 192 2665 192 1634 curveto 192 634 754 0 1642 0 curveto 2423 0 2917 449 2979 1215 curveto 2446 1215 lineto 2357 731 2085 489 1635 489 curveto 1052 489 704 921 704 1635 curveto 704 2390 1046 2839 1623 2839 curveto 2066 2839 2345 2603 2408 2182 curveto 2941 2182 lineto end_ol grestore gsave 19.187133 1.300000 translate 0.035278 -0.035278 scale start_ol 1569 3328 moveto 1024 3328 lineto 1024 4243 lineto 512 4243 lineto 512 3328 lineto 62 3328 lineto 62 2896 lineto 512 2896 lineto 512 518 lineto 512 187 737 0 1144 0 curveto 1269 0 1394 13 1569 44 curveto 1569 482 lineto 1499 464 1417 464 1316 464 curveto 1087 464 1024 524 1024 746 curveto 1024 2896 lineto 1569 2896 lineto 1569 3328 lineto end_ol grestore gsave 19.424200 1.300000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.661267 1.300000 translate 0.035278 -0.035278 scale start_ol 3904 3233 moveto 3904 4103 3259 4608 2153 4608 curveto 1100 4608 448 4117 448 3326 curveto 448 2792 744 2457 1347 2307 curveto 2485 2019 lineto 3064 1875 3328 1654 3328 1312 curveto 3328 1078 3205 839 3023 707 curveto 2853 587 2583 521 2238 521 curveto 1774 521 1458 634 1253 884 curveto 1094 1074 1024 1282 1024 1550 curveto 448 1550 lineto 448 1143 527 875 702 632 curveto 1004 219 1511 0 2182 0 curveto 2708 0 3137 120 3421 336 curveto 3717 570 3904 959 3904 1337 curveto 3904 1877 3552 2272 2929 2434 curveto 1779 2728 lineto 1225 2872 1024 3040 1024 3376 curveto 1024 3819 1459 4113 2116 4113 curveto 2893 4113 3328 3798 3328 3233 curveto 3904 3233 lineto end_ol grestore gsave 20.220067 1.300000 translate 0.035278 -0.035278 scale start_ol 3250 1472 moveto 3250 1959 3212 2251 3117 2488 curveto 2902 3012 2395 3328 1774 3328 curveto 849 3328 253 2650 253 1610 curveto 253 614 830 0 1761 0 curveto 2522 0 3048 397 3181 1062 curveto 2648 1062 lineto 2503 685 2205 489 1780 489 curveto 1445 489 1159 620 982 860 curveto 855 1024 811 1188 805 1472 curveto 3250 1472 lineto 817 1920 moveto 862 2477 1236 2839 1768 2839 curveto 2287 2839 2686 2448 2686 1954 curveto 2686 1943 2686 1932 2680 1920 curveto 817 1920 lineto end_ol grestore gsave 20.685733 1.300000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1728 lineto 960 2204 1080 2515 1334 2699 curveto 1498 2820 1657 2858 2024 2864 curveto 2024 3328 lineto 1939 3328 1896 3328 1829 3328 curveto 1501 3328 1252 3153 960 2725 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 20.965133 1.300000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 3079 3328 lineto 2484 3328 lineto 1546 629 lineto 659 3328 lineto 63 3328 lineto 1229 0 lineto 1806 0 lineto end_ol grestore gsave 21.388467 1.300000 translate 0.035278 -0.035278 scale start_ol 3250 1472 moveto 3250 1959 3212 2251 3117 2488 curveto 2902 3012 2395 3328 1774 3328 curveto 849 3328 253 2650 253 1610 curveto 253 614 830 0 1761 0 curveto 2522 0 3048 397 3181 1062 curveto 2648 1062 lineto 2503 685 2205 489 1780 489 curveto 1445 489 1159 620 982 860 curveto 855 1024 811 1188 805 1472 curveto 3250 1472 lineto 817 1920 moveto 862 2477 1236 2839 1768 2839 curveto 2287 2839 2686 2448 2686 1954 curveto 2686 1943 2686 1932 2680 1920 curveto 817 1920 lineto end_ol grestore gsave 21.854133 1.300000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1728 lineto 960 2204 1080 2515 1334 2699 curveto 1498 2820 1657 2858 2024 2864 curveto 2024 3328 lineto 1939 3328 1896 3328 1829 3328 curveto 1501 3328 1252 3153 960 2725 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 8.100000 14.650000 translate 0.035278 -0.035278 scale start_ol 3520 4608 moveto 3520 1485 lineto 3520 887 3058 521 2297 521 curveto 1946 521 1660 600 1433 753 curveto 1199 924 1088 1149 1088 1485 curveto 1088 4608 lineto 512 4608 lineto 512 1471 lineto 512 564 1190 0 2298 0 curveto 3392 0 4096 576 4096 1471 curveto 4096 4608 lineto 3520 4608 lineto end_ol grestore gsave 8.701133 14.650000 translate 0.035278 -0.035278 scale start_ol 2880 2355 moveto 2880 2977 2434 3328 1639 3328 curveto 839 3328 320 2959 320 2391 curveto 320 1911 585 1683 1366 1508 curveto 1858 1396 lineto 2223 1314 2368 1191 2368 969 curveto 2368 682 2053 489 1584 489 curveto 1295 489 1050 564 915 691 curveto 832 777 793 864 761 1077 curveto 320 1077 lineto 320 349 733 0 1567 0 curveto 2369 0 2880 380 2880 972 curveto 2880 1428 2603 1680 1947 1826 curveto 1443 1938 lineto 1015 2031 832 2160 832 2377 curveto 832 2658 1141 2839 1629 2839 curveto 2110 2839 2368 2672 2368 2355 curveto 2880 2355 lineto end_ol grestore gsave 9.124467 14.650000 translate 0.035278 -0.035278 scale start_ol 3250 1472 moveto 3250 1959 3212 2251 3117 2488 curveto 2902 3012 2395 3328 1774 3328 curveto 849 3328 253 2650 253 1610 curveto 253 614 830 0 1761 0 curveto 2522 0 3048 397 3181 1062 curveto 2648 1062 lineto 2503 685 2205 489 1780 489 curveto 1445 489 1159 620 982 860 curveto 855 1024 811 1188 805 1472 curveto 3250 1472 lineto 817 1920 moveto 862 2477 1236 2839 1768 2839 curveto 2287 2839 2686 2448 2686 1954 curveto 2686 1943 2686 1932 2680 1920 curveto 817 1920 lineto end_ol grestore gsave 9.590133 14.650000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1728 lineto 960 2204 1080 2515 1334 2699 curveto 1498 2820 1657 2858 2024 2864 curveto 2024 3328 lineto 1939 3328 1896 3328 1829 3328 curveto 1501 3328 1252 3153 960 2725 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 9.869533 14.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 10.106600 14.650000 translate 0.035278 -0.035278 scale start_ol 3003 1408 moveto 3478 0 lineto 4137 0 lineto 2515 4608 lineto 1755 4608 lineto 108 0 lineto 735 0 lineto 1223 1408 lineto 3003 1408 lineto 2839 1920 moveto 1369 1920 lineto 2129 3986 lineto 2839 1920 lineto end_ol grestore gsave 10.665400 14.650000 translate 0.035278 -0.035278 scale start_ol 2624 3328 moveto 2624 2758 lineto 2357 3146 2033 3328 1608 3328 curveto 763 3328 192 2641 192 1634 curveto 192 1125 341 711 620 415 curveto 873 154 1236 0 1593 0 curveto 2021 0 2319 175 2624 587 curveto 2624 396 lineto 2624 -105 2564 -409 2424 -614 curveto 2277 -833 1991 -960 1651 -960 curveto 1397 -960 1171 -883 1017 -745 curveto 891 -630 837 -522 804 -284 curveto 292 -284 lineto 350 -988 844 -1408 1634 -1408 curveto 2135 -1408 2565 -1248 2783 -980 curveto 3040 -674 3136 -253 3136 532 curveto 3136 3328 lineto 2624 3328 lineto 1671 2839 moveto 2271 2839 2624 2401 2624 1647 curveto 2624 927 2264 489 1677 489 curveto 1071 489 704 933 704 1664 curveto 704 2390 1077 2839 1671 2839 curveto end_ol grestore gsave 11.131067 14.650000 translate 0.035278 -0.035278 scale start_ol 3250 1472 moveto 3250 1959 3212 2251 3117 2488 curveto 2902 3012 2395 3328 1774 3328 curveto 849 3328 253 2650 253 1610 curveto 253 614 830 0 1761 0 curveto 2522 0 3048 397 3181 1062 curveto 2648 1062 lineto 2503 685 2205 489 1780 489 curveto 1445 489 1159 620 982 860 curveto 855 1024 811 1188 805 1472 curveto 3250 1472 lineto 817 1920 moveto 862 2477 1236 2839 1768 2839 curveto 2287 2839 2686 2448 2686 1954 curveto 2686 1943 2686 1932 2680 1920 curveto 817 1920 lineto end_ol grestore gsave 11.596733 14.650000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1776 lineto 960 2434 1318 2864 1869 2864 curveto 2291 2864 2560 2618 2560 2231 curveto 2560 0 lineto 3072 0 lineto 3072 2445 lineto 3072 2982 2668 3328 2041 3328 curveto 1556 3328 1246 3139 960 2680 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 12.062400 14.650000 translate 0.035278 -0.035278 scale start_ol 1569 3328 moveto 1024 3328 lineto 1024 4243 lineto 512 4243 lineto 512 3328 lineto 62 3328 lineto 62 2896 lineto 512 2896 lineto 512 518 lineto 512 187 737 0 1144 0 curveto 1269 0 1394 13 1569 44 curveto 1569 482 lineto 1499 464 1417 464 1316 464 curveto 1087 464 1024 524 1024 746 curveto 1024 2896 lineto 1569 2896 lineto 1569 3328 lineto end_ol grestore gsave 12.299467 14.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 12.536533 14.650000 translate 0.035278 -0.035278 scale start_ol 3003 1408 moveto 3478 0 lineto 4137 0 lineto 2515 4608 lineto 1755 4608 lineto 108 0 lineto 735 0 lineto 1223 1408 lineto 3003 1408 lineto 2839 1920 moveto 1369 1920 lineto 2129 3986 lineto 2839 1920 lineto end_ol grestore gsave 24.050000 14.650000 translate 0.035278 -0.035278 scale start_ol 3520 4608 moveto 3520 1485 lineto 3520 887 3058 521 2297 521 curveto 1946 521 1660 600 1433 753 curveto 1199 924 1088 1149 1088 1485 curveto 1088 4608 lineto 512 4608 lineto 512 1471 lineto 512 564 1190 0 2298 0 curveto 3392 0 4096 576 4096 1471 curveto 4096 4608 lineto 3520 4608 lineto end_ol grestore gsave 24.651133 14.650000 translate 0.035278 -0.035278 scale start_ol 2880 2355 moveto 2880 2977 2434 3328 1639 3328 curveto 839 3328 320 2959 320 2391 curveto 320 1911 585 1683 1366 1508 curveto 1858 1396 lineto 2223 1314 2368 1191 2368 969 curveto 2368 682 2053 489 1584 489 curveto 1295 489 1050 564 915 691 curveto 832 777 793 864 761 1077 curveto 320 1077 lineto 320 349 733 0 1567 0 curveto 2369 0 2880 380 2880 972 curveto 2880 1428 2603 1680 1947 1826 curveto 1443 1938 lineto 1015 2031 832 2160 832 2377 curveto 832 2658 1141 2839 1629 2839 curveto 2110 2839 2368 2672 2368 2355 curveto 2880 2355 lineto end_ol grestore gsave 25.074467 14.650000 translate 0.035278 -0.035278 scale start_ol 3250 1472 moveto 3250 1959 3212 2251 3117 2488 curveto 2902 3012 2395 3328 1774 3328 curveto 849 3328 253 2650 253 1610 curveto 253 614 830 0 1761 0 curveto 2522 0 3048 397 3181 1062 curveto 2648 1062 lineto 2503 685 2205 489 1780 489 curveto 1445 489 1159 620 982 860 curveto 855 1024 811 1188 805 1472 curveto 3250 1472 lineto 817 1920 moveto 862 2477 1236 2839 1768 2839 curveto 2287 2839 2686 2448 2686 1954 curveto 2686 1943 2686 1932 2680 1920 curveto 817 1920 lineto end_ol grestore gsave 25.540133 14.650000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1728 lineto 960 2204 1080 2515 1334 2699 curveto 1498 2820 1657 2858 2024 2864 curveto 2024 3328 lineto 1939 3328 1896 3328 1829 3328 curveto 1501 3328 1252 3153 960 2725 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 25.819533 14.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 26.056600 14.650000 translate 0.035278 -0.035278 scale start_ol 3003 1408 moveto 3478 0 lineto 4137 0 lineto 2515 4608 lineto 1755 4608 lineto 108 0 lineto 735 0 lineto 1223 1408 lineto 3003 1408 lineto 2839 1920 moveto 1369 1920 lineto 2129 3986 lineto 2839 1920 lineto end_ol grestore gsave 26.615400 14.650000 translate 0.035278 -0.035278 scale start_ol 2624 3328 moveto 2624 2758 lineto 2357 3146 2033 3328 1608 3328 curveto 763 3328 192 2641 192 1634 curveto 192 1125 341 711 620 415 curveto 873 154 1236 0 1593 0 curveto 2021 0 2319 175 2624 587 curveto 2624 396 lineto 2624 -105 2564 -409 2424 -614 curveto 2277 -833 1991 -960 1651 -960 curveto 1397 -960 1171 -883 1017 -745 curveto 891 -630 837 -522 804 -284 curveto 292 -284 lineto 350 -988 844 -1408 1634 -1408 curveto 2135 -1408 2565 -1248 2783 -980 curveto 3040 -674 3136 -253 3136 532 curveto 3136 3328 lineto 2624 3328 lineto 1671 2839 moveto 2271 2839 2624 2401 2624 1647 curveto 2624 927 2264 489 1677 489 curveto 1071 489 704 933 704 1664 curveto 704 2390 1077 2839 1671 2839 curveto end_ol grestore gsave 27.081067 14.650000 translate 0.035278 -0.035278 scale start_ol 3250 1472 moveto 3250 1959 3212 2251 3117 2488 curveto 2902 3012 2395 3328 1774 3328 curveto 849 3328 253 2650 253 1610 curveto 253 614 830 0 1761 0 curveto 2522 0 3048 397 3181 1062 curveto 2648 1062 lineto 2503 685 2205 489 1780 489 curveto 1445 489 1159 620 982 860 curveto 855 1024 811 1188 805 1472 curveto 3250 1472 lineto 817 1920 moveto 862 2477 1236 2839 1768 2839 curveto 2287 2839 2686 2448 2686 1954 curveto 2686 1943 2686 1932 2680 1920 curveto 817 1920 lineto end_ol grestore gsave 27.546733 14.650000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1776 lineto 960 2434 1318 2864 1869 2864 curveto 2291 2864 2560 2618 2560 2231 curveto 2560 0 lineto 3072 0 lineto 3072 2445 lineto 3072 2982 2668 3328 2041 3328 curveto 1556 3328 1246 3139 960 2680 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 28.012400 14.650000 translate 0.035278 -0.035278 scale start_ol 1569 3328 moveto 1024 3328 lineto 1024 4243 lineto 512 4243 lineto 512 3328 lineto 62 3328 lineto 62 2896 lineto 512 2896 lineto 512 518 lineto 512 187 737 0 1144 0 curveto 1269 0 1394 13 1569 44 curveto 1569 482 lineto 1499 464 1417 464 1316 464 curveto 1087 464 1024 524 1024 746 curveto 1024 2896 lineto 1569 2896 lineto 1569 3328 lineto end_ol grestore gsave 28.249467 14.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 28.486533 14.650000 translate 0.035278 -0.035278 scale start_ol 512 0 moveto 2563 0 lineto 2994 0 3312 120 3555 386 curveto 3779 627 3904 956 3904 1317 curveto 3904 1874 3658 2209 3086 2437 curveto 3495 2633 3712 2973 3712 3440 curveto 3712 3775 3587 4072 3350 4286 curveto 3112 4507 2800 4608 2362 4608 curveto 512 4608 lineto 512 0 lineto 1088 2624 moveto 1088 4087 lineto 2219 4087 lineto 2545 4087 2728 4043 2885 3923 curveto 3048 3797 3136 3608 3136 3355 curveto 3136 3110 3048 2914 2885 2788 curveto 2728 2668 2545 2624 2219 2624 curveto 1088 2624 lineto 1088 521 moveto 1088 2112 lineto 2508 2112 lineto 2790 2112 2978 2042 3115 1890 curveto 3253 1744 3328 1542 3328 1313 curveto 3328 1091 3253 889 3115 743 curveto 2978 591 2790 521 2508 521 curveto 1088 521 lineto end_ol grestore gsave 10.450000 6.300000 translate 0.035278 -0.035278 scale start_ol 1216 4608 moveto 640 4608 lineto 640 0 lineto 1216 0 lineto 1216 4608 lineto end_ol grestore gsave 10.687067 6.300000 translate 0.035278 -0.035278 scale start_ol 4032 4608 moveto 3520 4608 lineto 3520 841 lineto 1106 4608 lineto 512 4608 lineto 512 0 lineto 1024 0 lineto 1024 3736 lineto 3412 0 lineto 4032 0 lineto 4032 4608 lineto end_ol grestore gsave 11.288200 6.300000 translate 0.035278 -0.035278 scale start_ol 2484 0 moveto 4087 4608 lineto 3459 4608 lineto 2180 708 lineto 824 4608 lineto 190 4608 lineto 1850 0 lineto 2484 0 lineto end_ol grestore gsave 11.847000 6.300000 translate 0.035278 -0.035278 scale start_ol 1216 4608 moveto 640 4608 lineto 640 0 lineto 1216 0 lineto 1216 4608 lineto end_ol grestore gsave 12.084067 6.300000 translate 0.035278 -0.035278 scale start_ol 2240 4087 moveto 3754 4087 lineto 3754 4608 lineto 143 4608 lineto 143 4087 lineto 1664 4087 lineto 1664 0 lineto 2240 0 lineto 2240 4087 lineto end_ol grestore gsave 12.592067 6.300000 translate 0.035278 -0.035278 scale start_ol 1152 2112 moveto 3667 2112 lineto 3667 2624 lineto 1152 2624 lineto 1152 4087 lineto 3762 4087 lineto 3762 4608 lineto 576 4608 lineto 576 0 lineto 3876 0 lineto 3876 521 lineto 1152 521 lineto 1152 2112 lineto end_ol grestore gsave 13.150867 6.300000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 13.387933 6.300000 translate 0.035278 -0.035278 scale start_ol 3073 4445 moveto 2585 4445 lineto 2363 3200 lineto 1571 3200 lineto 1799 4445 lineto 1318 4445 lineto 1090 3200 lineto 323 3200 lineto 323 2752 lineto 1007 2752 lineto 811 1664 lineto 89 1664 lineto 89 1216 lineto 729 1216 lineto 488 0 lineto 969 0 lineto 1217 1216 lineto 2002 1216 lineto 1761 0 lineto 2243 0 lineto 2490 1216 lineto 3231 1216 lineto 3231 1664 lineto 2566 1664 lineto 2762 2752 lineto 3434 2752 lineto 3434 3200 lineto 2845 3200 lineto 3073 4445 lineto 2281 2752 moveto 2085 1664 lineto 1293 1664 lineto 1495 2752 lineto 2281 2752 lineto end_ol grestore gsave 13.853600 6.300000 translate 0.035278 -0.035278 scale start_ol 1664 3207 moveto 1664 0 lineto 2176 0 lineto 2176 4503 lineto 1839 4503 lineto 1645 3811 1520 3716 669 3608 curveto 669 3207 lineto 1664 3207 lineto end_ol grestore gsave 17.100000 13.950000 translate 0.035278 -0.035278 scale start_ol 1216 4608 moveto 640 4608 lineto 640 0 lineto 1216 0 lineto 1216 4608 lineto end_ol grestore gsave 17.337067 13.950000 translate 0.035278 -0.035278 scale start_ol 4032 4608 moveto 3520 4608 lineto 3520 841 lineto 1106 4608 lineto 512 4608 lineto 512 0 lineto 1024 0 lineto 1024 3736 lineto 3412 0 lineto 4032 0 lineto 4032 4608 lineto end_ol grestore gsave 17.938200 13.950000 translate 0.035278 -0.035278 scale start_ol 2484 0 moveto 4087 4608 lineto 3459 4608 lineto 2180 708 lineto 824 4608 lineto 190 4608 lineto 1850 0 lineto 2484 0 lineto end_ol grestore gsave 18.497000 13.950000 translate 0.035278 -0.035278 scale start_ol 1216 4608 moveto 640 4608 lineto 640 0 lineto 1216 0 lineto 1216 4608 lineto end_ol grestore gsave 18.734067 13.950000 translate 0.035278 -0.035278 scale start_ol 2240 4087 moveto 3754 4087 lineto 3754 4608 lineto 143 4608 lineto 143 4087 lineto 1664 4087 lineto 1664 0 lineto 2240 0 lineto 2240 4087 lineto end_ol grestore gsave 19.242067 13.950000 translate 0.035278 -0.035278 scale start_ol 1152 2112 moveto 3667 2112 lineto 3667 2624 lineto 1152 2624 lineto 1152 4087 lineto 3762 4087 lineto 3762 4608 lineto 576 4608 lineto 576 0 lineto 3876 0 lineto 3876 521 lineto 1152 521 lineto 1152 2112 lineto end_ol grestore gsave 19.800867 13.950000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 20.037933 13.950000 translate 0.035278 -0.035278 scale start_ol 3073 4445 moveto 2585 4445 lineto 2363 3200 lineto 1571 3200 lineto 1799 4445 lineto 1318 4445 lineto 1090 3200 lineto 323 3200 lineto 323 2752 lineto 1007 2752 lineto 811 1664 lineto 89 1664 lineto 89 1216 lineto 729 1216 lineto 488 0 lineto 969 0 lineto 1217 1216 lineto 2002 1216 lineto 1761 0 lineto 2243 0 lineto 2490 1216 lineto 3231 1216 lineto 3231 1664 lineto 2566 1664 lineto 2762 2752 lineto 3434 2752 lineto 3434 3200 lineto 2845 3200 lineto 3073 4445 lineto 2281 2752 moveto 2085 1664 lineto 1293 1664 lineto 1495 2752 lineto 2281 2752 lineto end_ol grestore gsave 20.503600 13.950000 translate 0.035278 -0.035278 scale start_ol 3200 553 moveto 863 553 lineto 919 907 1116 1133 1654 1445 curveto 2273 1775 lineto 2885 2104 3200 2550 3200 3082 curveto 3200 3442 3051 3778 2790 4010 curveto 2529 4242 2206 4352 1790 4352 curveto 1231 4352 815 4155 573 3785 curveto 418 3551 349 3280 337 2837 curveto 895 2837 lineto 914 3128 952 3304 1028 3444 curveto 1174 3705 1465 3863 1801 3863 curveto 2308 3863 2688 3515 2688 3050 curveto 2688 2708 2479 2414 2080 2194 curveto 1497 1876 lineto 559 1363 287 954 236 0 curveto 3200 0 lineto 3200 553 lineto end_ol grestore gsave 17.500000 9.850000 translate 0.035278 -0.035278 scale start_ol 1330 2071 moveto 1399 2071 lineto 1634 2077 lineto 2287 2077 2624 1820 2624 1324 curveto 2624 804 2265 495 1664 495 curveto 1036 495 729 775 690 1376 curveto 132 1376 lineto 157 1034 221 811 328 619 curveto 557 210 1000 0 1615 0 curveto 2540 0 3136 517 3136 1312 curveto 3136 1847 2914 2144 2375 2316 curveto 2754 2471 2944 2768 2944 3191 curveto 2944 3917 2442 4352 1604 4352 curveto 717 4352 246 3883 227 2976 curveto 785 2976 lineto 791 3227 817 3367 886 3495 curveto 1013 3723 1292 3863 1640 3863 curveto 2134 3863 2432 3595 2432 3157 curveto 2432 2865 2318 2690 2071 2597 curveto 1919 2538 1723 2515 1330 2509 curveto 1330 2071 lineto end_ol grestore gsave 17.965667 9.850000 translate 0.035278 -0.035278 scale start_ol 1696 4352 moveto 1286 4352 914 4180 684 3888 curveto 399 3520 256 2955 256 2176 curveto 256 755 753 0 1696 0 curveto 2627 0 3136 755 3136 2140 curveto 3136 2961 2999 3508 2708 3888 curveto 2478 4186 2112 4352 1696 4352 curveto 1696 3857 moveto 2317 3857 2624 3296 2624 2187 curveto 2624 1013 2323 464 1683 464 curveto 1075 464 768 1036 768 2169 curveto 768 3302 1075 3857 1696 3857 curveto end_ol grestore gsave 18.431333 9.850000 translate 0.035278 -0.035278 scale start_ol 3200 553 moveto 863 553 lineto 919 907 1116 1133 1654 1445 curveto 2273 1775 lineto 2885 2104 3200 2550 3200 3082 curveto 3200 3442 3051 3778 2790 4010 curveto 2529 4242 2206 4352 1790 4352 curveto 1231 4352 815 4155 573 3785 curveto 418 3551 349 3280 337 2837 curveto 895 2837 lineto 914 3128 952 3304 1028 3444 curveto 1174 3705 1465 3863 1801 3863 curveto 2308 3863 2688 3515 2688 3050 curveto 2688 2708 2479 2414 2080 2194 curveto 1497 1876 lineto 559 1363 287 954 236 0 curveto 3200 0 lineto 3200 553 lineto end_ol grestore gsave 18.897000 9.850000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.134067 9.850000 translate 0.035278 -0.035278 scale start_ol 2950 0 moveto 4288 3862 lineto 4288 0 lineto 4800 0 lineto 4800 4608 lineto 4020 4608 lineto 2637 594 lineto 1228 4608 lineto 448 4608 lineto 448 0 lineto 960 0 lineto 960 3862 lineto 2311 0 lineto 2950 0 lineto end_ol grestore gsave 19.828333 9.850000 translate 0.035278 -0.035278 scale start_ol 1722 3328 moveto 809 3328 256 2706 256 1664 curveto 256 622 803 0 1728 0 curveto 2641 0 3200 622 3200 1640 curveto 3200 2712 2660 3328 1722 3328 curveto 1728 2839 moveto 2330 2839 2688 2395 2688 1647 curveto 2688 938 2317 489 1728 489 curveto 1133 489 768 933 768 1664 curveto 768 2390 1133 2839 1728 2839 curveto end_ol grestore gsave 20.294000 9.850000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 3079 3328 lineto 2484 3328 lineto 1546 629 lineto 659 3328 lineto 63 3328 lineto 1229 0 lineto 1806 0 lineto end_ol grestore gsave 20.717333 9.850000 translate 0.035278 -0.035278 scale start_ol 3250 1472 moveto 3250 1959 3212 2251 3117 2488 curveto 2902 3012 2395 3328 1774 3328 curveto 849 3328 253 2650 253 1610 curveto 253 614 830 0 1761 0 curveto 2522 0 3048 397 3181 1062 curveto 2648 1062 lineto 2503 685 2205 489 1780 489 curveto 1445 489 1159 620 982 860 curveto 855 1024 811 1188 805 1472 curveto 3250 1472 lineto 817 1920 moveto 862 2477 1236 2839 1768 2839 curveto 2287 2839 2686 2448 2686 1954 curveto 2686 1943 2686 1932 2680 1920 curveto 817 1920 lineto end_ol grestore gsave 21.183000 9.850000 translate 0.035278 -0.035278 scale start_ol 3136 4608 moveto 2624 4608 lineto 2624 2816 lineto 2403 3151 2051 3328 1610 3328 curveto 753 3328 192 2683 192 1694 curveto 192 645 728 0 1596 0 curveto 2039 0 2347 169 2624 576 curveto 2624 0 lineto 3136 0 lineto 3136 4608 lineto 1680 2833 moveto 2258 2833 2624 2372 2624 1652 curveto 2624 956 2252 495 1686 495 curveto 1096 495 704 961 704 1664 curveto 704 2367 1096 2833 1680 2833 curveto end_ol grestore gsave 21.648667 9.850000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 21.885733 9.850000 translate 0.035278 -0.035278 scale start_ol 2240 4087 moveto 3754 4087 lineto 3754 4608 lineto 143 4608 lineto 143 4087 lineto 1664 4087 lineto 1664 0 lineto 2240 0 lineto 2240 4087 lineto end_ol grestore gsave 22.393733 9.850000 translate 0.035278 -0.035278 scale start_ol 3250 1472 moveto 3250 1959 3212 2251 3117 2488 curveto 2902 3012 2395 3328 1774 3328 curveto 849 3328 253 2650 253 1610 curveto 253 614 830 0 1761 0 curveto 2522 0 3048 397 3181 1062 curveto 2648 1062 lineto 2503 685 2205 489 1780 489 curveto 1445 489 1159 620 982 860 curveto 855 1024 811 1188 805 1472 curveto 3250 1472 lineto 817 1920 moveto 862 2477 1236 2839 1768 2839 curveto 2287 2839 2686 2448 2686 1954 curveto 2686 1943 2686 1932 2680 1920 curveto 817 1920 lineto end_ol grestore gsave 22.859400 9.850000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 2022 lineto 960 2489 1312 2864 1747 2864 curveto 2144 2864 2368 2631 2368 2219 curveto 2368 0 lineto 2880 0 lineto 2880 2022 lineto 2880 2489 3232 2864 3667 2864 curveto 4058 2864 4288 2624 4288 2219 curveto 4288 0 lineto 4800 0 lineto 4800 2427 lineto 4800 3007 4459 3328 3841 3328 curveto 3399 3328 3134 3195 2825 2821 curveto 2634 3176 2374 3328 1954 3328 curveto 1522 3328 1238 3164 960 2766 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 23.553667 9.850000 translate 0.035278 -0.035278 scale start_ol 320 -1407 moveto 832 -1407 lineto 832 495 lineto 1110 152 1419 0 1849 0 curveto 2702 0 3264 645 3264 1634 curveto 3264 2677 2730 3328 1868 3328 curveto 1428 3328 1074 3128 832 2741 curveto 832 3328 lineto 320 3328 lineto 320 -1407 lineto 1773 2833 moveto 2365 2833 2752 2367 2752 1647 curveto 2752 961 2359 495 1773 495 curveto 1206 495 832 956 832 1664 curveto 832 2372 1206 2833 1773 2833 curveto end_ol grestore gsave 24.019333 9.850000 translate 0.035278 -0.035278 scale start_ol 1722 3328 moveto 809 3328 256 2706 256 1664 curveto 256 622 803 0 1728 0 curveto 2641 0 3200 622 3200 1640 curveto 3200 2712 2660 3328 1722 3328 curveto 1728 2839 moveto 2330 2839 2688 2395 2688 1647 curveto 2688 938 2317 489 1728 489 curveto 1133 489 768 933 768 1664 curveto 768 2390 1133 2839 1728 2839 curveto end_ol grestore gsave 24.485000 9.850000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1728 lineto 960 2204 1080 2515 1334 2699 curveto 1498 2820 1657 2858 2024 2864 curveto 2024 3328 lineto 1939 3328 1896 3328 1829 3328 curveto 1501 3328 1252 3153 960 2725 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 24.764400 9.850000 translate 0.035278 -0.035278 scale start_ol 3407 458 moveto 3350 464 3325 464 3293 464 curveto 3109 464 3008 544 3008 683 curveto 3008 2459 lineto 3008 3024 2546 3328 1669 3328 curveto 1153 3328 725 3195 487 2960 curveto 324 2797 256 2617 256 2303 curveto 901 2303 lineto 948 2672 1200 2839 1718 2839 curveto 2217 2839 2496 2675 2496 2382 curveto 2496 2254 lineto 2496 2049 2360 1961 1934 1914 curveto 1173 1826 1056 1803 850 1727 curveto 456 1580 256 1305 256 907 curveto 256 351 682 0 1366 0 curveto 1792 0 2134 145 2515 487 curveto 2552 152 2715 0 3051 0 curveto 3157 0 3239 13 3407 60 curveto 3407 458 lineto 2496 1127 moveto 2496 966 2443 868 2277 735 curveto 2051 556 1778 464 1453 464 curveto 1021 464 768 643 768 948 curveto 768 1265 1014 1427 1605 1502 curveto 2190 1571 2310 1594 2496 1669 curveto 2496 1127 lineto end_ol grestore gsave 25.230067 9.850000 translate 0.035278 -0.035278 scale start_ol 448 3328 moveto 448 0 lineto 960 0 lineto 960 1728 lineto 960 2204 1080 2515 1334 2699 curveto 1498 2820 1657 2858 2024 2864 curveto 2024 3328 lineto 1939 3328 1896 3328 1829 3328 curveto 1501 3328 1252 3153 960 2725 curveto 960 3328 lineto 448 3328 lineto end_ol grestore gsave 25.509467 9.850000 translate 0.035278 -0.035278 scale start_ol 960 3309 moveto 448 3309 lineto 448 0 lineto 960 0 lineto 960 3309 lineto 960 4608 moveto 448 4608 lineto 448 3941 lineto 960 3941 lineto 960 4608 lineto end_ol grestore gsave 25.695733 9.850000 translate 0.035278 -0.035278 scale start_ol 960 4608 moveto 448 4608 lineto 448 0 lineto 960 0 lineto 960 4608 lineto end_ol grestore gsave 25.882000 9.850000 translate 0.035278 -0.035278 scale start_ol 2458 3328 moveto 1540 717 lineto 691 3328 lineto 127 3328 lineto 1248 -38 lineto 1045 -569 lineto 963 -806 843 -896 621 -896 curveto 545 -896 456 -883 342 -858 curveto 342 -1327 lineto 450 -1383 558 -1408 697 -1408 curveto 868 -1408 1052 -1351 1191 -1248 curveto 1356 -1127 1451 -987 1552 -719 curveto 3029 3328 lineto 2458 3328 lineto end_ol grestore showpage kamailio-4.0.4/doc/sip/figures/transaction.eps0000644000000000000000000012045012223032460020041 0ustar rootroot%!PS-Adobe-2.0 EPSF-2.0 %%Title: transaction.dia %%Creator: Dia v0.91 %%CreationDate: Tue Jul 15 15:00:02 2003 %%For: janakj %%Orientation: Portrait %%Magnification: 1.0000 %%BoundingBox: 0 0 413 511 %%BeginSetup %%EndSetup %%EndComments %%BeginProlog [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def /cp {closepath} bind def /c {curveto} bind def /f {fill} bind def /a {arc} bind def /ef {eofill} bind def /ex {exch} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth pop} bind def /tr {translate} bind def /ellipsedict 8 dict def ellipsedict /mtrx matrix put /ellipse { ellipsedict begin /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc savematrix setmatrix end } def /mergeprocs { dup length 3 -1 roll dup length dup 5 1 roll 3 -1 roll add array cvx dup 3 -1 roll 0 exch putinterval dup 4 2 roll putinterval } bind def /dpi_x 300 def /dpi_y 300 def /conicto { /to_y exch def /to_x exch def /conic_cntrl_y exch def /conic_cntrl_x exch def currentpoint /p0_y exch def /p0_x exch def /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def /p2_x p1_x to_x p0_x sub 1 3 div mul add def /p2_y p1_y to_y p0_y sub 1 3 div mul add def p1_x p1_y p2_x p2_y to_x to_y curveto } bind def /start_ol { gsave 1.1 dpi_x div dup scale} bind def /end_ol { closepath fill grestore } bind def 28.346000 -28.346000 scale -15.200000 -24.050000 translate %%EndProlog 0.100000 slw [] 0 sd [] 0 sd 0 slc 0.000000 0.000000 0.000000 srgb n 16.000000 7.000000 m 16.000000 24.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 7.000000 m 23.000000 24.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 16.000000 9.000000 m 22.200000 9.000000 l s 0 slj n 22.200000 9.400000 m 23.000000 9.000000 l 22.200000 8.600000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 11.000000 m 16.800000 11.000000 l s 0 slj n 16.800000 10.600000 m 16.000000 11.000000 l 16.800000 11.400000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 13.000000 m 16.800000 13.000000 l s 0 slj n 16.800000 12.600000 m 16.000000 13.000000 l 16.800000 13.400000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 15.000000 m 16.800000 15.000000 l s 0 slj n 16.800000 14.600000 m 16.000000 15.000000 l 16.800000 15.400000 l f gsave 18.650000 8.550000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 18.819333 8.550000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 19.259600 8.550000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 2972 3392 lineto 2516 3392 lineto 1585 521 lineto 599 3392 lineto 138 3392 lineto 1346 0 lineto 1806 0 lineto end_ol grestore gsave 19.666000 8.550000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 19.835333 8.550000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 20.207867 8.550000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 18.135000 10.575000 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 18.473667 10.575000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 18.812333 10.575000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.151000 10.575000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.320333 10.575000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 19.692867 10.575000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 19.896067 10.575000 translate 0.035278 -0.035278 scale start_ol 1788 2432 moveto 1120 533 lineto 502 2432 lineto 92 2432 lineto 908 -16 lineto 760 -403 lineto 700 -575 613 -640 452 -640 curveto 396 -640 332 -631 249 -612 curveto 249 -963 lineto 327 -1005 406 -1024 507 -1024 curveto 631 -1024 765 -982 866 -908 curveto 986 -819 1055 -717 1129 -521 curveto 2203 2432 lineto 1788 2432 lineto end_ol grestore gsave 20.200867 10.575000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 20.336333 10.575000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 20.675000 10.575000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 18.100000 12.550000 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 18.438667 12.550000 translate 0.035278 -0.035278 scale start_ol 1821 1738 moveto 2136 1929 2240 2086 2240 2378 curveto 2240 2861 1835 3200 1248 3200 curveto 666 3200 256 2861 256 2378 curveto 256 2090 360 1934 670 1738 curveto 307 1567 128 1308 128 966 curveto 128 395 563 0 1184 0 curveto 1805 0 2240 395 2240 961 curveto 2240 1308 2103 1567 1821 1738 curveto 1248 2838 moveto 1619 2838 1856 2657 1856 2373 curveto 1856 2101 1614 1920 1248 1920 curveto 882 1920 640 2101 640 2377 curveto 640 2657 882 2838 1248 2838 curveto 1184 1536 moveto 1584 1536 1856 1300 1856 951 curveto 1856 598 1584 362 1175 362 curveto 784 362 512 602 512 951 curveto 512 1300 784 1536 1184 1536 curveto end_ol grestore gsave 18.777333 12.550000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.116000 12.550000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.285333 12.550000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 19.725600 12.550000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 19.861067 12.550000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 20.199733 12.550000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 20.538400 12.550000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 20.673867 12.550000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 21.012533 12.550000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 18.700000 14.650000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 19.038667 14.650000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.377333 14.650000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.716000 14.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.885333 14.650000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 20.359467 14.650000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 22.450000 7.000000 m 23.475000 7.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 15.500000 7.000000 m 16.500000 7.000000 l s gsave 15.200000 6.700000 translate 0.035278 -0.035278 scale start_ol 2977 2327 moveto 2845 3043 2428 3392 1702 3392 curveto 1258 3392 900 3254 655 2988 curveto 355 2668 192 2207 192 1683 curveto 192 1150 360 693 673 377 curveto 927 120 1254 0 1685 0 curveto 2492 0 2946 428 3046 1288 curveto 2604 1288 lineto 2567 1069 2521 920 2452 793 curveto 2314 530 2028 381 1668 381 curveto 1000 381 576 889 576 1687 curveto 576 2507 982 3011 1631 3011 curveto 1903 3011 2157 2932 2295 2809 curveto 2419 2700 2488 2564 2539 2327 curveto 2977 2327 lineto end_ol grestore gsave 15.640267 6.700000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 15.978933 6.700000 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 16.114400 6.700000 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 16.249867 6.700000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 16.588533 6.700000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 22.050000 6.687500 translate 0.035278 -0.035278 scale start_ol 2977 2327 moveto 2845 3043 2428 3392 1702 3392 curveto 1258 3392 900 3254 655 2988 curveto 355 2668 192 2207 192 1683 curveto 192 1150 360 693 673 377 curveto 927 120 1254 0 1685 0 curveto 2492 0 2946 428 3046 1288 curveto 2604 1288 lineto 2567 1069 2521 920 2452 793 curveto 2314 530 2028 381 1668 381 curveto 1000 381 576 889 576 1687 curveto 576 2507 982 3011 1631 3011 curveto 1903 3011 2157 2932 2295 2809 curveto 2419 2700 2488 2564 2539 2327 curveto 2977 2327 lineto end_ol grestore gsave 22.490267 6.687500 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 22.828933 6.687500 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 22.964400 6.687500 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 23.099867 6.687500 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 23.438533 6.687500 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 21.000000 m 16.800000 21.000000 l s 0 slj n 16.800000 20.600000 m 16.000000 21.000000 l 16.800000 21.400000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 16.000000 23.000000 m 22.200000 23.000000 l s 0 slj n 22.200000 23.400000 m 23.000000 23.000000 l 22.200000 22.600000 l f gsave 19.000000 20.000000 translate 0.035278 -0.035278 scale start_ol 384 0 moveto 1855 0 lineto 2163 0 2391 88 2566 282 curveto 2727 458 2816 698 2816 962 curveto 2816 1369 2632 1614 2204 1780 curveto 2478 1925 2624 2178 2624 2525 curveto 2624 2773 2537 2994 2370 3153 curveto 2204 3317 1985 3392 1679 3392 curveto 384 3392 lineto 384 0 lineto 768 1920 moveto 768 3011 lineto 1581 3011 lineto 1816 3011 1947 2978 2059 2889 curveto 2177 2795 2240 2654 2240 2466 curveto 2240 2282 2177 2136 2059 2042 curveto 1947 1953 1816 1920 1581 1920 curveto 768 1920 lineto 768 381 moveto 768 1536 lineto 1823 1536 lineto 2032 1536 2172 1485 2274 1375 curveto 2376 1269 2432 1122 2432 956 curveto 2432 795 2376 648 2274 542 curveto 2172 432 2032 381 1823 381 curveto 768 381 lineto end_ol grestore gsave 19.406400 20.000000 translate 0.035278 -0.035278 scale start_ol 1728 1331 moveto 2991 3392 lineto 2479 3392 lineto 1542 1740 lineto 579 3392 lineto 49 3392 lineto 1344 1331 lineto 1344 0 lineto 1728 0 lineto 1728 1331 lineto end_ol grestore gsave 19.812800 20.000000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 18.000000 22.000000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 18.338667 22.000000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 18.677333 22.000000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.016000 22.000000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.185333 22.000000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 19.659467 22.000000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 23.800000 8.487500 m 25.000000 8.837500 23.400000 11.587500 24.500000 11.987500 c s 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 24.400000 11.987500 m 23.350000 12.287500 25.050000 14.987500 23.800000 15.487500 c s 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 23.750000 20.437500 m 24.650000 20.837500 23.400000 21.607500 24.500000 22.007500 c s 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 24.400000 22.007500 m 23.350000 22.307500 24.600000 22.987500 23.850000 23.587500 c s gsave 24.900000 12.187500 translate 0.035278 -0.035278 scale start_ol 1984 3689 moveto 3333 3689 lineto 3333 4160 lineto 117 4160 lineto 117 3689 lineto 1472 3689 lineto 1472 0 lineto 1984 0 lineto 1984 3689 lineto end_ol grestore gsave 25.365667 12.187500 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1561 lineto 896 1992 1003 2273 1229 2440 curveto 1376 2549 1517 2583 1844 2589 curveto 1844 3008 lineto 1763 3008 1723 3008 1659 3008 curveto 1347 3008 1110 2849 832 2463 curveto 832 3008 lineto 384 3008 lineto end_ol grestore gsave 25.619667 12.187500 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 26.043000 12.187500 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1606 lineto 896 2200 1211 2589 1696 2589 curveto 2067 2589 2304 2367 2304 2017 curveto 2304 0 lineto 2816 0 lineto 2816 2210 lineto 2816 2695 2437 3008 1847 3008 curveto 1392 3008 1100 2837 832 2422 curveto 832 3008 lineto 384 3008 lineto end_ol grestore gsave 26.466333 12.187500 translate 0.035278 -0.035278 scale start_ol 2624 2129 moveto 2624 2691 2211 3008 1476 3008 curveto 736 3008 256 2675 256 2161 curveto 256 1728 496 1521 1204 1363 curveto 1649 1262 lineto 1981 1188 2112 1077 2112 876 curveto 2112 617 1835 442 1421 442 curveto 1167 442 952 510 833 624 curveto 759 702 725 780 697 973 curveto 256 973 lineto 256 315 638 0 1409 0 curveto 2152 0 2624 344 2624 878 curveto 2624 1291 2373 1518 1779 1651 curveto 1322 1751 lineto 934 1836 768 1952 768 2148 curveto 768 2402 1038 2566 1465 2566 curveto 1886 2566 2112 2415 2112 2129 curveto 2624 2129 lineto end_ol grestore gsave 26.847333 12.187500 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 27.270667 12.187500 translate 0.035278 -0.035278 scale start_ol 2697 1973 moveto 2674 2249 2612 2428 2498 2585 curveto 2293 2851 1934 3008 1519 3008 curveto 716 3008 192 2409 192 1477 curveto 192 573 704 0 1512 0 curveto 2224 0 2673 406 2730 1098 curveto 2256 1098 lineto 2177 661 1934 442 1534 442 curveto 1014 442 704 832 704 1478 curveto 704 2160 1009 2566 1522 2566 curveto 1917 2566 2166 2353 2222 1973 curveto 2697 1973 lineto end_ol grestore gsave 27.651667 12.187500 translate 0.035278 -0.035278 scale start_ol 1509 3008 moveto 1024 3008 lineto 1024 3835 lineto 512 3835 lineto 512 3008 lineto 111 3008 lineto 111 2618 lineto 512 2618 lineto 512 468 lineto 512 169 724 0 1108 0 curveto 1226 0 1344 11 1509 40 curveto 1509 435 lineto 1447 419 1374 419 1283 419 curveto 1080 419 1024 473 1024 674 curveto 1024 2618 lineto 1509 2618 lineto 1509 3008 lineto end_ol grestore gsave 27.863333 12.187500 translate 0.035278 -0.035278 scale start_ol 896 2987 moveto 384 2987 lineto 384 0 lineto 896 0 lineto 896 2987 lineto 896 4160 moveto 384 4160 lineto 384 3557 lineto 896 3557 lineto 896 4160 lineto end_ol grestore gsave 28.032667 12.187500 translate 0.035278 -0.035278 scale start_ol 1530 3008 moveto 697 3008 192 2446 192 1504 curveto 192 562 691 0 1536 0 curveto 2370 0 2880 562 2880 1483 curveto 2880 2451 2387 3008 1530 3008 curveto 1536 2566 moveto 2057 2566 2368 2165 2368 1488 curveto 2368 848 2046 442 1536 442 curveto 1020 442 704 843 704 1504 curveto 704 2160 1020 2566 1536 2566 curveto end_ol grestore gsave 28.456000 12.187500 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1606 lineto 896 2200 1211 2589 1696 2589 curveto 2067 2589 2304 2367 2304 2017 curveto 2304 0 lineto 2816 0 lineto 2816 2210 lineto 2816 2695 2437 3008 1847 3008 curveto 1392 3008 1100 2837 832 2422 curveto 832 3008 lineto 384 3008 lineto end_ol grestore gsave 28.879333 12.187500 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 29.091000 12.187500 translate 0.035278 -0.035278 scale start_ol 2738 4005 moveto 2303 4005 lineto 2106 2880 lineto 1400 2880 lineto 1603 4005 lineto 1174 4005 lineto 971 2880 lineto 288 2880 lineto 288 2496 lineto 898 2496 lineto 723 1472 lineto 79 1472 lineto 79 1088 lineto 649 1088 lineto 435 0 lineto 864 0 lineto 1084 1088 lineto 1784 1088 lineto 1569 0 lineto 1998 0 lineto 2218 1088 lineto 2879 1088 lineto 2879 1472 lineto 2286 1472 lineto 2461 2496 lineto 3059 2496 lineto 3059 2880 lineto 2535 2880 lineto 2738 4005 lineto 2032 2496 moveto 1857 1472 lineto 1152 1472 lineto 1332 2496 lineto 2032 2496 lineto end_ol grestore gsave 29.514333 12.187500 translate 0.035278 -0.035278 scale start_ol 1472 2899 moveto 1472 0 lineto 1984 0 lineto 1984 4070 lineto 1647 4070 lineto 1471 3445 1358 3359 586 3261 curveto 586 2899 lineto 1472 2899 lineto end_ol grestore gsave 24.900000 22.237500 translate 0.035278 -0.035278 scale start_ol 1984 3689 moveto 3333 3689 lineto 3333 4160 lineto 117 4160 lineto 117 3689 lineto 1472 3689 lineto 1472 0 lineto 1984 0 lineto 1984 3689 lineto end_ol grestore gsave 25.365667 22.237500 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1561 lineto 896 1992 1003 2273 1229 2440 curveto 1376 2549 1517 2583 1844 2589 curveto 1844 3008 lineto 1763 3008 1723 3008 1659 3008 curveto 1347 3008 1110 2849 832 2463 curveto 832 3008 lineto 384 3008 lineto end_ol grestore gsave 25.619667 22.237500 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 26.043000 22.237500 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1606 lineto 896 2200 1211 2589 1696 2589 curveto 2067 2589 2304 2367 2304 2017 curveto 2304 0 lineto 2816 0 lineto 2816 2210 lineto 2816 2695 2437 3008 1847 3008 curveto 1392 3008 1100 2837 832 2422 curveto 832 3008 lineto 384 3008 lineto end_ol grestore gsave 26.466333 22.237500 translate 0.035278 -0.035278 scale start_ol 2624 2129 moveto 2624 2691 2211 3008 1476 3008 curveto 736 3008 256 2675 256 2161 curveto 256 1728 496 1521 1204 1363 curveto 1649 1262 lineto 1981 1188 2112 1077 2112 876 curveto 2112 617 1835 442 1421 442 curveto 1167 442 952 510 833 624 curveto 759 702 725 780 697 973 curveto 256 973 lineto 256 315 638 0 1409 0 curveto 2152 0 2624 344 2624 878 curveto 2624 1291 2373 1518 1779 1651 curveto 1322 1751 lineto 934 1836 768 1952 768 2148 curveto 768 2402 1038 2566 1465 2566 curveto 1886 2566 2112 2415 2112 2129 curveto 2624 2129 lineto end_ol grestore gsave 26.847333 22.237500 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 27.270667 22.237500 translate 0.035278 -0.035278 scale start_ol 2697 1973 moveto 2674 2249 2612 2428 2498 2585 curveto 2293 2851 1934 3008 1519 3008 curveto 716 3008 192 2409 192 1477 curveto 192 573 704 0 1512 0 curveto 2224 0 2673 406 2730 1098 curveto 2256 1098 lineto 2177 661 1934 442 1534 442 curveto 1014 442 704 832 704 1478 curveto 704 2160 1009 2566 1522 2566 curveto 1917 2566 2166 2353 2222 1973 curveto 2697 1973 lineto end_ol grestore gsave 27.651667 22.237500 translate 0.035278 -0.035278 scale start_ol 1509 3008 moveto 1024 3008 lineto 1024 3835 lineto 512 3835 lineto 512 3008 lineto 111 3008 lineto 111 2618 lineto 512 2618 lineto 512 468 lineto 512 169 724 0 1108 0 curveto 1226 0 1344 11 1509 40 curveto 1509 435 lineto 1447 419 1374 419 1283 419 curveto 1080 419 1024 473 1024 674 curveto 1024 2618 lineto 1509 2618 lineto 1509 3008 lineto end_ol grestore gsave 27.863333 22.237500 translate 0.035278 -0.035278 scale start_ol 896 2987 moveto 384 2987 lineto 384 0 lineto 896 0 lineto 896 2987 lineto 896 4160 moveto 384 4160 lineto 384 3557 lineto 896 3557 lineto 896 4160 lineto end_ol grestore gsave 28.032667 22.237500 translate 0.035278 -0.035278 scale start_ol 1530 3008 moveto 697 3008 192 2446 192 1504 curveto 192 562 691 0 1536 0 curveto 2370 0 2880 562 2880 1483 curveto 2880 2451 2387 3008 1530 3008 curveto 1536 2566 moveto 2057 2566 2368 2165 2368 1488 curveto 2368 848 2046 442 1536 442 curveto 1020 442 704 843 704 1504 curveto 704 2160 1020 2566 1536 2566 curveto end_ol grestore gsave 28.456000 22.237500 translate 0.035278 -0.035278 scale start_ol 384 3008 moveto 384 0 lineto 896 0 lineto 896 1606 lineto 896 2200 1211 2589 1696 2589 curveto 2067 2589 2304 2367 2304 2017 curveto 2304 0 lineto 2816 0 lineto 2816 2210 lineto 2816 2695 2437 3008 1847 3008 curveto 1392 3008 1100 2837 832 2422 curveto 832 3008 lineto 384 3008 lineto end_ol grestore gsave 28.879333 22.237500 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 29.091000 22.237500 translate 0.035278 -0.035278 scale start_ol 2738 4005 moveto 2303 4005 lineto 2106 2880 lineto 1400 2880 lineto 1603 4005 lineto 1174 4005 lineto 971 2880 lineto 288 2880 lineto 288 2496 lineto 898 2496 lineto 723 1472 lineto 79 1472 lineto 79 1088 lineto 649 1088 lineto 435 0 lineto 864 0 lineto 1084 1088 lineto 1784 1088 lineto 1569 0 lineto 1998 0 lineto 2218 1088 lineto 2879 1088 lineto 2879 1472 lineto 2286 1472 lineto 2461 2496 lineto 3059 2496 lineto 3059 2880 lineto 2535 2880 lineto 2738 4005 lineto 2032 2496 moveto 1857 1472 lineto 1152 1472 lineto 1332 2496 lineto 2032 2496 lineto end_ol grestore gsave 29.514333 22.237500 translate 0.035278 -0.035278 scale start_ol 2880 499 moveto 742 499 lineto 793 822 974 1029 1466 1313 curveto 2032 1614 lineto 2592 1916 2880 2323 2880 2808 curveto 2880 3137 2744 3444 2507 3656 curveto 2269 3868 1976 3968 1597 3968 curveto 1088 3968 709 3788 489 3452 curveto 347 3238 285 2991 274 2587 curveto 771 2587 lineto 788 2854 822 3015 890 3143 curveto 1019 3382 1279 3526 1578 3526 curveto 2029 3526 2368 3208 2368 2784 curveto 2368 2472 2182 2204 1826 2003 curveto 1307 1713 lineto 471 1244 228 870 183 0 curveto 2880 0 lineto 2880 499 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 16.000000 17.000000 m 22.200000 17.000000 l s 0 slj n 22.200000 17.400000 m 23.000000 17.000000 l 22.200000 16.600000 l f gsave 18.850000 16.600000 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 19.256400 16.600000 translate 0.035278 -0.035278 scale start_ol 2977 2327 moveto 2845 3043 2428 3392 1702 3392 curveto 1258 3392 900 3254 655 2988 curveto 355 2668 192 2207 192 1683 curveto 192 1150 360 693 673 377 curveto 927 120 1254 0 1685 0 curveto 2492 0 2946 428 3046 1288 curveto 2604 1288 lineto 2567 1069 2521 920 2452 793 curveto 2314 530 2028 381 1668 381 curveto 1000 381 576 889 576 1687 curveto 576 2507 982 3011 1631 3011 curveto 1903 3011 2157 2932 2295 2809 curveto 2419 2700 2488 2564 2539 2327 curveto 2977 2327 lineto end_ol grestore gsave 19.696667 16.600000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore showpage kamailio-4.0.4/doc/sip/figures/dialog.eps0000644000000000000000000011372112223032460016756 0ustar rootroot%!PS-Adobe-2.0 EPSF-2.0 %%Title: dialog.dia %%Creator: Dia v0.91 %%CreationDate: Tue Jul 15 14:59:02 2003 %%For: janakj %%Orientation: Portrait %%Magnification: 1.0000 %%BoundingBox: 0 0 410 511 %%BeginSetup %%EndSetup %%EndComments %%BeginProlog [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def /cp {closepath} bind def /c {curveto} bind def /f {fill} bind def /a {arc} bind def /ef {eofill} bind def /ex {exch} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth pop} bind def /tr {translate} bind def /ellipsedict 8 dict def ellipsedict /mtrx matrix put /ellipse { ellipsedict begin /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc savematrix setmatrix end } def /mergeprocs { dup length 3 -1 roll dup length dup 5 1 roll 3 -1 roll add array cvx dup 3 -1 roll 0 exch putinterval dup 4 2 roll putinterval } bind def /dpi_x 300 def /dpi_y 300 def /conicto { /to_y exch def /to_x exch def /conic_cntrl_y exch def /conic_cntrl_x exch def currentpoint /p0_y exch def /p0_x exch def /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def /p2_x p1_x to_x p0_x sub 1 3 div mul add def /p2_y p1_y to_y p0_y sub 1 3 div mul add def p1_x p1_y p2_x p2_y to_x to_y curveto } bind def /start_ol { gsave 1.1 dpi_x div dup scale} bind def /end_ol { closepath fill grestore } bind def 28.346000 -28.346000 scale -15.200000 -24.050000 translate %%EndProlog 0.100000 slw [] 0 sd [] 0 sd 0 slc 0.000000 0.000000 0.000000 srgb n 16.000000 7.000000 m 16.000000 24.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 7.000000 m 23.000000 24.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 16.000000 9.000000 m 22.200000 9.000000 l s 0 slj n 22.200000 9.400000 m 23.000000 9.000000 l 22.200000 8.600000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 11.000000 m 16.800000 11.000000 l s 0 slj n 16.800000 10.600000 m 16.000000 11.000000 l 16.800000 11.400000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 13.000000 m 16.800000 13.000000 l s 0 slj n 16.800000 12.600000 m 16.000000 13.000000 l 16.800000 13.400000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 15.000000 m 16.800000 15.000000 l s 0 slj n 16.800000 14.600000 m 16.000000 15.000000 l 16.800000 15.400000 l f gsave 18.650000 8.550000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 18.819333 8.550000 translate 0.035278 -0.035278 scale start_ol 2944 3392 moveto 2560 3392 lineto 2560 619 lineto 765 3392 lineto 320 3392 lineto 320 0 lineto 704 0 lineto 704 2750 lineto 2480 0 lineto 2944 0 lineto 2944 3392 lineto end_ol grestore gsave 19.259600 8.550000 translate 0.035278 -0.035278 scale start_ol 1806 0 moveto 2972 3392 lineto 2516 3392 lineto 1585 521 lineto 599 3392 lineto 138 3392 lineto 1346 0 lineto 1806 0 lineto end_ol grestore gsave 19.666000 8.550000 translate 0.035278 -0.035278 scale start_ol 896 3392 moveto 448 3392 lineto 448 0 lineto 896 0 lineto 896 3392 lineto end_ol grestore gsave 19.835333 8.550000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 20.207867 8.550000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 18.135000 10.575000 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 18.473667 10.575000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 18.812333 10.575000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.151000 10.575000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.320333 10.575000 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 19.692867 10.575000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 19.896067 10.575000 translate 0.035278 -0.035278 scale start_ol 1788 2432 moveto 1120 533 lineto 502 2432 lineto 92 2432 lineto 908 -16 lineto 760 -403 lineto 700 -575 613 -640 452 -640 curveto 396 -640 332 -631 249 -612 curveto 249 -963 lineto 327 -1005 406 -1024 507 -1024 curveto 631 -1024 765 -982 866 -908 curveto 986 -819 1055 -717 1129 -521 curveto 2203 2432 lineto 1788 2432 lineto end_ol grestore gsave 20.200867 10.575000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 20.336333 10.575000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 20.675000 10.575000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 18.100000 12.550000 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 18.438667 12.550000 translate 0.035278 -0.035278 scale start_ol 1821 1738 moveto 2136 1929 2240 2086 2240 2378 curveto 2240 2861 1835 3200 1248 3200 curveto 666 3200 256 2861 256 2378 curveto 256 2090 360 1934 670 1738 curveto 307 1567 128 1308 128 966 curveto 128 395 563 0 1184 0 curveto 1805 0 2240 395 2240 961 curveto 2240 1308 2103 1567 1821 1738 curveto 1248 2838 moveto 1619 2838 1856 2657 1856 2373 curveto 1856 2101 1614 1920 1248 1920 curveto 882 1920 640 2101 640 2377 curveto 640 2657 882 2838 1248 2838 curveto 1184 1536 moveto 1584 1536 1856 1300 1856 951 curveto 1856 598 1584 362 1175 362 curveto 784 362 512 602 512 951 curveto 512 1300 784 1536 1184 1536 curveto end_ol grestore gsave 18.777333 12.550000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.116000 12.550000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.285333 12.550000 translate 0.035278 -0.035278 scale start_ol 832 1472 moveto 1984 1472 lineto 2382 1472 2560 1285 2560 863 curveto 2560 558 lineto 2560 347 2586 141 2627 0 curveto 3008 0 lineto 3008 108 lineto 3008 220 3008 342 3008 797 curveto 3008 1359 2913 1528 2517 1687 curveto 2867 1872 3008 2107 3008 2491 curveto 3008 3073 2646 3392 1990 3392 curveto 448 3392 lineto 448 0 lineto 832 0 lineto 832 1472 lineto 832 1856 moveto 832 3011 lineto 1889 3011 lineto 2133 3011 2274 2974 2382 2882 curveto 2499 2786 2560 2634 2560 2431 curveto 2560 2035 2353 1856 1889 1856 curveto 832 1856 lineto end_ol grestore gsave 19.725600 12.550000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 19.861067 12.550000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 20.199733 12.550000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 20.538400 12.550000 translate 0.035278 -0.035278 scale start_ol 704 2439 moveto 320 2439 lineto 320 0 lineto 704 0 lineto 704 2439 lineto 704 3392 moveto 320 3392 lineto 320 2905 lineto 704 2905 lineto 704 3392 lineto end_ol grestore gsave 20.673867 12.550000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 21.012533 12.550000 translate 0.035278 -0.035278 scale start_ol 1920 2432 moveto 1920 2016 lineto 1723 2299 1485 2432 1171 2432 curveto 549 2432 128 1930 128 1194 curveto 128 822 238 519 443 303 curveto 630 113 897 0 1160 0 curveto 1476 0 1695 128 1920 429 curveto 1920 297 lineto 1920 -49 1876 -259 1773 -401 curveto 1666 -552 1455 -640 1206 -640 curveto 1020 -640 854 -589 741 -496 curveto 649 -420 609 -348 585 -189 curveto 203 -189 lineto 246 -712 611 -1024 1194 -1024 curveto 1564 -1024 1882 -908 2043 -712 curveto 2233 -488 2304 -181 2304 392 curveto 2304 2432 lineto 1920 2432 lineto 1221 2075 moveto 1661 2075 1920 1755 1920 1203 curveto 1920 677 1656 357 1226 357 curveto 781 357 512 681 512 1216 curveto 512 1747 786 2075 1221 2075 curveto end_ol grestore gsave 18.700000 14.650000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 19.038667 14.650000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.377333 14.650000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.716000 14.650000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.885333 14.650000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 20.359467 14.650000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 22.450000 7.000000 m 23.475000 7.000000 l s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 15.500000 7.000000 m 16.500000 7.000000 l s gsave 15.200000 6.700000 translate 0.035278 -0.035278 scale start_ol 2977 2327 moveto 2845 3043 2428 3392 1702 3392 curveto 1258 3392 900 3254 655 2988 curveto 355 2668 192 2207 192 1683 curveto 192 1150 360 693 673 377 curveto 927 120 1254 0 1685 0 curveto 2492 0 2946 428 3046 1288 curveto 2604 1288 lineto 2567 1069 2521 920 2452 793 curveto 2314 530 2028 381 1668 381 curveto 1000 381 576 889 576 1687 curveto 576 2507 982 3011 1631 3011 curveto 1903 3011 2157 2932 2295 2809 curveto 2419 2700 2488 2564 2539 2327 curveto 2977 2327 lineto end_ol grestore gsave 15.640267 6.700000 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 15.978933 6.700000 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 16.114400 6.700000 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 16.249867 6.700000 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 16.588533 6.700000 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 22.050000 6.687500 translate 0.035278 -0.035278 scale start_ol 2977 2327 moveto 2845 3043 2428 3392 1702 3392 curveto 1258 3392 900 3254 655 2988 curveto 355 2668 192 2207 192 1683 curveto 192 1150 360 693 673 377 curveto 927 120 1254 0 1685 0 curveto 2492 0 2946 428 3046 1288 curveto 2604 1288 lineto 2567 1069 2521 920 2452 793 curveto 2314 530 2028 381 1668 381 curveto 1000 381 576 889 576 1687 curveto 576 2507 982 3011 1631 3011 curveto 1903 3011 2157 2932 2295 2809 curveto 2419 2700 2488 2564 2539 2327 curveto 2977 2327 lineto end_ol grestore gsave 22.490267 6.687500 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 22.828933 6.687500 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 22.964400 6.687500 translate 0.035278 -0.035278 scale start_ol 704 3392 moveto 320 3392 lineto 320 0 lineto 704 0 lineto 704 3392 lineto end_ol grestore gsave 23.099867 6.687500 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore gsave 23.438533 6.687500 translate 0.035278 -0.035278 scale start_ol 2364 1088 moveto 2364 1441 2336 1652 2267 1824 curveto 2110 2203 1742 2432 1290 2432 curveto 617 2432 184 1941 184 1187 curveto 184 453 604 0 1281 0 curveto 1834 0 2216 293 2313 783 curveto 1926 783 lineto 1820 503 1604 357 1295 357 curveto 1051 357 843 454 714 633 curveto 622 755 590 877 585 1088 curveto 2364 1088 lineto 594 1408 moveto 627 1812 899 2075 1286 2075 curveto 1663 2075 1954 1792 1954 1433 curveto 1954 1425 1954 1417 1949 1408 curveto 594 1408 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slc n 23.000000 21.000000 m 16.800000 21.000000 l s 0 slj n 16.800000 20.600000 m 16.000000 21.000000 l 16.800000 21.400000 l f 0.100000 slw [] 0 sd [] 0 sd 0 slc n 16.000000 23.000000 m 22.200000 23.000000 l s 0 slj n 22.200000 23.400000 m 23.000000 23.000000 l 22.200000 22.600000 l f gsave 19.000000 20.000000 translate 0.035278 -0.035278 scale start_ol 384 0 moveto 1855 0 lineto 2163 0 2391 88 2566 282 curveto 2727 458 2816 698 2816 962 curveto 2816 1369 2632 1614 2204 1780 curveto 2478 1925 2624 2178 2624 2525 curveto 2624 2773 2537 2994 2370 3153 curveto 2204 3317 1985 3392 1679 3392 curveto 384 3392 lineto 384 0 lineto 768 1920 moveto 768 3011 lineto 1581 3011 lineto 1816 3011 1947 2978 2059 2889 curveto 2177 2795 2240 2654 2240 2466 curveto 2240 2282 2177 2136 2059 2042 curveto 1947 1953 1816 1920 1581 1920 curveto 768 1920 lineto 768 381 moveto 768 1536 lineto 1823 1536 lineto 2032 1536 2172 1485 2274 1375 curveto 2376 1269 2432 1122 2432 956 curveto 2432 795 2376 648 2274 542 curveto 2172 432 2032 381 1823 381 curveto 768 381 lineto end_ol grestore gsave 19.406400 20.000000 translate 0.035278 -0.035278 scale start_ol 1728 1331 moveto 2991 3392 lineto 2479 3392 lineto 1542 1740 lineto 579 3392 lineto 49 3392 lineto 1344 1331 lineto 1344 0 lineto 1728 0 lineto 1728 1331 lineto end_ol grestore gsave 19.812800 20.000000 translate 0.035278 -0.035278 scale start_ol 768 1536 moveto 2597 1536 lineto 2597 1920 lineto 768 1920 lineto 768 3011 lineto 2666 3011 lineto 2666 3392 lineto 384 3392 lineto 384 0 lineto 2749 0 lineto 2749 381 lineto 768 381 lineto 768 1536 lineto end_ol grestore gsave 18.000000 22.000000 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore gsave 18.338667 22.000000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 18.677333 22.000000 translate 0.035278 -0.035278 scale start_ol 1248 3200 moveto 948 3200 674 3073 506 2859 curveto 297 2588 192 2173 192 1600 curveto 192 555 556 0 1248 0 curveto 1931 0 2304 555 2304 1574 curveto 2304 2177 2204 2579 1990 2859 curveto 1822 3078 1553 3200 1248 3200 curveto 1248 2838 moveto 1698 2838 1920 2425 1920 1608 curveto 1920 743 1702 339 1239 339 curveto 798 339 576 761 576 1595 curveto 576 2429 798 2838 1248 2838 curveto end_ol grestore gsave 19.016000 22.000000 translate 0.035278 -0.035278 scale start_ol end_ol grestore gsave 19.185333 22.000000 translate 0.035278 -0.035278 scale start_ol 1787 3392 moveto 837 3392 192 2708 192 1696 curveto 192 684 837 0 1792 0 curveto 2192 0 2551 120 2819 342 curveto 3178 639 3392 1141 3392 1669 curveto 3392 2713 2760 3392 1787 3392 curveto 1787 3011 moveto 2529 3011 3008 2485 3008 1678 curveto 3008 907 2515 381 1792 381 curveto 1064 381 576 907 576 1696 curveto 576 2485 1064 3011 1787 3011 curveto end_ol grestore gsave 19.659467 22.000000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 23.800000 8.487500 m 25.000000 8.837500 23.400000 11.587500 24.500000 11.987500 c s 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 24.400000 11.987500 m 23.350000 12.287500 25.050000 14.987500 23.800000 15.487500 c s 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 23.750000 20.437500 m 24.650000 20.837500 23.400000 21.607500 24.500000 22.007500 c s 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 24.400000 22.007500 m 23.350000 22.307500 24.600000 22.987500 23.850000 23.587500 c s 0.100000 slw [] 0 sd [] 0 sd 0 slc n 16.000000 17.000000 m 22.200000 17.000000 l s 0 slj n 22.200000 17.400000 m 23.000000 17.000000 l 22.200000 16.600000 l f gsave 18.850000 16.600000 translate 0.035278 -0.035278 scale start_ol 2184 1024 moveto 2530 0 lineto 3009 0 lineto 1829 3392 lineto 1276 3392 lineto 78 0 lineto 535 0 lineto 889 1024 lineto 2184 1024 lineto 2064 1408 moveto 995 1408 lineto 1548 2933 lineto 2064 1408 lineto end_ol grestore gsave 19.256400 16.600000 translate 0.035278 -0.035278 scale start_ol 2977 2327 moveto 2845 3043 2428 3392 1702 3392 curveto 1258 3392 900 3254 655 2988 curveto 355 2668 192 2207 192 1683 curveto 192 1150 360 693 673 377 curveto 927 120 1254 0 1685 0 curveto 2492 0 2946 428 3046 1288 curveto 2604 1288 lineto 2567 1069 2521 920 2452 793 curveto 2314 530 2028 381 1668 381 curveto 1000 381 576 889 576 1687 curveto 576 2507 982 3011 1631 3011 curveto 1903 3011 2157 2932 2295 2809 curveto 2419 2700 2488 2564 2539 2327 curveto 2977 2327 lineto end_ol grestore gsave 19.696667 16.600000 translate 0.035278 -0.035278 scale start_ol 768 1187 moveto 1316 1740 lineto 2501 0 lineto 3007 0 lineto 1625 2010 lineto 2994 3392 lineto 2441 3392 lineto 768 1675 lineto 768 3392 lineto 384 3392 lineto 384 0 lineto 768 0 lineto 768 1187 lineto end_ol grestore 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 26.000000 7.550000 m 27.200000 7.900000 25.950000 15.137500 27.050000 15.537500 c s 0.100000 slw [] 0 sd [] 0 sd 0 slj 0 slc n 27.000000 15.537500 m 25.950000 15.837500 27.250000 23.050000 26.000000 23.550000 c s gsave 27.600000 15.737500 translate 0.035278 -0.035278 scale start_ol 512 0 moveto 2099 0 lineto 3138 0 3776 787 3776 2083 curveto 3776 3373 3144 4160 2099 4160 curveto 512 4160 lineto 512 0 lineto 1024 471 moveto 1024 3689 lineto 2007 3689 lineto 2830 3689 3264 3137 3264 2077 curveto 3264 1029 2830 471 2007 471 curveto 1024 471 lineto end_ol grestore gsave 28.150333 15.737500 translate 0.035278 -0.035278 scale start_ol 896 2987 moveto 384 2987 lineto 384 0 lineto 896 0 lineto 896 2987 lineto 896 4160 moveto 384 4160 lineto 384 3557 lineto 896 3557 lineto 896 4160 lineto end_ol grestore gsave 28.319667 15.737500 translate 0.035278 -0.035278 scale start_ol 3044 413 moveto 2993 419 2971 419 2942 419 curveto 2778 419 2688 491 2688 617 curveto 2688 2222 lineto 2688 2733 2280 3008 1505 3008 curveto 1049 3008 670 2888 460 2675 curveto 316 2528 256 2365 256 2081 curveto 876 2081 lineto 914 2415 1120 2566 1542 2566 curveto 1949 2566 2176 2418 2176 2153 curveto 2176 2037 lineto 2176 1852 2060 1772 1695 1730 curveto 1042 1651 942 1630 765 1561 curveto 428 1428 256 1180 256 820 curveto 256 317 622 0 1209 0 curveto 1575 0 1868 131 2195 440 curveto 2231 137 2385 0 2706 0 curveto 2807 0 2884 12 3044 54 curveto 3044 413 lineto 2176 1018 moveto 2176 872 2133 784 1997 664 curveto 1813 502 1591 419 1326 419 curveto 974 419 768 581 768 857 curveto 768 1143 968 1289 1450 1357 curveto 1927 1419 2024 1440 2176 1508 curveto 2176 1018 lineto end_ol grestore gsave 28.743000 15.737500 translate 0.035278 -0.035278 scale start_ol 896 4160 moveto 384 4160 lineto 384 0 lineto 896 0 lineto 896 4160 lineto end_ol grestore gsave 28.912333 15.737500 translate 0.035278 -0.035278 scale start_ol 1530 3008 moveto 697 3008 192 2446 192 1504 curveto 192 562 691 0 1536 0 curveto 2370 0 2880 562 2880 1483 curveto 2880 2451 2387 3008 1530 3008 curveto 1536 2566 moveto 2057 2566 2368 2165 2368 1488 curveto 2368 848 2046 442 1536 442 curveto 1020 442 704 843 704 1504 curveto 704 2160 1020 2566 1536 2566 curveto end_ol grestore gsave 29.335667 15.737500 translate 0.035278 -0.035278 scale start_ol 2304 3008 moveto 2304 2493 lineto 2072 2844 1791 3008 1422 3008 curveto 688 3008 192 2387 192 1477 curveto 192 1017 322 642 564 375 curveto 783 139 1099 0 1409 0 curveto 1780 0 2039 158 2304 531 curveto 2304 355 lineto 2304 -108 2254 -388 2137 -577 curveto 2015 -779 1776 -896 1493 -896 curveto 1282 -896 1093 -824 965 -695 curveto 859 -588 815 -487 787 -265 curveto 292 -265 lineto 342 -901 770 -1280 1453 -1280 curveto 1886 -1280 2258 -1136 2447 -893 curveto 2669 -615 2752 -234 2752 477 curveto 2752 3008 lineto 2304 3008 lineto 1510 2566 moveto 2010 2566 2304 2170 2304 1488 curveto 2304 838 2004 442 1515 442 curveto 1010 442 704 843 704 1504 curveto 704 2160 1015 2566 1510 2566 curveto end_ol grestore gsave 24.550000 11.887500 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 24.922533 11.887500 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 25.125733 11.887500 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 25.464400 11.887500 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 25.803067 11.887500 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore gsave 26.107867 11.887500 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 24.550000 12.687500 translate 0.035278 -0.035278 scale start_ol 2235 3214 moveto 1880 3214 lineto 1719 2304 lineto 1143 2304 lineto 1309 3214 lineto 958 3214 lineto 793 2304 lineto 235 2304 lineto 235 1984 lineto 733 1984 lineto 590 1216 lineto 65 1216 lineto 65 896 lineto 530 896 lineto 355 0 lineto 705 0 lineto 885 896 lineto 1456 896 lineto 1281 0 lineto 1631 0 lineto 1811 896 lineto 2350 896 lineto 2350 1216 lineto 1866 1216 lineto 2009 1984 lineto 2498 1984 lineto 2498 2304 lineto 2069 2304 lineto 2235 3214 lineto 1659 1984 moveto 1516 1216 lineto 940 1216 lineto 1087 1984 lineto 1659 1984 lineto end_ol grestore gsave 24.888667 12.687500 translate 0.035278 -0.035278 scale start_ol 1216 2343 moveto 1216 0 lineto 1600 0 lineto 1600 3290 lineto 1347 3290 lineto 1205 2784 1114 2715 493 2636 curveto 493 2343 lineto 1216 2343 lineto end_ol grestore gsave 24.450000 21.887500 translate 0.035278 -0.035278 scale start_ol 1600 3011 moveto 2701 3011 lineto 2701 3392 lineto 110 3392 lineto 110 3011 lineto 1216 3011 lineto 1216 0 lineto 1600 0 lineto 1600 3011 lineto end_ol grestore gsave 24.822533 21.887500 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1262 lineto 704 1610 792 1838 976 1972 curveto 1096 2061 1211 2088 1478 2093 curveto 1478 2432 lineto 1416 2432 1385 2432 1336 2432 curveto 1098 2432 916 2304 704 1991 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 25.025733 21.887500 translate 0.035278 -0.035278 scale start_ol 2466 334 moveto 2425 339 2406 339 2383 339 curveto 2250 339 2176 398 2176 499 curveto 2176 1797 lineto 2176 2210 1843 2432 1211 2432 curveto 839 2432 530 2335 359 2163 curveto 241 2044 192 1912 192 1683 curveto 670 1683 lineto 703 1953 880 2075 1245 2075 curveto 1596 2075 1792 1955 1792 1741 curveto 1792 1647 lineto 1792 1497 1695 1433 1391 1399 curveto 847 1335 764 1318 616 1262 curveto 335 1155 192 954 192 663 curveto 192 257 496 0 985 0 curveto 1290 0 1534 106 1806 356 curveto 1834 111 1954 0 2203 0 curveto 2281 0 2341 10 2466 44 curveto 2466 334 lineto 1792 824 moveto 1792 706 1755 634 1638 537 curveto 1479 406 1287 339 1058 339 curveto 754 339 576 470 576 693 curveto 576 925 749 1043 1165 1098 curveto 1577 1148 1661 1165 1792 1220 curveto 1792 824 lineto end_ol grestore gsave 25.364400 21.887500 translate 0.035278 -0.035278 scale start_ol 320 2432 moveto 320 0 lineto 704 0 lineto 704 1298 lineto 704 1779 962 2093 1358 2093 curveto 1662 2093 1856 1913 1856 1630 curveto 1856 0 lineto 2240 0 lineto 2240 1787 lineto 2240 2179 1946 2432 1490 2432 curveto 1138 2432 912 2294 704 1958 curveto 704 2432 lineto 320 2432 lineto end_ol grestore gsave 25.703067 21.887500 translate 0.035278 -0.035278 scale start_ol 2112 1721 moveto 2112 2176 1777 2432 1182 2432 curveto 581 2432 192 2162 192 1747 curveto 192 1397 390 1230 977 1101 curveto 1345 1020 lineto 1619 960 1728 870 1728 708 curveto 1728 498 1492 357 1140 357 curveto 923 357 740 412 639 505 curveto 576 568 547 631 523 787 curveto 192 787 lineto 192 255 502 0 1127 0 curveto 1729 0 2112 278 2112 710 curveto 2112 1044 1904 1228 1413 1335 curveto 1034 1416 lineto 713 1485 576 1579 576 1737 curveto 576 1942 807 2075 1174 2075 curveto 1534 2075 1728 1953 1728 1721 curveto 2112 1721 lineto end_ol grestore gsave 26.007867 21.887500 translate 0.035278 -0.035278 scale start_ol 832 483 moveto 384 483 lineto 384 0 lineto 832 0 lineto 832 483 lineto end_ol grestore gsave 24.450000 22.687500 translate 0.035278 -0.035278 scale start_ol 2235 3214 moveto 1880 3214 lineto 1719 2304 lineto 1143 2304 lineto 1309 3214 lineto 958 3214 lineto 793 2304 lineto 235 2304 lineto 235 1984 lineto 733 1984 lineto 590 1216 lineto 65 1216 lineto 65 896 lineto 530 896 lineto 355 0 lineto 705 0 lineto 885 896 lineto 1456 896 lineto 1281 0 lineto 1631 0 lineto 1811 896 lineto 2350 896 lineto 2350 1216 lineto 1866 1216 lineto 2009 1984 lineto 2498 1984 lineto 2498 2304 lineto 2069 2304 lineto 2235 3214 lineto 1659 1984 moveto 1516 1216 lineto 940 1216 lineto 1087 1984 lineto 1659 1984 lineto end_ol grestore gsave 24.788667 22.687500 translate 0.035278 -0.035278 scale start_ol 2304 404 moveto 593 404 lineto 634 665 779 831 1172 1060 curveto 1625 1303 lineto 2073 1546 2304 1874 2304 2265 curveto 2304 2530 2195 2777 2004 2948 curveto 1813 3119 1577 3200 1273 3200 curveto 864 3200 560 3055 383 2784 curveto 269 2612 219 2413 210 2087 curveto 616 2087 lineto 630 2302 657 2431 713 2534 curveto 819 2727 1031 2843 1275 2843 curveto 1644 2843 1920 2587 1920 2245 curveto 1920 1993 1768 1777 1478 1615 curveto 1054 1381 lineto 372 1003 174 702 137 0 curveto 2304 0 lineto 2304 404 lineto end_ol grestore showpage kamailio-4.0.4/doc/sip/figures/event.dia0000644000000000000000000000254112223032460016603 0ustar rootroot‹í[]o£8}Ÿ_èkÆ`ƒOÚŽ¦£i´«­´mö©r7a—@NÛ¼Ìoßk ÍÑ`ê6»i*õ+Žïõ9\ß{!§Ÿ§‰u/ò"ÎÒ3#×¶D:ÌFq:>³o®¿ŒìÏçNG1ÿßãœO-˜‘ê¿3{"åì“ã<<< dQp™å(‰ç¨ÎOž$Ü79öùêì—¼zK™Çƒ¹VʧâÌðá?ã<›§£zÆ0K²ÜºçÉ™}rW~ÙÎù©³6·jÆg"F™Î²"†1¹˜mŒmÎS?ë¡FÒñùÉÿ¤Z²~¡ÕúrÊóqœÖP¹àIåA!¤­#(‰”ÜJ\Üβ\æ<–5Ò ËÁÓ LæsѪòøØ2·¸‹¥Ìš¬¸ãIÑh†³&§v«Œóx´Cˆ+C›³⑜Ü>vw®Xt¸‹xˆgâTvDX´Bè´·óx$Š]»»:¸9sR:;L_ÿÊ.§1´%|!òìbÖ¬zOj¥WïÍ‹¡¬­¾’<ñ|d}´~Sa/#3Äåxtf_º;\¸¶žžeËíFíA\§m‰SHƒÁ³t†`Ò8O`¡Ï"ÜscðÛïcÄ\³öuÝÖ`˜¥é­HG¥¿2z{L-ç·v(O‡³Ö:V¦Ó°éÅæ ÖØQô22ÛZŽª——ŠYq–4w¢ëRÎPä)Š"”3†Œ•x„1ô™¢Ü KÊ©‹MQ¾fôö-×Û?åá›PN:]åž±«<êD¨¢@#jô²öš/k¦ëÇ›³ÁÊ[žçÙC ®‚ð´!n‘Žådë´uQ¤UÝm¡^ª3¯Ch¡=jPgþ“ΨA­YÙ¤AzÔÙëèìZ<Êùº:ó ˆA•`©„!퇈†¤ 4¬5¶TN5§™+C[JY0n”“W7W_ÿüqñ­CUy—¥Ok©?­;>“,ÄÓ¶ ¹¨Ï©êÝ_³yC¥ÛÒljˆÇùi™á±,únùÕjÃq:Ïûµ¼êÜ.ÕÇKåOõåöTzeFý¡:Ä•øÈå@ü>dïû?q]ëò·w£|= ÿßÂß‘_úy¬g²Zõz¸OˆØ\ëí,M‰®õís‹—2êgzžÉ"’D Ìëcy‘Ú#Ï\²çí,Õjz^¼#;…H—ð\ \î{•bñ>Žƒ›BäÖ—1«wr$èx¹Ó•}¤ 6Ø6²¯±\ÉĆ@eC ~/¥€ÈasÞ‰ôuI<Èl»]Ú-´"ê·@5 ŠË&.íE›ìß®˜ÚÐtÑóãØuyy¨ÅX¿î ¨’E˜©<+47ªjOˆº=ÈÝcíùêµ§.™j`ÒAÿ#C}……Wú.!"{é»üqyýãû_ïGûZf¦áu¹¨¢3fÔ\ª!Ñ:×`:MÇ67W¬mLE´|9¦D§}—Õ¡s1ÓcŠpúÀ»Ï¢÷8%= õ ¦™RÞªÉ[ºcjÈ;Šî s\í{+ÀSÑÈH˜«°HÙO È/û  €½4¾Ý¿ŸVš.‰‡yÊú êV8F®oêy@Z*žøêœWÇ.€G¾É'9Ö Þδý9F]Q7ìÖYP=WFÍõ°êåU`Eª¹ ÎzÿX`½EsAÉà ¿Q·" 欲ûOeöP讳–7ZzþïðË:Ÿßsû¨_º+šÞæ#Ý1€k׮͜9³gΚ5kåÊ•ÕÕÕÕÕÕ#FŒX³fMoûùçŸçæææååUUU™››¯_¿ž~ëæÍ›yyy2™Œá/‰AwEÓG‘!]¡(ŠÏçËd²¾“²¹¹ÙÑÑ‘LC¯OOOÏüü|2]YY9tèPzgÏž1þ 1pè®hz›t(в··¯®®îùÞõë×_~ùe+++’–<ïù:=JÔÔÔ”Ïçóù|¥K"Íè®hz›tÇ&Mš”––Ö³Ò{ë­·V¬XQ^^.—Ë% ÕûÏßÎÎÎeee2™¬««‹¢(¹\ÎLµ;àé®h°ÈôÏÖ¯_¿aÆôôôŽŽŽâââwÞy‡¼×ÚÚ*„BaYYÙ²eËúØÊ{ï½÷î»ïÉd²¼ýöÛúˆ}Ð]Ñ`‘±€TpgΜ5j”@ ðõõ¥/¥¦¦úøø˜ššzyyíÚµ z4Q艮®®Ï?ÿÜÓÓÓÌÌlôèÑÇï¶Ò˜ŽŠ¦·ùHwxÞ‰^དé &Bz‚Ɇž`²!¤'¦=gM™2EãÍá²z MÅÆÆ.^¼˜¹X”$[vv¶ÞÃ@jЦ€^}õUæAêQ’l}ÔNä;«/võýù÷]FÆ ÓEHHêýÎFžÿÅŸæ –‘Á $é &Bz‚Ɇž`²!¤'˜lé &Bz‚Ɇž`²!ƒ°{÷îêêj¶£Ð-L6ľ¢¢¢÷ßßÙÙyÍš5]]]l‡£+˜lˆ}–––111&&&_ýõìÙ³ÛÚÚØŽH'ðv-®1Þ2ÊÏψˆ¨««‹ŒŒ<þ<Ûá0k6d(‚ƒƒsssíííÓÓÓ÷ïßÏv8ÌÚkŒ½Œ>¼páB{{û‡:::²“°fC†eÁ‚3f̨««ûôÓOÙŽ…aX³q ʨ   ((hРAååå666l‡Ã¬ÙÁ1bDDDDccãØŽ…I˜lÈýõ¯€o¿ý–í@˜„ÍH®áFÉd2[[Û¦¦¦òòrWWW¶ÃaÖlÈ™šš’¾‰¸Ôý“|µ“‰I“&u{kÏž=óæÍë¹Ö¼yóöìÙCÖå)£t¾îfà"=eff²c8˜lЬ¬¬Nœ8¡8'&&æòåËeeeŠ3ËÊÊ._¾C^ÒÃŽt›†ƒòèã*îuåÆÁs62ˆ&™øé§ŸæÍ›wïÞ=333ú­-[¶ÔÕÕ%&&Ò«¬^½ÚÖÖöã?¦×í¶)¥/ “Q”‘*är¹££c}}}ii)7zàãxÍ6räÈI“&u»¨wôèQ±XL^J$’£GÆÅű ꕉ‰ 9mãLåÆñd€-[¶|õÕWô{{û·Þz+))‰¼LNN~óÍ7TÙž³éÇNÛ8ÞŒ$Ÿ|òI{{ûgŸ}FÏ)))‰ˆˆ(..æñx~~~.\ðõõ…þÚØŒÔ³üüüww÷'Ož° D²µ´´Œ=:++ËÍÍþ­·Þzã7ÁñãÇé‹(˜l…¢(++«ÖÖV‰D2hÐ ¶ÃÑšZƒk°ŠþAq¥)ŠÚ¿?ýž“““2nܸ«W¯ö\W•—†Iƒ2zã7âããŸ>}ª£´wïÞe;pÿœX´hÑýû÷çLœ8Q$™™™…‡‡«¾î³åççŸ>}:>>ÞÇÇgÆ MMMlGôooo())a;p0Ù(…owz¦‰‰É­[·¨W—.]ºvíšÒu{{٠á³!(((##cþüù[·n?~|mm-ÛAý“ éO||¼Nk7uêÔ#GŽþüóϯ½öZCCƒîö¨L6¤? îîîñññôƒ:âíí••åïï÷îÝ™3gJ¥RîNE^^^À•dãàŽ¡KJ$mÞ¼¹¡¡A•å5ÞÝÓ§OIeòî»ïj¼åççÀðáÃÙ„¼ôÏ1Ý.ÈD¢•+W®ZµJ$õ±¼6eTXX`ffVRRâââ¢ñvÑÒÒbmm-ZZZLLŒ»!fÜÑ@b±8!!ÁËËKw K??¿¹sç¶··ýõ׺ؾZ,--œœÚÛÛËËËÙŽE[˜lFI×)÷·¿ý ¾ýö[C¸R™k$˜lFLw)7vìØiÓ¦I¥ÒÓ§O3¸Y͸¹¹@ee%ÛhËTƒuÈí¡È@”Û¹s'9—cj³Ó§OÏÈÈÈÍÍ]´hSÛÔ 9;ÕõÅX=Ð$Ù¸ô¤:gˆÅâ;v˜šjR J7nݺÅÔ5fkk †Ð Õ’&eÙ狌‚*íÅK”}ô#û åñxwïÞ•Éd æ°tÍÁtHCýþ 1[[[Ÿ¢¢¢‡†„„0»qu#[³!C »4£¹¹¹±~«ä€®Ù»ôf„µµ5°þÖlˆzK3Â@’,&Ò=§a ÉFj6lF"c%Í+++hnnÖó~»Áf$Ò“šš¶®¼wtt€¹¹9+{§ <˜ÇãI$Š¢Œúéx¼]Ëбøy¤õÒLMM­­­»ºº ä;a²¡^H²€¥¥%´¶¶²ˆV0ÙP¯ 'Ù´··³ˆV0ÙP¯H7Ò†l休œC/L6Ô«šš°··g;ç5&â&™LVQQÁçóÉãdìÂdC\V^^.“É\\\È?:»ðœ q/ÒÓÓ“í@ðœ qI6…›‘ˆË?~ S³a²!.Úq˜lH9¬Ùð â&ƒªÙð ⬮®®òòr¶cÀf$‹z‹¢¨uëÖÙÚÚÚÙÙmذReBéüžT} C£~Ö£_NNN¬?_Cpc £L62&HÏù{öì¹råJAAA~~~vvöÞ½{ûžßmƒÔ‹ùö‡b  ª r¹p` ràÀO?ýÔÙÙÙÅÅå³Ï>Û¿ßóûÀãñ=<iÒ¤#F½úê«úÓŸúž¯–µk×VTT¼òÊ+*.¿eË–Áƒ{xx‡‡‡›™™i°SV”••QåééÉçóÙŽå9n4#u{D*•ÆÇÇ«µ ƒõ¿ÿû¿ÿûß5[÷áÇ^^^ÌÆÓ е2ÚèŒ3˜ I{óæÍ€””¶ÑŠ®¾*Èp˜îîîñññ:Ú…>I$’þóŸ+V¬Pk­÷ß¿¾¾¾²²òÃ?ŒŠŠÒQlŒ3´«#À•f$ó€ŠÅâ;w&&&r sv‚ÇãñùüþóŸ...j­èïïØÒÒ2{öìO>ùDGá1ÎЮŽ&[OÜK3‚Òôά+V¨[¬Ù¸qÎÆL²q5Í¦ŠŠ 0ûý ¬Ù0͸¨¾¾ £»HÚ@O6L3®"ÉfggÇv ¿¸ÍHuÓlÊ”)ì±B&“I$>Ÿ?xð`¶cùÝÀ­ÙNž<¹}ûvÕ¤ÌÎÎÖ`/ˆ EÙÚÚT52p“mñâÅQQQªWnYYYìiFËvà“ öi8n²€H$Ú¼yóÊ•+UI¹ˆˆÍö‚ØbhÿÖÜ8gÓ*z’r¥¥¥ñññúñé¹²««‹í@^Àš¯ L9.!£ ÓOåL¶`ÊqƒaÖlØŒTSÎØ‘G ­ïa¬Ùz…)g¼,,,jkkUÿiG0ÙúARŽ ‹ŒŸÏ÷óó£(êÑ£GlÇò;L6•ndDüýýàçŸVú®½Gkçl˜ ¨;’l¿üò‹ÒwéŽx<žÆ© k6ÄM£G€´´4—§{&­˜ÒÒÒ¨¨( ‹™3gVWWÓ‹)í:úìÙ³ÁÁÁ`ذa{öìQº nÔlÆ=Ò…7ÞxÃÖÖ677—·~‘ޣɯs³fÍZ¹reuuuuuõˆ#{SÚutLLL||¼T*½téÒ­[·”nŸÉfÄ="#¥)£¿üå/°råʾwDOнGwÓÜÜìèèH/¦´ëhÄÄÄÇ÷±¯ððp¸zõªZGahŒü«鯒%Kàûï¿onnVeyº÷hÈÉÉyå•W¬­­y<ž••=& ôÒuô?ü‘‘êááqòäI¥ÛÇš "¦ÊˆÜ>þꫯ’A”î¨Ûáææ–’’ÒÐÐ —Ë%Io‹õ 2--ÍÉÉIé¾&L˜7nÜÐà@ ‡‘U 9r䈫«ëÅ‹ÕíιµµU …²²²eË–õ»üüùó :::är9}›X· d>ÖlȰ0XF999däÑ+VtvvöÜQ· "55ÕÇÇÇÔÔÔËËk×®]½-F¿6[^^±`Áz¦ÆAj¶¢!xøðáÂ… årù_|ñÞ{ï±W’ÍX¯Fö â­­L·¶¶ …B2–‘‘A¦322&MšÔ÷ÖÚÚÚA·ù°{÷nOOO33³1cÆüôÓOd~KKKLLŒ¥¥åСC·mÛ=.s«»¢ö´/#ò]óæ›o2’öÈ:ÅÅÅl¢#ÿªPðúë¯oÛ¶M"‘ˆÅâ/¾øbæÌ™dþƒÆG¦CCCjhh(++»wïÞ¥K—”nSãYÑÜÜ|êÔ)·}ûvͶ ‹‡Ü°fcSÏ0ÊËËéW¼¼¼è[cMLLè_‡ºººø|¾Ò­Ñ\\\JKK»íz¹‰ÖÕÕµ¨¨ˆL‚²šM­µ§e>|”Öÿ=ÔÔÔdddDGG÷ Cãz"%Û÷ÍʆÏÈ¿*,Y²$66¶¡¡¡¡¡aÁ‚ô(õÖÖÖtwR©ÔÆÆFéêEÉåòÂÂBŸû÷ï÷\@éM´•••Æ #ÓôS+²âøñãðöÛo÷»ä£GlmmNŸ>ýúë¯ë4*¬ÙØÔ3 sssÅs6sss2­î9ÛãÇ¥R)ÕûIôK—~k6µVÔž–e÷ïßïc™£Göû¿Äìÿ óµ¢¢‚ÁmꟑU( úÇ?þ!‹ÅbñÖ­[ƒƒƒÉü˜˜˜M›6ýúë¯Ïž=Û¸q#]ãõÆÃÃ#,,ìÈ‘#ªì4::zõêÕµµµ555jݰ«ñŠºV^^nnn},C±±±©©©ÞÞÞäŸIwQaÍÆŽÞâøðáÔ©S…B¡P(œ:uêÏ?ÿLæËåòµk׊D"‘H´~ýz¹\®t›Š/Ï;J©PA577/\¸P(2äÿø‡™™Y·Ô]Q{Ú”y"ÆÚÚZ•…½½½ëëëÏŸ?¿páB¥ahƒRC† €ªª*·©Æ—lëáÇ^^^ú\Q)mʈ\ªõ÷÷WeÝ=z´Û2šÅ ”ƒƒÔÔÔ0¸MýÓm½ÜÔÔ” Ó]°îý÷߯¯¯¯¬¬üð㢢ô°¢î´´´@o×hE>|xÕªUE…‡‡?xð€¢(ú.] ϳ‘®Ñ—®’M,'$$¸»»ÇÇÇëhÂßß?00ÐÏÏoРAŸ|ò‰VÔ¡P­­­ý.I®@Ö×××ÔÔŒ1Bñ-úG6mãÆ9›z]ÿ‘Ï®ïUz¬Ö.–T)£Þûúúz{{3—V $•Jû­u “wýãöÆŽÔlmmmlÒ7j6f’ ÓŒH²‘37ƒ‚É€iÆ-ƒ‰¤­­Í‚íp~ÇdÓ>3LLL\]])Šzúô)Û±¼€ɦIͦnm6eÊ ö‚XáááQZZúäÉ???¶cùI3’rÆK“d;yòäöíÛU,/;;[ƒ½ VûëŸ&dDHÍ†ÉÆ8ã¾Ù éB`` ŸÏôèQGGÛ±<‡É†¸I(z{{wvvÒƒ“°“ q–¡]#1ØíԂɆ” ×H衳XGzCJ¥l¢L6¤„¿¿?üòË/lò&â,L6]ÀdCJ > ¤g4’lª?Bi˜0Ùƒvrrjii1þ°fC\FZ’rõ“ qiIÈi›µµ5`²!®2¨k$X³!.#56#„Ɇ”Úq˜lH9///33³'OžB×ÿƒ €ÆÆF¶Ñ 7“-===22R(ÚÛÛ/\¸°ººšÌ§(jݺu¶¶¶vvv6l †T:¿›´´´ssó   Ó§OÓóFºÿ~@@€ÎŽLÌÌ̼½½år9ò›]NNNðìÙ3¶ÑŽZC'j° +"##ÓÒÒšššªªªâââ¦NJæ'%%………={ö¬¢¢â¥—^JNNî{¾¢ëׯ;;;gff¶µµedd8;;çää·èÏäâÅ‹>>>7oÞÔý!öŠÁ2ŠŒŒ€³gÏ2²5mtuu™™™ñx¼¶¶6¶cÑ7“MQss³¹¹9™ ËÈÈ Ó“&Mê{¾¢9sæìÛ·~¹wïÞ¨¨(2M>“'Nøúúèæ8TÅ`‘ÁD9ÂÈÖ´äéé ÅÅÅl¢9î'ÛÉ“'#""Èô Aƒ$ ™‹Å"‘¨ïùŠP¯¬¬ttt$Ó””PVV¦££Pƒeß|ó #[ÓÒäÉ“ ;;›í@4Çñ> nß¾ýÁœ;w޼ljj"?€ }u«·ùŠìííé— ô˸¸¸‹/’o_ΰ³³€úúz¶pss€òòr¶Ñ7/YYYsçÎ=v옯¯/™cmmMßÌ*•Jé1c{›¯ÈÖÖ¶®®Ž~YWWgkkK¿LNNž7ožáO~øá:i¨¬¬Œ‹‹ …ŽŽŽË—/ojjê¹LRR’™™™¯¯o·g “““IÇ›¾¾¾IIId-?<ÚÙÙ©ÖòHKê–‘L&;tèéÿØÄÄdëÖ­º‹A»ví ‹K—.©¸ŠÒ¥ªªÊÞÞ¾çnnnû÷ïomm­¨¨ˆ]±bE·e<èééyõêÕöööëׯ{zz>|˜¼õïÿÛ××÷Æííí7nÜ 7—R¡w­FÝ2"e’+"wîÜÑ]`ŒûË_þÖÖÖÛ¶mëèèèwy¥Kgg§©©i P%•Jœœº-š––F/sæÌ™ñãÇ“é‰'¦§§Óo?“›4(£˜˜˜}ûö]D&“M:•ïùóçû]¾·šÍÁÁ¡ÛUUUK–,quu%íjRçw[ÆÂÂBñ·¾ææf ‹ÞÞ"kq¼+;¤Š°‚Ú:::"##/^¼ÿõ_ÿE:oÖÀåË—{>Ö’••åîînaaÑÙÙÉÌ}Ïj}—h° Ò³PF2™lîܹ0dÈï¾ûN&“©²VÏ¥©©)$$„þ}œ^ÀÌ̬µµ•^,==~ š‘Š?Ó={–nFN˜0áÂ… =WÇdãšPFÛ·o[[Û{÷ý±´··—••8p @éïl¡¡¡}ôQ}}}KKKjjª³³sÏd;xð ··÷õë×;::rrr¼¼¼è $)))~~~·nÝêèè¸uë–ŸŸ&7q¾ŒèîqÏœ9£ÖŠtkN(zyyõqIaaák¯½faaaaa1jÔ(ÒÌî¶ EQ»wï¦/ýïÙ³GqSIIIÆ 355õõõýæ›o…ÉÆ=œ/#ò‹ök¯½Æv ªÊËË ¤(ÊPž DHûöíÒ’4X111………2™ì§Ÿ~Z¾|ùŸþô'оTê‹…IDAT„ô)--­¾¾~ôèÑ#GŽd;–¾DFFΚ5«¸¸ØÕÕuéÒ¥«W¯L6d\: .d;~,\¸°gšwøƒ ‡ËH.—ÛÙÙI$’ŠŠ ¶ÃQž³!£QXX(‘HÜÜÜŒ1Ó“ 2¬—¡õA¤:L6d4nݺãÆc; ˆd >|x·™çÎ3fŒ¹¹¹¿¿ÿÁƒÉLrÂCÜ¿? @Q¢þ`Ífènܸ!“ÉÁõë×é™999d¬P©Tš––Ö³³½K—.ý÷ÿ÷÷߯ß`Q¯d2Ù;wx<žâP²Æ…ûW#ãââ\\\AII yfæÌ™óæ›oÆÆÆv[˜ÇãQõã?®[·.55500PïñjËËH÷îÝ5j”OQQÛ±hJ­O4X…]mmmöööeeeööömmmd¾ƒƒCUUUÏå )))  ¬¬L¿‘2ÆèÊHEß}÷DGG³ˆæ8ÞŒüÏþ3räHOOO—±cǦ¦¦’ù öööJW‰‹‹KNNöôôÔc˜¨=¿q¤oO¶ÐmÅØØXú)I‘HT__¯t•äääyóæåççë)D¤š_ýŒô¶çÔª5X…EUUU¦¦/ÜfjjZYYIQÔìÙ³÷ïßßsrt2dHnn®¾#f‚q•‘ê¦OŸÝŠ1.\®Ù>¼`ÁÅ£‰‰9rä¬_¿~Æ éééÅÅÅï¼óŽâŠ‹-úŸÿùŸ3f\½z•¥ØQw•••@žã4Vj¥¦«°hÔ¨QYYYŠs²³³GE¦Ïœ93jÔ(@àëëK×rŠGwâÄ [[[}Ëã*#Õ‘sìêêj¶Ñ÷/ý»Õ«WoÙ²ÅÚÚZÅå9YFíííB¡ÐÔÔ´­­ÍpgT—±Æ=p$&&º»»ÇÇÇäQËÉï4C‡5ÞLL6£ ‹¼¼¼lÊ‘K‘Æ}†ÉfDrÊ‘dsrrb;­`²™™r\¸‰Éf¤ZÊÕÕÕ€ƒƒÛh“͈ œ”kiiKKK¶ÑŠ&þL™2…ñ8ÆHÊíܹsåÊ•«V­b;hmm…™lÙÙÙL‡´%‹wìØ¡tLs 5›P(d;­h’l=µDº£J;B$‘jM$‘. 9fàÖlL‡4¤˜flÇ¢C÷œ ‚’fÄÀmF"v ¨4#n3±e¦ÍH¤?6ÍlF"}àiF`3éCiié@N3‚5Þ®eè0Ó «« ºõ(ct0ÙËå`ÔOŽ&2 ¤—Å‘Œ&2˜lé &Bz‚Ɇž`²!¤'˜lé &Bz‚ɆžàÚé Ölé &BzÂçóá·Û‘&2fffÐÙÙÉv ZÁdCF€<\#“ÉØD+˜lÈ`²!¤'˜lé ž³!¤'X³!¤'˜lé &Bz‚çlé Ölé &Bz‚ɆždÃs6„tŽ\ Áš !Ãf$Bz‚Ɇžà9Bz‚çlé 6#ÒL6„ôÏÙÒ¦Û[˜Zˆ5Û€kF"cÄš “ ¬ÙÒ¬ÙÒ¬ÙÒ¬ÙÒ¬ÙÒ¬Ù,_¾üÇd;Š~`͆ŒÞÕ«W÷ìÙóæ›o†„„ܹs‡ípzEj6L6dÄ&L˜°k×®¡C‡æç燅…>|˜íˆ”#56#‘333ûóŸÿüôéÓM›6µ··/Z´hÏž=l¥6#û×ÔÔ” Ó] í™™™mÙ²å›o¾111ùóŸÿLžô3(x¤/b±8!!ÁÝÝ=>>^G»@ÌZ±bÅÇ,—Ë£££[ZZØçX³)GÒÌËË+>>^,3¾}¤;7n ¬ªªRìäÏ`ÍÖ¦™±ãóùï½÷?~œíX^€5Ûï0Í8cæÌ™pûöm¶y7j6m‹Å;wîLLLÄ㨮®f; ôdÃ4ã$¡PÈãñÚÛÛår9ù7¤I“ÆK“dS7ͦL™¢Á^+êêê(в³³3œL€ŽŽl¢M’íäɓ۷oojjRqyüÝõæéÓ§àääÄv / à ’QÚŒ—&ɶxñ⨨¨ÄÄÄ;wªR¹eeei°¤-Ûyyy0jÔ(†ÂaÆÀ­Ù@$ÅÇǯZµJ•”‹ˆˆÐl/HÿH²;–í@^ÀdÓª]NR®´´tóæÍ"‘ˆ©˜‹ 3ٸьdà$˜N¹øøxL9£FQÔ½{÷x<^hh(Û±¼k¶ˆD¢Í›7cʵÒÒÒÖÖVWW×Aƒ±Ë 0Ù”À”3j¿üò øûû³Hw،즜‘2ØdÚ­$åjjjt· Ä,’llÒI6c¯Ùt>°†©é@»ƒÈO8Jßí6¸¡ŸqHH3ÒØk6Ìô; hooWú.W<OÏcý`3RßÒÓÓ###…B¡½½ýÂ… é;Ó)ŠZ·n­­­Ý† èÿƒÞæw“––bnntúôiz¾âùýû÷ °qÅ8GGG(--Uqy—˜˜èááAÚ/¥¥¥QQQ6663gΤ ˆÇã%%% 6L Œ;öÞ½{dþÙ³gƒƒƒÁ°aÃúîûD*•€µµµÆ‡f(uh° ƒ"##ÓÒÒšššªªªâââ¦NJæ'%%………={ö¬¢¢â¥—^JNNî{¾¢ëׯ;;;gff¶µµedd8;;çää·è#½xñ¢ÏÍ›7uˆ ЦŒN:¯¼òJ¿» 'æÏŸ_SSC^eff¶´´H$’5kÖ,X°€^ìüãÓ§O›››ÆOæ;88?~¼­­íñãÇK—.ícï¼óìÝ»W³ã2Æ”lŠš››ÍÍÍÉtXXXFF™ÎÈȘ4iRßóÍ™3gß¾}ô˽{÷FEE‘ir¤'Nœðõõ-((ÐÍq0O›2ª¯¯çóù&&&·oßî{ôijgÏ”.ÓÜÜìèèH/V[[KÏ755%Ó‰‰‰?î7°¨¨(8qℊb˜Œ5ÙNž<A¦ $‘HÈ´X,‰D}ÏWäààPUUE¿¬¬¬TüIJJ (++ÓÑQè‚–e´víZ˜>>………䥉‰IWW™îêêâóù}ÏWÄçóe2ýR&“Ñß»`bbrùòe…ŽhYF‰ÄÎÎ.\Hz=wÑm‚pssKIIihhË剤·Åz†—––æääÔGT£G€¼¼<µŽÅÐÓ"++kîܹǎóõõ%s¬­­é‡ë¤R©MßóÙÚÚÖÕÕÑ/ëêêlmmé—ÉÉÉóæÍËÏÏ×ŦAƒ¥¥¥ÙØØ:t(**Š\™PQkk«@  …eeeË–-ëwùùóçtttÈårº3¥?0444€bÑ%µRSƒU˜•’’âêêÚíŒB›s¶Ù³gÿë_ÿ¢_îÛ·oΜ9dšéÁƒ‡ ’››Ëð‘è #etãÆ Ò2dÈ©S§zî¢Û‘ššêããcjjêååµk×®Þ£_=zÔßßßÔÔ488øüùóJ& Z»Œ)ÙvìØáááÑóZÅîÝ»•^uìm¾¢«W¯:99eff¶··gff:99]»v¼EiJJн½ý•+WtvdLbªŒŠŠŠ^}õU²µñãÇ>|¸££CûÍj@&“ñx<Å“#eLÉÖ³Z–J¥EÉåòµk׊D"‘H´~ýzú̾·ùݤ¦¦™™™*~‹+é‰'lmmuypŒa°ŒärùæÍ›éŸ’mllø`îܹäv{WWWVBâÈ¥HL6¤¨¦¦&""¢  ÀÙÙyÛ¶m ,Pz!^Ïž=sÇŽ«W¯®­­}øðáàÁƒßÒgj6lF¢çrssAµ•=zdkkëààpúôé×_žþüy2aeeõå—_’{,µ‡É†¸¦±±úëå?%%…®¯èk'111=Ö»páBXX˜öQ‘Ç-,,8p›‘è9KKKøí™èÞDGGS›ššÚØØèííMîè¶ØíÛ·?øàƒäädí£"'l^^^†p]TK˜lè9???¸sçN¿K^¾|yòäÉׯ_ïùnÏÇ2´Á™6$`²!Ú+¯¼/^ìcÇãñJJJìììf̘qèÐ!—’’B/pìØ±E‹8q‚©Ì1Ùþ¬^½Zõ¡ð´Aîñ'Ëö¶ EQ‡^µjEQáááä¾äèèhòîW_}µnݺ .08.—’͘îú˜à·înU|šK›2rss€û÷ï÷±ÌüùóÓÓÓëêêüüü”îZy,C“'O€3gÎh¹C€Éfèè\SN›2Š€/¿üR³Õ×ÜÜ,ø|¾X,f;`3ÒhˆÅâ„„//¯øøxG3WùÑìÌ™3ºØ¸®^½ÚÑÑ1vìXò¤¶±Ãd32:M¹3f˜™™]½zUGɬ.2@´–L6£¤£”¤õƒÜxéààP^^nnnÎVÌÂGlŒUÏ4cDggggg'ŸÏg÷_œt¨üî»ïr&Ók6Ã׳fë;Í´,£ââb___OÛª««ÝÜÜäryqq19‡ä¬ÙŒ‰Žj3Eç΀‘#GêhûªX·n]ggçܹs¹”ix#²Á#Ÿ¹ê÷"kYFãÆ€””· ¥Ë—/óx< ‹¢¢"¶bÐL6C§Ö-ÿ”vetüøq’ØmmmšmAK‰„,#ƒõÿ¨Å h£—IEND®B`‚kamailio-4.0.4/doc/sip/Makefile0000644000000000000000000000013112223032460014770 0ustar rootrootdocs = sip_introduction.xml docbook_dir = ../../docbook include $(docbook_dir)/Makefile kamailio-4.0.4/doc/dst_blacklist.txt0000644000000000000000000000615512223032457016142 0ustar rootroot# $Id$ # # History: # -------- # 2006-09-08 created by andrei # Destination blacklist Overview ------------------------------ The destination blacklist (dst_blacklist) is used to try to mark bad destinations and avoid possible future expensive send operation to them. A destination is added to the blacklist when an attempt to send to it fails (e.g. timeout while trying to send or connect on TCP), or when a SIP timeout occurs while trying to forward statefully an INVITE (using tm) and the remote side doesn't send back any response. The blacklist (if enabled) is checked before any send attempt. Drawbacks --------- Using the destination blacklist will cause some performance degradation, especially on multi cpu machines. If you don't need it you can easily disable it, either in sip-router's config or at compile time. Disabling it at compile time is slightly better (but not in a "measurable" way) than disabling it at runtime, from the config file. Whether the destination blacklist is a good solution for you depends a lot on the setup. In general it is better to turn it on when: - sending to clients that don't respond is expensive (e.g. lots of clients use tcp and they have the habit of silently discarding tcp traffic from time to time) - stateful forwarding is used (tm) and lower memory usage is desired (a transaction will fail immediately if the destination is already blacklisted by a previous transaction to the same destination that failed due to timeout) - faster dns failover is desired, especially when stateful forwarding (tm) and UDP are used - better chances of DOS attack survival are important Config Variables ---------------- use_dst_blacklist = on | off (default off) - enable the destination blacklist: If on each failed send attempt will cause the destination to be blacklisted. Before any send operation this blacklist will be checked and if a match is found the send is no longer attempted (an error is returned immediately). Note: using the blacklist incurs a small performance penalty. dst_blacklist_mem = size in Kb (default 250 Kb) - maximum shared memory amount used for keeping the blacklisted destinations. dst_blacklist_expire = time in s (default 60 s) - how long time a blacklisted destination will be kept in the blacklist (w/o any update). dst_blacklist_gc_interval = time in s (default 60 s) - how often the garbage collection will run (eliminating old, expired entries). dst_blacklist_init = on | off (default on) - if off, the blacklist is not initialized at startup and cannot be enabled at runtime, which saves some memory. Compile Time Options -------------------- USE_DST_BLACKLIST - if defined the blacklist support will be compiled-in (default). Note: To remove a compile time option, edit the file Makefile.defs and remove USE_DST_BLACKLIST from the list named DEFS. To add a compile time option, just add it to the make command line, e.g.: make proper; make all extra_defs=-DUSE_DNS_FAILOVER or for a permanent solution, edit Makefile.defs and add it to DEFS (don't forget to prefix it with -D). kamailio-4.0.4/doc/presence/0000755000000000000000000000000012223032457014354 5ustar rootrootkamailio-4.0.4/doc/presence/biblio.xml0000644000000000000000000000731012223032457016337 0ustar rootroot There might be new versions of internet drafts and thus links to them my be obsolete. In such case try increment version in link or find the draft on IETF by name. XCAP xcap <ulink url="http://tools.ietf.org/html/rfc4825" >RFC 4825</ulink> - The XCAP specification - The XML Configuration Access Protocol xcap_diff <ulink url="http://tools.ietf.org/html/draft-ietf-simple-xcap-diff" >draft-ietf-simple-xcap-diff-13.txt</ulink> - XCAP changes notifications format xcap_profiles <ulink url="http://tools.ietf.org/html/draft-ietf-sipping-config-framework-16" >draft-ietf-sipping-config-framework-16.txt</ulink> -XCAP user profiles Presence events <ulink url="http://www.ietf.org/rfc/rfc3265.txt">RFC 3265 - SIP Events</ulink> presence <ulink url="http://www.ietf.org/rfc/rfc3856.txt">RFC 3856 - Presence Event package</ulink> pidf <ulink url="http://www.ietf.org/rfc/rfc3863.txt">RFC 3863 - Presence Information Data Format</ulink> rpid <ulink url="http://www.ietf.org/rfc/rfc4480.txt" >RFC 4480 - Rich Presence Extensions to the Presence Information Data Format</ulink> publish <ulink url="http://www.ietf.org/rfc/rfc3903.txt">RFC 3903 - Event state publication</ulink> winfo <ulink url="http://www.ietf.org/rfc/rfc3857.txt">RFC 3857 - Watcher info event package</ulink> winfo_doc <ulink url="http://www.ietf.org/rfc/rfc3858.txt">RFC 3858 - Data Format for watcher info</ulink> reg <ulink url="http://www.ietf.org/rfc/rfc3680.txt">RFC 3680 - SIP Reg Events</ulink> Authorization common auth <ulink url="http://tools.ietf.org/html/rfc4745" >RFC 4745: Common Policy: A Document Format for Expressing Privacy Preferences</ulink> presence auth <ulink url="http://tools.ietf.org/html/rfc5025" >RFC 5025</ulink> - Presence authorization rules and usage with XCAP Resource lists rls <ulink url="http://tools.ietf.org/html/rfc4826" >RFC 4826</ulink> - XML formats for representing resource lists sip rls <ulink url="http://tools.ietf.org/html/rfc4662" >RFC 4662</ulink> - SIP Event Notification Extension for Resource Lists kamailio-4.0.4/doc/presence/install.xml0000644000000000000000000001302212223032457016542 0ustar rootroot ]>
Installation and running Presence modules use some dynamic libraries distributed with SIP-router and the compilation procedure and running is a bit more difficult than usual. For detailed description of libraries see their separate documentation (stored in lib/doc subdirectory of main SIP-router directory).
Incompatibility XCAP module needed for authorization is not working with TLS module. For more information see .
Dependencies Presence module dependecies may be found in sections PA module dependencies and RLS module dependencies. These modules depend on common libraries which have their own dependencies as mentioned below. Missing dependencies for common libraries!
Installation from CVS There is an example of steps which need to be done while installing SIP-router with shared libraries into directory /base/ser/directory (if no directory specified - no prefix parameter given - default value is used: /usr/local/) Download current SIP-router sources: cvs -d :pserver:anonymous@cvs.berlios.de:/cvsroot/ser checkout sip_router Download very useful ser_ctl utility (not necessary, but it is handy for adding data into database, running XML-RPC functions etc): cvs -d :pserver:anonymous@cvs.berlios.de:/cvsroot/ser checkout serctl Compile and install SIP-router with presence modules. Common libraries should be compiled automaticaly - it may fail in the case of unsatisfied library dependencies. You need to install all external libraries introduced in cd sip_router make install group_include="standard,presence,standard-dep" prefix=/base/ser/directory
Presence snapshots In past there were published presence snapshots with stable and tested code and up to date documentation. This will change - tested and documented stable features will be probably imported into stable SIP-router branch (Ottendorf) and unstable ones will remain in CVS head.
Running SIP-router Linker used for dynamic linking must know, where to find these libraries. This may be done by setting LD_LIBRARY_PATH before startup. export LD_LIBRARY_PATH=/base/ser/directory/lib/ser /base/ser/directory/sbin/ser -f /base/ser/directory/etc/ser/ser.cfg If you want to run SIP-router under sudo like sudo /base/ser/directory/sbin/ser -f /base/ser/directory/etc/ser/ser.cfg it need not work - it is possible that LD_LIBRARY_PATH will not be propagated in this case!
Database initialization This paragraph can be out-of-dated by changes in DB init scripts or in ser_ctl. It has only informative value! It is very handy to use SIP-router together with database - at least domains and users with attributes can be stored there. First must be database created - there are scripts to do this in SIP-router's source tree in directory scripts; for example "scripts/mysql/ser_mysql.sh create" will create the database for MySQL. Later MySQL versions can complain with current script, if so, you can try to remove qotes around $PW in script to get it into work. After database creation you can add data using ser_ctl utility: add domain: ser_domain add test test-domain.com add user parf with password parf: ser_user add parf ser_uri add parf parf@test-domain.com ser_cred add parf parf test test-domain.com parf Running ser_xxx without arguments shows help. You can specify database URI used by ser_ctl; for example you can use postgres://... instead of default mysql://... etc.
kamailio-4.0.4/doc/presence/cfg/0000755000000000000000000000000012223032457015113 5ustar rootrootkamailio-4.0.4/doc/presence/cfg/ps.cfg0000644000000000000000000003726312223032457016231 0ustar rootroot# # $Id$ # # First start SIP-router sample config script with: # database, accounting, authentication, multi-domain support # PSTN GW section, named flags, named routes, global-, # domain- and user-preferences with AVPs # Several of these features are only here for demonstration purpose # what can be achieved with the SIP-router config script language. # # If you look for a simpler version with a lot less dependencies # please refer to the ser-basic.cfg file in your SIP-router distribution. # To get this config running you need to execute the following commands # with the new serctl (the capital word are just place holders) # - ser_ctl domain add DOMAINNAME # - ser_ctl user add USERNAME@DOMAINNAME -p PASSWORD # If you want to have PID header for your user # - ser_attr add uid=UID asserted_id="PID" # If you want to have gateway support # - ser_db add attr_types name=gw_ip rich_type=string raw_type=2 description="The gateway IP for the default ser.cfg" default_flags=33 # - ser_attr add global gw_ip=GATEWAY-IP # ----------- global configuration parameters ------------------------ debug=3 # debug level (cmd line: -dddddddddd) memdbg=10 # memory debug log level #memlog=10 # memory statistics log level #log_facility=LOG_LOCAL0 # sets the facility used for logging (see syslog(3)) /* Uncomment these lines to enter debugging mode fork=no log_stderror=yes */ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) #port=5060 #children=4 #user=ser #group=ser #disable_core=yes #disables core dumping #open_fd_limit=1024 # sets the open file descriptors limit #mhomed=yes # usefull for multihomed hosts, small performance penalty #disable_tcp=yes #tcp_accept_aliases=yes # accepts the tcp alias via option (see NEWS) # # ------------------ module loading ---------------------------------- # load a SQL database for authentication, domains, user AVPs etc. loadmodule "/home/kubartv/SER/lib/ser/modules/mysql.so" loadmodule "/home/kubartv/SER/lib/ser/modules/sl.so" loadmodule "/home/kubartv/SER/lib/ser/modules/tm.so" loadmodule "/home/kubartv/SER/lib/ser/modules/rr.so" loadmodule "/home/kubartv/SER/lib/ser/modules/maxfwd.so" loadmodule "/home/kubartv/SER/lib/ser/modules/usrloc.so" loadmodule "/home/kubartv/SER/lib/ser/modules/registrar.so" loadmodule "/home/kubartv/SER/lib/ser/modules/xlog.so" loadmodule "/home/kubartv/SER/lib/ser/modules/textops.so" loadmodule "/home/kubartv/SER/lib/ser/modules/ctl.so" loadmodule "/home/kubartv/SER/lib/ser/modules/fifo.so" loadmodule "/home/kubartv/SER/lib/ser/modules/auth.so" loadmodule "/home/kubartv/SER/lib/ser/modules/auth_db.so" loadmodule "/home/kubartv/SER/lib/ser/modules/gflags.so" loadmodule "/home/kubartv/SER/lib/ser/modules/domain.so" loadmodule "/home/kubartv/SER/lib/ser/modules/uri_db.so" loadmodule "/home/kubartv/SER/lib/ser/modules/avp.so" loadmodule "/home/kubartv/SER/lib/ser/modules/avp_db.so" loadmodule "/home/kubartv/SER/lib/ser/modules/acc_db.so" loadmodule "/home/kubartv/SER/lib/ser/modules/xmlrpc.so" # presence related modules loadmodule "/home/kubartv/SER/lib/ser/modules/pa.so" loadmodule "/home/kubartv/SER/lib/ser/modules/dialog.so" # ----------------- setting script FLAGS ----------------------------- flags FLAG_ACC : 1 # include message in accounting avpflags dialog_cookie; # handled by rr module # ----------------- setting module-specific parameters --------------- # specify the path to you database here modparam("acc_db|auth_db|avp_db|domain|gflags|usrloc|uri_db", "db_url", "mysql://ser:heslo@127.0.0.1/ser") # -- usrloc params -- # as we use the database anyway we will use it for usrloc as well modparam("usrloc", "db_mode", 1) # -- auth params -- modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # # limit the length of the AVP cookie to only necessary ones modparam("rr", "cookie_filter", "(account)") # # you probably do not want that someone can simply read and change # the AVP cookie in your Routes, thus should really change this # secret value below modparam("rr", "cookie_secret", "MyRRAVPcookiesecret") # -- gflags params -- # load the global AVPs modparam("gflags", "load_global_attrs", 1) # -- domain params -- # load the domain AVPs modparam("domain", "load_domain_attrs", 1) # -- ctl params -- # by default ctl listens on unixs:/tmp/ser_ctl if no other address is # specified in modparams; this is also the default for sercmd modparam("ctl", "binrpc", "unixs:/tmp/ser_ctl") # listen on the "standard" fifo for backward compatibility modparam("ctl", "fifo", "fifo:/tmp/ser_fifo") # listen on tcp, localhost #modparam("ctl", "binrpc", "tcp:localhost:2046") # -- acc_db params -- # failed transactions (=negative responses) should be logged to modparam("acc_db", "failed_transactions", 1) # comment the next line if you dont want to have accounting to DB modparam("acc_db", "log_flag", "FLAG_ACC") # -- tm params -- # uncomment the following line if you want to avoid that each new reply # restarts the resend timer (see INBOUND route below) #modparam("tm", "restart_fr_on_each_reply", "0") # -- presence modules parameters -- modparam("pa", "use_db", 1) modparam("pa", "auth", "none") modparam("pa", "winfo_auth", "none") modparam("pa", "db_url", "mysql://ser:heslo@127.0.0.1/ser") # ------------------------- request routing logic ------------------- # main routing logic route{ # if you have a PSTN gateway just un-comment the follwoing line and # specify the IP address of it to route calls to it #$gw_ip = "1.2.3.4" # first do some initial sanity checks route(INIT); # check if the request is routed via Route header or # needs a Record-Route header route(RR); # check if the request belongs to our proxy route(DOMAIN); # handle REGISTER requests route(REGISTRAR); # handle requests destined for presence server (SUBSCRIBE, PUBLISH, ...) route(PRESENCE); # from here on we want to know you is calling route(AUTHENTICATION); # check if we should be outbound proxy for a local user route(OUTBOUND); # check if the request is for a local user route(INBOUND); # here you coud for example try to an ENUM lookup before # the call gets routed to the PSTN #route(ENUM); # lets see if someone wants to call a PSTN number route(PSTN); # nothing matched, reject it finally sl_send_reply("404", "No route matched"); } route[FORWARD] { # here you could decide wether this call needs a RTP relay or not # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; drop; } route[INIT] { # as soon as it is save to distinguish HTTP from SIP # we can un-comment the next line route(RPC); # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); drop; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); drop; }; # you could add some NAT detection here for example # or you cuold call here some of the check from the sanity module # lets account all initial INVITEs and the BYEs # if (method=="INVITE" && @to.tag=="" || method=="BYE") { if (method=="INVITE" && @to.tag=="") { setflag(FLAG_ACC); } } route[RPC] { # allow XMLRPC from localhost if ((method=="POST" || method=="GET") && src_ip==127.0.0.1) { if (msg:len >= 8192) { sl_send_reply("513", "Request to big"); drop; } # lets see if a module want to answer this dispatch_rpc(); drop; } } route[RR] { # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); # if the Route contained the accounting AVP cookie we # set the accounting flag for the acc_db module. # this is more for demonstration purpose as this could # also be solved without RR cookies. # Note: this means all in-dialog request will show up in the # accounting tables, so prepare your accounting software for this ;-) if ($account == "yes") { setflag(FLAG_ACC); } # for broken devices which overwrite their Route's with each # (not present) RR from within dialog requests it is better # to repeat the RRing # and if we call rr after loose_route the AVP cookies are restored # automatically :) record_route(); route(FORWARD); } else if (!method=="REGISTER") { # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol # if the inital INVITE got the ACC flag store this in # an RR AVP cookie. this is more for demonstration purpose if (isflagset(FLAG_ACC)) { $account = "yes"; setavpflag($account, "dialog_cookie"); } record_route(); } } route[DOMAIN] { # check if the caller is from a local domain lookup_domain("$fd", "@from.uri.host"); # check if the callee is at a local domain [presence: we can not use @ruri # here because in consequent SUBSCRIBEs will be there our IP and not the # domain] lookup_domain("$td", "@to.uri.host"); # we dont know the domain of the caller and also not # the domain of the callee -> somone uses our proxy as # a relay if (!$t.did && !$f.did) { sl_send_reply("403", "Relaying Forbidden"); drop; } } route[REGISTRAR] { # if the request is a REGISTER lets take care of it if (method=="REGISTER") { # check if the REGISTER if for one of our local domains if (!$t.did) { sl_send_reply("403", "Register forwarding forbidden"); drop; } # we want only authenticated users to be registered # if (!www_authenticate("$fd.digest_realm", "credentials")) { # if ($? == -2) { # sl_send_reply("500", "Internal Server Error"); # } else if ($? == -3) { # sl_send_reply("400", "Bad Request"); # } else { # if ($digest_challenge) { # append_to_reply("%$digest_challenge"); # } # sl_send_reply("401", "Unauthorized"); # } # drop; # }; # check if the authenticated user is the same as the target user if (!lookup_user("$tu.uid", "@to.uri")) { sl_send_reply("404", "Unknown user in To"); drop; } if ($f.uid != $t.uid) { sl_send_reply("403", "Authentication and To-Header mismatch"); drop; } # check if the authenticated user is the same as the request originator # you may uncomment it if you care, what uri is in From header #if (!lookup_user("$fu.uid", "@from.uri")) { # sl_send_reply("404", "Unknown user in From"); # drop; #} #if ($fu.uid != $tu.uid) { # sl_send_reply("403", "Authentication and From-Header mismatch"); # drop; #} # everyhting is fine so lets store the binding save_contacts("location"); drop; }; } route[AUTHENTICATION] { if (method=="CANCEL" || method=="ACK") { # you are not allowed to challenge these methods break; } # requests from non-local to local domains should be permitted # remove this if you want a walled garden if (! $f.did) { break; } # as gateways are usually not able to authenticate for their # requests you will have trust them base on some other information # like the source IP address. WARNING: if at all this is only safe # in a local network!!! #if (src_ip==a.b.c.d) { # break; #} if (!proxy_authenticate("$fd.digest_realm", "credentials")) { if ($? == -2) { sl_send_reply("500", "Internal Server Error"); } else if ($? == -3) { sl_send_reply("400", "Bad Request"); } else { if ($digest_challenge) { append_to_reply("%$digest_challenge"); } sl_send_reply("407", "Proxy Authentication Required"); } drop; } # check if the UID from the authentication meets the From header $authuid = $uid; if (!lookup_user("$fu.uid", "@from.uri")) { del_attr("$uid"); } if ($fu.uid != $fr.authuid) { sl_send_reply("403", "Fake Identity"); drop; } # load the user AVPs (preferences) of the caller, e.g. for RPID header load_attrs("$fu", "$f.uid"); } route[OUTBOUND] { # if a local user calls to a foreign domain we play outbound proxy for him # comment this out if you want a walled garden if ($f.did && ! $t.did) { append_hf("P-hint: outbound\r\n"); route(FORWARD); } } route[INBOUND] { # lets see if know the callee if (lookup_user("$tu.uid", "@ruri")) { # load the preferences of the callee to have his timeout values loaded load_attrs("$tu", "$t.uid"); # if you want to know if the callee username was an alias # check it like this #if (! $tu.uri_canonical) { # if the alias URI has different AVPs/preferences # you can load them into the URI track like this #load_attrs("$tr", "@ruri"); #} # native SIP destinations are handled using our USRLOC DB if (lookup_contacts("location")) { append_hf("P-hint: usrloc applied\r\n"); # we set the TM module timers according to the prefences # of the callee (avoid too long ringing of his phones) # Note1: timer values have to be in ms now! # Note2: this makes even more sense if you switch to a voicemail # in a FAILURE route if ($t.fr_inv_timer) { if ($t.fr_timer) { t_set_fr("$t.fr_inv_timer", "$t.fr_timer"); } else { t_set_fr("$t.fr_inv_timer"); } } route(FORWARD); } else { sl_send_reply("480", "User temporarily not available"); drop; } }; } route[PSTN] { # Only if the AVP 'gw_ip' is set and the request URI contains # only a number we consider sending this to the PSTN GW. # Only users from a local domain are permitted to make calls. # Additionally you might want to check the acl AVP to verify # that the user is allowed to make such expensives calls. if ($f.did && $gw_ip && uri=~"sips?:\+?[0-9]{3,18}@.*") { # probably you need to convert the number in the request # URI according to the requirements of your gateway here # if an AVP 'asserted_id' is set we insert an RPID header if ($asserted_id) { xlset_attr("$rpidheader", ";screen=yes"); replace_attr_hf("Remote-Party-ID", "$rpidheader"); } # just replace the domain part of the RURI with the # value from the AVP and send it out attr2uri("$gw_ip", "domain"); route(FORWARD); } } route[PRES_AUTHENTICATE] { # we want only authenticated users to be registered if (!www_authenticate("$fd.digest_realm", "credentials")) { if ($? == -2) { sl_send_reply("500", "Internal Server Error"); } else if ($? == -3) { sl_send_reply("400", "Bad Request"); } else { if ($digest_challenge) { append_to_reply("%$digest_challenge"); } sl_send_reply("401", "Unauthorized"); } drop; }; # check if the UID from the authentication meets the From header $authuid = $uid; if (!lookup_user("$fu.uid", "@from.uri")) { del_attr("$uid"); } if ($fu.uid != $fr.authuid) { sl_send_reply("403", "Fake Identity"); drop; } } route[RLS] { # to be done ... ;-) break; } route[PRESENCE] { # if the request is one of presence messages lets take care of it if (!((method=="SUBSCRIBE") || (method=="PUBLISH"))) { # it is not presence related message let it be break; } # check if the request is for one of our local domains if (!$t.did) { xlog("L_ERR", "Unknown domain in presence related message\n"); sl_send_reply("403", "Will not forward presence"); drop; } # authenticate the user in From using www_authenticate # route(PRES_AUTHENTICATE); if (!t_newtran()) { sl_send_reply("500", "Internal error (t_newtran)"); drop; } if (!lookup_user("$tu.uid", "@to.uri")) { if (method=="SUBSCRIBE") { # SUBSCRIBE to nonexisting user -> try to handle it # with RLS route(RLS); } t_reply("404", "Unknown user in To"); drop; } if (method=="SUBSCRIBE") { handle_subscription("registrar"); } if (method=="PUBLISH") { handle_publish("registrar"); } drop; } kamailio-4.0.4/doc/presence/cfg/full_ps.cfg0000644000000000000000000003630512223032457017247 0ustar rootrootdebug=3 # debug level (cmd line: -dddddddddd) #memdbg=100 #fork=yes #log_stderror=no # (cmd line: -E) #memlog=5 # memory debug log level #log_facility=LOG_LOCAL0 # sets the facility used for logging (see syslog(3)) check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) port=5060 children=2 alias="example.com" alias="t-online.de" #user=ser #group=ser #open_fd_limit=1024 # sets the open file descriptors limit mhomed=yes # usefull for multihomed hosts, small performance penalty #disable_tcp=yes tcp_accept_aliases=yes # accepts the tcp alias via option (see NEWS) #tcp_poll_method="sigio_rt" tcp_send_timeout=1 tcp_children=32 tcp_connect_timeout=1 tcp_connection_lifetime=600 tcp_max_connections=50000 # ------------------ module loading ---------------------------------- # Uncomment this if you want to use SQL database loadmodule "/usr/lib/ser/modules/xcap.so" loadmodule "/usr/lib/ser/modules/sl.so" loadmodule "/usr/lib/ser/modules/avp.so" loadmodule "/usr/lib/ser/modules/avpops.so" loadmodule "/usr/lib/ser/modules/tm.so" loadmodule "/usr/lib/ser/modules/rr.so" loadmodule "/usr/lib/ser/modules/maxfwd.so" loadmodule "/usr/lib/ser/modules/usrloc.so" loadmodule "/usr/lib/ser/modules/registrar.so" loadmodule "/usr/lib/ser/modules/textops.so" loadmodule "/usr/lib/ser/modules/mysql.so" loadmodule "/usr/lib/ser/modules/dialog.so" loadmodule "/usr/lib/ser/modules/rls.so" loadmodule "/usr/lib/ser/modules/pa.so" loadmodule "/usr/lib/ser/modules/presence_b2b.so" loadmodule "/usr/lib/ser/modules/uri.so" loadmodule "/usr/lib/ser/modules/uri_db.so" loadmodule "/usr/lib/ser/modules/domain.so" loadmodule "/usr/lib/ser/modules/fifo.so" loadmodule "/usr/lib/ser/modules/xmlrpc.so" loadmodule "/usr/lib/ser/modules/xlog.so" #loadmodule "/usr/lib/ser/modules/unixsock.so" # binrpc loadmodule "/usr/lib/ser/modules/ctl.so" # Uncomment this if you want digest authentication # mysql.so must be loaded ! loadmodule "/usr/lib/ser/modules/auth.so" loadmodule "/usr/lib/ser/modules/auth_db.so" loadmodule "/usr/lib/ser/modules/msilo.so" # ----------------- setting module-specific parameters --------------- # modparam("msilo","registrar","sip:registrar@example.com") modparam("msilo","use_contact",0) modparam("msilo","expire_time",7200) # -- usrloc params -- # -- auth params -- # Uncomment if you are using auth module # modparam("auth_db", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) # modparam("auth_db", "password_column", "password") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) modparam("rls", "min_expiration", 300) modparam("rls", "max_expiration", 300) modparam("rls", "default_expiration", 300) modparam("rls", "expiration_timer_period", 30) modparam("rls", "auth", "none") modparam("rls", "reduce_xcap_needs", 1) modparam("rls", "db_mode", 1) modparam("rls", "timer_interval", 10) modparam("rls", "max_notifications_at_once", 100); modparam("rls", "max_list_nesting_level", 4); modparam("pa", "use_db", 1) # allow storing authorization requests for offline users into database modparam("pa", "use_offline_winfo", 1) # how often try to remove old stored authorization requests modparam("pa", "offline_winfo_timer", 600) # how long stored authorization requests live modparam("pa", "offline_winfo_expiration", 600) # mode of PA authorization: none, implicit or xcap modparam("pa", "auth", "xcap") # do not authorize watcherinfo subscriptions modparam("pa", "winfo_auth", "none") # use only published information if set to 0 modparam("pa", "use_callbacks", 1) # don't accept internal subscriptions from RLS, ... modparam("pa", "accept_internal_subscriptions", 0) # maximum value of Expires for subscriptions modparam("pa", "max_subscription_expiration", 300) # maximum value of Expires for publications modparam("pa", "max_publish_expiration", 300) # how often test if something changes and send NOTIFY modparam("pa", "timer_interval", 1) modparam("pa", "async_auth_queries", 0) modparam("pa", "auth_rules_refresh_time", 60) modparam("pa", "max_auth_requests_per_tick", 1000) modparam("pa", "ignore_408_on_notify", 1) #modparam("pa", "pres_rules_file", "presence-rules.xml") #experimental: #modparam("pa", "subscribe_to_users", 1); #modparam("pa", "pa_subscription_uri", "sip:presence-server@example.com"); # route for generated SUBSCRIBE requests for presence #modparam("presence_b2b", "presence_route", "") modparam("presence_b2b", "presence_outbound_proxy", "sip:127.0.0.1;transport=tcp") #modparam("presence_b2b", "presence_outbound_proxy", "sip:127.0.0.1") # waiting time from error to new attepmt about SUBSCRIBE modparam("presence_b2b", "on_error_retry_time", 60) # how long wait for NOTIFY with Subscription-Status=terminated after unsubscribe modparam("presence_b2b", "wait_for_term_notify", 33) # how long before expiration send renewal SUBSCRIBE request modparam("presence_b2b", "resubscribe_delta", 30) # minimal time to send renewal SUBSCRIBE request from receiving previous response modparam("presence_b2b", "min_resubscribe_time", 60) # default expiration timeout modparam("presence_b2b", "default_expiration", 3600) # process internal subscriptions to presence events modparam("presence_b2b", "handle_presence_subscriptions", 1) #additional headers for presence #modparam("presence_b2b", "additional_presence_headers", "P-Generated: yes\r\nP-Regenreated: no\r\n") # randomized SUBSCRIBE requests? modparam("presence_b2b", "max_subscribe_delay", 10) #modparam("usrloc", "reg_avp_flag", "regavps") modparam("usrloc", "db_mode", 0) modparam("domain", "db_mode", 1) modparam("domain", "load_domain_attrs", 1) #modparam("domain|uri_db|acc|auth_db|usrloc|msilo|rls|pa", "db_url", "mysql://ser:heslo@spsdb:3306/ser") modparam("domain|uri_db|acc|auth_db|usrloc|msilo|rls|pa", "db_url", "mysql://ser:heslo@127.0.0.1:3306/ser") modparam("fifo", "fifo_file", "/tmp/ser_fifo") #modparam("xcap", "xcap_root", "http://pulpuk/xcap") modparam("xcap", "xcap_root", "http://localhost/xcap") # ------------------------- request routing logic ------------------- # main routing logic avpflags regavps; route{ # XML RPC if (method == "POST" || method == "GET") { dispatch_rpc(); break; } # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); break; }; if (msg:len >= max_len ) { sl_send_reply("513", "Message too big"); break; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!method=="REGISTER") record_route(); # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); break; }; # lookup_domain("To"); # lookup_user("To"); # # xlog("L_ERR", "Dispatch request %rm to: %tu from: %fu\n"); # ds_select_new("1", "3"); /* request uri */ # sl_send_reply("302", "Moved temporarily"); # break; if (!lookup_domain("$td", "@to.uri.host")) { xlog("L_ERR", "Unknown domain to: %tu from: %fu\n"); route(1); break; } # xlog("L_INFO", "xcap_root: %$t.xcap_root\n"); if (method=="SUBSCRIBE") { # if ((@msg.supported=~"eventlist")) { # xlog("L_ERR","!!! Support for event lists: %@msg.supported\n"); # } # else { # xlog("L_ERR","!!! NON-Support for event lists: %@msg.supported\n"); # } if (search("^(From|f):.*sip:presence-server@test-domain")) { log(1,"subscription from PA!\n"); # subscriptions from PA to user !!! if (!lookup("location")) { sl_send_reply("404", "Not Found"); break; }; # append_hf("P-hint: usrloc applied\r\n"); route(1); drop; }; if (!t_newtran()) { sl_reply_error(); break; }; if (@to.tag=="") { # only for new subscriptions (with empty to tag) if (lookup_user("$tu.uid", "@to.uri")) { # existing user -> it is subscription to PA # xcap parameters # set_xcap_root("hTTp://localhost/xcap"); # set_xcap_filename("pres.xml"); # xlog("L_INFO", "Hopla\n"); $xcap_root = "pokus"; # set_xcap_root("http://nekde.nic.cz"); set_xcap_filename("pre.xml"); xlog("L_ERR", "XCAP_ROOT before: %$xcap_root\n"); if (handle_subscription("registrar")) { xlog("L_ERR", "XCAP_ROOT after: %$xcap_root\n"); break; if ((@msg.event=~"presence\.winfo")) { # new watcher info subscription # sends one watcher info NOTIFY message with all saved authorization requests #xlog("L_ERR", "dumping stored winfo to %fu\n"); dump_stored_winfo("registrar", "presence"); } else { # new presence subscription #if ((@msg.event=~"presence") && check_subscription_status("pending")) { if ((@msg.event=~"presence")) { # if offline user and new pending subscription if (!target_online("registrar")) { #xlog("L_ERR", "storing 'pending' winfo to: %tu, from: %fu\n"); store_winfo("registrar"); } } } } break; } if ((@msg.supported=~"eventlist")) { # such user doesn't exist and Supported header field # -> probably RLS subscription #set_xcap_root("HttP://LOCALhost/xcap"); if (lookup_domain("$fd", "@from.uri.host")) { if (lookup_user("$fu.uid","@from.uri")) { if (is_simple_rls_target("$uid-list")) { # if (is_simple_rls_target("contact-list")) { # log(1, "it is simple subscription!\n"); # takes From UID and makes XCAP query for user's # list named "default" if (!query_resource_list("default")) { t_reply("404", "No such user list"); break; } } else { if (is_simple_rls_target("contact-list")) { if (!query_resource_list("testing")) { t_reply("404", "No such user contact list"); break; } } } } } if (!have_flat_list()) { # query_resource_list failed or was not called # do standard RLS query acording to To/AOR if (!query_rls_services()) { log(1, "XCAP query failed\n"); t_reply("404", "No such list URI"); break; } } # uncomment this if you want to authenticate first SUBSCRIBE request to resource list # if (!proxy_authenticate("example.com", "credentials")) { # proxy_challenge( "example.com", "0"); # break; # }; handle_rls_subscription("1"); } else { # not resource list subscription -> invalid user #xlog("L_ERR", "subscription to invalid user %tu\n"); t_reply("404", "User not found"); } break; } else { # renewal subscriptions - try to handle it as RLS and if failed, handle it as PA subscription # FIXME: better will be test like existing_rls_subscription() # and existing_subscription("registrar") if (!handle_rls_subscription("0")) { lookup_user("$tu.uid", "@to.uri"); # needed to get correct UID (internal call converts it to lowercase!) handle_subscription("registrar"); } break; } }; if (method=="NOTIFY") { if (search("^(To|t):.*sip:presence-server@test-domain")) { log(1,"notify to PA!\n"); # notification to PA from user !!! if (!t_newtran()) { log(1, "newtran error\n"); sl_reply_error(); break; }; # handle notification sent in internal subscriptions (presence_b2b) if (!handle_notify()) { t_reply("481", "Unable to handle notification for PA"); } break; } }; # get user (common for all other messages than SUBSCRIBE) if (!lookup_user("$tu.uid", "@to.uri")) { xlog("L_ERR", "Unknown user, To: %tu?"); # break; #append_hf("P-hint: unknown user\r\n"); sl_send_reply("404", "Unknown user"); #route(1); break; } if (method=="PUBLISH") { if (!t_newtran()) { # log(1, "newtran error\n"); sl_reply_error(); break; }; handle_publish("registrar"); # deliver messages to online user # TODO: only if user goes from offline to online? if (target_online("registrar")) { # log(1, "Dumping stored messages\n"); # dump stored messages - route it through myself (otherwise routed via DNS!) if (m_dump("sip:127.0.0.1")) { #xlog("L_ERR", "MSILO: offline messages for %fu dumped\n"); break; } } break; }; if (method=="NOTIFY") { if (!t_newtran()) { log(1, "newtran error\n"); sl_reply_error(); break; }; # handle notification sent in internal subscriptions (presence_b2b) if (!handle_notify()) { t_reply("481", "Unable to handle notification"); } break; }; if (method=="MESSAGE") { if (authorize_message("im-rules.xml")) { # use usrloc for delivery if (lookup("location")) { #log(1, "Delivering MESSAGE using usrloc\n"); t_on_failure("1"); if (!t_relay()) { sl_reply_error(); } break; } else { # store messages for offline user #xlog("L_ERR", "MSILO: storing MESSAGE for %tu\n"); if (!t_newtran()) { log(1, "newtran error\n"); sl_reply_error(); break; }; # store only text messages NOT isComposing... ! if (search("^(Content-Type|c):.*application/im-iscomposing\+xml.*")) { #log(1, "it is only isComposing message - ignored\n"); t_reply("202", "Ignored"); break; } if (m_store("0", "sip:127.0.0.1")) { # #log(1, "MSILO: offline message stored\n"); if (!t_reply("202", "Accepted")) { sl_reply_error(); }; } else { log(1, "MSILO: error storing offline message\n"); if (!t_reply("503", "Service Unavailable")) { sl_reply_error(); }; }; break; } break; } else { # log(1, "unauthorized message\n"); sl_reply("403", "Forbidden"); } break; } if (method=="REGISTER") { # uncomment this if you want to authenticate REGISTER request # if (!www_authenticate("example.com", "credentials")) { # www_challenge( "example.com", "0"); # break; # }; $t.a = @msg.cseq; setavpflag("$t.a","regavps"); save("location"); # dump stored messages - route it through myself (otherwise routed via DNS!) if (m_dump("sip:127.0.0.1")) { #xlog("L_ERR", "MSILO: offline messages for %fu dumped\n"); break; } break; }; # native SIP destinations are handled using our USRLOC DB t_on_branch("1"); if (!lookup("location")) { sl_send_reply("404", "Not Found"); break; }; # append_hf("P-hint: usrloc applied\r\n"); route(1); } branch_route[1] { # xlog("L_ERR", "on_branch: to: %tu, from: %fu\n"); # xlog("L_ERR", "ruri: %ru uid: %$t.uid\n"); read_reg_avps("location", "$t.uid"); xlog("L_ERR", "$t.a = %$t.a"); } route[1] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; } failure_route[1] { # forwarding failed -- check if the request was a MESSAGE if (!method=="MESSAGE") { break; }; #log(1, "MSILO: MESSAGE forward failed - storing it\n"); # we have changed the R-URI with the contact address, ignore it now if (m_store("0", "")) { t_reply("202", "Accepted"); } else { log(1, "MSILO: offline message NOT stored\n"); t_reply("503", "Service Unavailable"); }; } kamailio-4.0.4/doc/presence/trouble.xml0000644000000000000000000001650712223032457016563 0ustar rootroot
Tracing of trouble Sometimes it is needed to solve problems with presence related stuff... ;-)
Known problems AVPs might not work properly with presence. If you create AVPs they can disappear after call to handle_subscription or similar function. The reason is that from such functions is created new transaction (sent a NOTIFY request) and AVPs are not returned correctly to previous one. There are some standard incompliances in presence modules, often caused by standard ambiguity or contradiction or by partial implementation only. Please refer to specific module documentation in such situations. SIP clients often use broken data, not corresponding with the data format specification. Look into the specification in such cases (for example ). The XCAP module is incompatible with TLS module due to Openssl initialization! XCAP module uses libcurl for HTTP communication and libcurl is using libopenssl internaly. But the TLS module needs some extra openssl initialization which is not working when libcurl initializes Openssl by itself. Thanks Samuel (samu60@gmail.com) for pointing this out.
Reporting problems If you can not find the source of your problem or if you are not sure how to do this or that, you can: Try to search for similar problem in mailing lists on SIP-router's main page or in list archives. Send an email to sr-users@lists.sip-router.org for user related problem or sr-dev@lists.sip-router.org for development related things. There are lots of interesting people on these lists with lots of experiences with SIP related stuff ;-). For presence-only related things you can send me an email directly to vaclav.kubart@iptel.org, but I highly prefer to use one of mailing lists above because many people may be interested in the same problem as you have. If you find a bug, please report it to our bug tracker or send an email to lists mentioned above or directly to me (email above).
New features There is a list of features to be implemented in my TODO list. I will put them into our bug tracker as soon as I will have more time for it. ;-) If you are interested in a feature you can search or ask in mailing lists or you can add your feature into bug tracker and might be that it will be implemented or at least discussed.
Searching a problem Most of presence modules have trace method which can be invoked through SIP-router's management interface. Such methods often dumps current status, existing subscriptions etc. For calling management methods you can simply use set of sercmd utilities (don't mess with deprecated serctl utility which is from unknown reason still installed with SIP-router) or binrpc which uses more effective binary protocol. Using ser_ctl for debugging problems kubartv@~/src/serctl$ ./ser_rpc -t pa.trace 2
kamailio-4.0.4/doc/presence/xcap.xml0000644000000000000000000003722512223032457016042 0ustar rootroot
XCAP
Introduction XCAP is a HTTP based protocol for access remote configuration data. Data is stored in XML format and the XCAP protocol allows to query, modify or delete parts of such data. This is in detail described in . The XCAP server is server able to handle XCAP requests. The XCAP server may be used for storing presence interesting data. From the SIP_ROUTER's point of view these items are interesting: authorization data buddy lists
XCAP authorization Definition of authorization documents and theirs usage is specified in and especially for presence purposes in . Both documents are quite common and in SIP_ROUTER's presence modules implemented only partialy. For more information about XCAP authorization see details in .
Buddy lists XCAP server may be used for storing lists of users too. These lists may be used for presence subscriptions - subscription to such list means subscription to all users on it at once. This reduces number of created subscriptions and may reduce data transfers between server and client too; but presence documents for lists of users may be very big and thus require TCP connection. There may be not only lists for individual users with their contacts but there may be other sort of lists representing some logical entities such businessmen, technical support, ... which are used in cases like if some customer needs someone from technical support department and doesn't want to remeber all people there. Such customer may simply watch presence state of technical-support@somewhere.net if he needs help from them. Lists of users - more common resource lists - are defined in and their usage with SIP in . These lists are partialy implemented in RLS module. For more information about resource lists see details in .
Manipulation with XCAP documents Manipulating with XCAP documents is quite simple because XCAP uses standard HTTP methods like GET, PUT or DELETE. Every web browser may be used to read XCAP data and it is quite simple to write utility to write data to XCAP server. These features allow to use XCAP with SIP_ROUTER although there are is not much client software supporting it.
XCAP examples XCAP documents examples published there doesn't use correct XML namespaces due to problems with XCAP server used for tests (problems querying partial documents with namespaces). Storing XCAP documents There is a sample script in Python which stores XCAP documents onto a XCAP server. Documents are: rls-services document stored under name index buddy list for user Smith stored under name smith presence authorization document for user Smith stored under name presence-rules.xml #!/usr/bin/python import httplib, urllib machine = "localhost" # # store rls-services document # uri = "/xcap-root/rls-services/global/index" headers = {"Content-Type": "application/rls-services+xml"} bf = file("rls.xml", "r") body = bf.read(65536); conn = httplib.HTTPConnection(machine) conn.request("PUT", uri, body, headers) response = conn.getresponse() print "Storing rls-services document: ", response.status, response.reason data = response.read() conn.close() # # store resource-list document for user # uri = "/xcap-root/resource-lists/users/smith/resource-list.xml" headers = {"Content-Type": "application/resource-lists+xml"} bf = file("list.xml", "r") body = bf.read(65536); conn = httplib.HTTPConnection(machine) conn.request("PUT", uri, body, headers) response = conn.getresponse() print "Storing resource-lists document: ", response.status, response.reason data = response.read() conn.close() # # store presence authorization rules # uri = "/xcap-root/pres-rules/users/smith/presence-rules.xml" headers = {"Content-Type": "application/pres-rules+xml"} bf = file("presence-rules.xml", "r") body = bf.read(65536); conn = httplib.HTTPConnection(machine) conn.request("PUT", uri, body, headers) response = conn.getresponse() print "Storing pres-rules document: ", response.status, response.reason data = response.read() conn.close() Example resource list document (list.xml) Simple buddy lists which shows the possibility of nested lists. <?xml version="1.0" ?> <resource-lists> <list name="default"> <list name="work"> <entry uri="sip:someone@someorg.org"> <display-name>Someone</display-name> </entry> <entry uri="sip:smith@someorg.org"> <display-name>Jonathan Smith</display-name> </entry> </list> <entry uri="sip:vasek@someorg.org"> <display-name>Vasek</display-name> </entry> <entry uri="sip:vaclav.picbumprask@someorg.org"> <display-name>Vaclav Picbumprask</display-name> </entry> </list> </resource-lists> Example rls-services document (rls.xml) Example document which is processed by Resource List Server (RLS module). This document can contain references to users buddy lists like smith-list@someorg.org which points to buddy list for user smith named default and can contain such lists directly. <?xml version="1.0" encoding="UTF-8"?> <rls-services> <service uri="sip:smith-list@someorg.org"> <resource-list>http://localhost/xcap-root/resource-lists/users/smith/resource-list.xml/~~/resource-lists/list[@name=%22default%22]</resource-list> <packages> <package>presence</package> </packages> </service> <service uri="sip:cz@someorg.org"> <list name="czech part of some org"> <entry uri="sip:abc@someorg.org"> <display-name>A B</display-name> </entry> <entry uri="sip:cde@someorg.org"> <display-name>C D</display-name> </entry> <entry uri="sip:efg@someorg.org"> <display-name>Ef Ge</display-name> </entry> </list> <packages> <package>presence</package> <package>email</package> </packages> </service> </rls-services> Example presence authorization document (presence-rules.xml) This document contains two rules: white list, which allows access to presence information from all from domain someorg.org black list, which denies access for user nemo@somewhere.net <?xml version="1.0" ?> <ruleset xmlns="urn:ietf:params:xml:ns:common-policy" xmlns:pr="urn:ietf:params:xml:ns:pres-rules"> <rule id="blacklist"> <conditions> <identity> <id>sip:nemo@somewhere.net</id> </identity> </conditions> <actions> <pr:sub-handling>block</pr:sub-handling> </actions> <transformations/> </rule> <rule id="whitelist"> <conditions> <identity> <domain domain="someorg.org"/> </identity> </conditions> <actions> <pr:sub-handling>allow</pr:sub-handling> </actions> <transformations/> </rule> </ruleset>
XCAP server simulation XCAP server is a HTTP server with some features like document validation or ability of working with parts of stored documents. If you have no XCAP server, you can simulate it using standard web server. There are not many XCAP servers available today, thus the simulation may be interesting for - at least - demonstration or testing purposes. There are some disadvantages when the XCAP server is only simulated: no XML document validation unable to work with XPointer terms (mainly unable to work with parts of documents) possible synchronization problems (!) More clients used by one user working with the same document (authorization document, buddy list) may rewrite it to each other. When using regular XCAP server this will be done in one atomic query. In the case of simulation it is needed to download whole document, modify it and put it back. Depending on your needs you can create hierarchical directory structure of XML documents according to allow upload (handle HTTP PUT method) which stores documents into the directory structure improve upload to validate documents according to schema (every sort of XCAP document should have their XSD published) allow document removing (handle DELETE method) process HTTP GET requests with a CGI-script so it processes queries for partial documents
Directory structure Presence modules use XCAP documents stored in structure like this: xcap-root pres-rules users smith presence-rules.xml (file containg presence authorization rules for user smith) joe presence-rules.xml (file containing presence authorization rules for user joe) ... (directories for other users) resource-lists users smith resource-list.xml (file containing resources lists for user smith) joe resource-list.xml (file containing resource lists for user joe) ... (directories for other users) rls-services global index (file containing global rls-services documents)
Usage with SIP_ROUTER You don't need a full XCAP server for presence authorization documents - these are read as standalone documents from directories of standalone users. For resource lists you have to set RLS module parameters mode and/or reduce_xcap_needs to work as much as possible with XCAP server simulation.
XCAP simulation examples Examples presented here can be used as simple XCAP server simulation. It is able to handle PUT method (for whole XML documents). Apache2 configuration Options Indexes FollowSymLinks MultiViews Script PUT /cgi-bin/upload Order Allow,Deny Deny from none Allow from all ... ]]> If apache is running on machine with SIP_ROUTER, you can use as xcap-root http://localhost/xcap-root. Simple (and dangerous) cgi-script for upload This code is written in C and it is able to create directories if needed, but its usage in presented form is realy unsafe! You have to compile it and put into directory with other CGI scripts. #include #include #include #include void copy_file(const char *filename) { char buf[2048]; int r; FILE *f; f = fopen(filename, "wb"); if (f) { while (!feof(stdin)) { r = fread(buf, 1, sizeof(buf), stdin); fwrite(buf, 1, r, f); } fclose(f); } } int main(int argc, char **argv) { char *filename, *x; char tmp[1024]; int res = 0; filename = getenv ("PATH_TRANSLATED"); strcpy(tmp, filename); x = strrchr(tmp, '/'); if (x) { *x = 0; res = mkdir(tmp, 0755); /* ! dangerous ! */ } else { printf("Status: 500\n"); printf("Content-Type: text/html\n\n"); printf("\nIncorrect filename\n"); return -1; } copy_file(filename); /* ! dangerous ! */ printf("Status: 200\n"); printf("Content-Type: text/html\n\n"); printf("Upload\n\nFinished...\n"); return 0; } ]]>
kamailio-4.0.4/doc/presence/intro.xml0000644000000000000000000001052512223032457016234 0ustar rootroot ]>
Introduction This document describes usage of SIP Router as a presence server.
Main features presence events with XCAP authorization and watcher info support resource list server (only for presence now) B2BUA for presence events (no resource list support now) MESSAGE authorization (via XCAP)
SIP-router presence basics Presence is one of quite important components of SIP-router. It allows users to watch presence state of other users, process lists of them and manipulate one's own presence state. Configuration data can be stored on a XCAP server by user's client software and processed by SIP-router. This may be useful for lists of watched users (resource lists) and for authorization rules. There is a few of modules which serve as parts of "presence": PA acts as a presence server. Its main function is processing of subscriptions to presence state of standalone users and processing presence state publications for them. RLS - Resource list server - this module processes subscriptions to lists of resources. It gets presence information for standalone users from internal queries to PA module or remote presence server queries and build them together into list notifications. PRESENCE_B2B can be used to subscribe to presence state on remote server. It can be used by RLS for remote presence server queries. XCAP offers internal functions for querying XCAP server. DIALOG module is a helper module used by other presence modules for some dialog operations. It was intended to contain dialog management functions but it was not finished. All presence modules share common code stored in SIP-router libraries. Their interface is described in standalone documents.
Persistence Modules can store their status (working data) into database. This data is automatically reloaded on startup, so it is possible to restart SIP-router and clients don't note it. Established SIP dialogs are stored in database too. Details about database storage are described for each module separately in module documentation.
Authorization Authorization is very important in presence services. The server must take care about authorization rules defined by user about whom and whom not allow access to user's presence status. More about authorization rules may be found in and . Only XCAP storage of authorization rules is supported at this moment. It is not fully implemented now - only basic rule conditions, no sphere and time conditions. Transformations defined in are ignored. Maybe, that in the future it will be possible to use other variants like webdav or storing authorization rules in SIP-router's own database.
kamailio-4.0.4/doc/presence/examples.xml0000644000000000000000000000360512223032457016720 0ustar rootroot
Examples This section is under construction... I found a bug during testing sample configs - one TM module change caused impossibility of sending request to predefined destination. As far as I know affected modules are presence_b2b and msilo.
Full presence server This config file has grown from previous versions of presence server configurations. It has to be re-tested with Ottendorf and de-uglyfied. (TBD soon) cfg/full_ps.cfg
Presence server with no authorization and without RLS Following config file is based on demo config file installed with SIP-router It is not finished yet. I recommend to use a variant of the config file above right now. cfg/ps.cfg
Forwarding to presence server It is good idea to have separated presence stuff from the proxy. You can use following piece of code for that. Instead of t_forward_nonack you can use t_forward_nonack_tcp. forwarding to presence server
kamailio-4.0.4/doc/presence/presence_book.xml0000644000000000000000000000414712223032457017722 0ustar rootroot ]>
SIP-router presence handbook
kamailio-4.0.4/doc/presence/Makefile0000644000000000000000000000012612223032457016013 0ustar rootrootdocs = presence_book.xml docbook_dir = ../../docbook include $(docbook_dir)/Makefile kamailio-4.0.4/doc/presence/draft_iptel_im_rules.xml0000644000000000000000000001305112223032457021272 0ustar rootroot
MESSAGE authorization rules VaclavKubart This document follows specification of authorization documents suggested by and defines a document format for storing rules for authorization of instant messages.
Terms sender User sending the instant message represented by URI present in From header field. recipient User receiving the instant message represented by AOR/To URI.
Instant message authorization documents Instant message authorization document is XML document formated according to the schema defined in . It inherits the MIME type of common policy documents defined there - application/auth-policy+xml. All XML elements designed in this document belong to the urn:iptel:xml:ns:im-rules namespace. This namespace breaks conventions mentioned in the document which was used as a source but it will stay here due to compatibility reasons if there will be no problems with it.
Conditions Conditions are processed according to the specification in .
Sphere If the "instant messaging server" (proxy) trying to resolve authorization rules is bound together with the presence server it can take the sphere value from the presence server as defined in , otherwise sphere value is considered undefined in terms of common policy processing.
Actions This document defines one action - <im-handling>. It is defined an enumerated integer type (like sub-handling in ). Possible values are: block (value 0) The message should not be delivered to the user and should be rejected with a 403 Forbidden result code. This is the dafault value of im-handling. allow (value 1) The message should be delivered to the destination user. In the future these values may change. If there are more matching rules, the resulting action will be the maximum of their <im-handling> values.
Transformations Transformations are not defined at this moment. In the future there can be for example length limitations or some flagging (like spam) or rate limitations.
Example sip:jan@example.com sip:pavel@example.com sip:vasek@example.com sip:ja@example.com allow sip:smith@example.com block ]]>
Usage with XCAP This document defines im-rules as unique application usage ID (AUID) requiered by XCAP specification.
Naming conventions When an instant message comes to a IM/presence server (proxy) within its domain, the server should look for document [xcap-root]/im-rules/users/[recipient username]/im-rules.xml and process rules in it.
There might be new versions of internet drafts and thus links to them my be obsolete. In such case try increment version in link or find the draft on IETF by name. common auth <ulink url="http://www.ietf.org/internet-drafts/draft-ietf-geopriv-common-policy-05.txt" >draft-ietf-geopriv-common-policy-05.txt</ulink> presence auth <ulink url="http://www.ietf.org/internet-drafts/draft-ietf-simple-presence-rules-03.txt" >draft-ietf-simple-presence-rules-03.txt</ulink> - presence authorization XML based data format and usage with XCAP
kamailio-4.0.4/doc/sr-coding-style.txt0000644000000000000000000000307312223032460016331 0ustar rootroot# $Id$ # # SIP-router Coding Style # # 2004-06-07 Andrei Pelinescu - Onciul Important rules: ---------------- - use tabs for identations - tabs are set to 4 spaces - break lines longer than 80 characters - don't use c++ style comments (//); they belong in c++- - don't declare variable inside blocks. e.g: if (){ int i; or for (i=0; ...){ int a; - declare functions as follows (braces placement): int function(int x) { /* body */ } - try to avoid c99 specific stuff, it might not work with older compilers Not so important rules: ----------------------- - don't declare and init variable in the same time (unless they are static or global) e.g.: use instead of int i=0; int i; /* ... */ i=0; - with the exception of functions, put the opening brace on the same line and the closing brace aligned to the first character in the line: if (cond) { /* ...*/ } - avoid mixed case naming for variables or functions - try to describe what a function does in a comment at the head of the function (try to document at least the return values) Doxygen ------- - try to always add doxygen comments to functions and variables declared in your code. Especially remember to document public functions, functions and structures that are part of the SIP-router API. - each file needs a declaration of the purpose of the file in the \file section If you are editing someone elses code, try to use his coding conventions (unless they contradict with some of the above rules). kamailio-4.0.4/doc/HISTORY0000644000000000000000000000556512223032457013647 0ustar rootroot# $Id$ # A short history of ser beginnings # # by Andrei Pelinescu-Onciul This is a short ser history based mainly on my memory and my old mail archive. I've tried to mention only the important events. I'm sure I have missed a lot of things and/or people. If this is your case, please don't feel offended, send me an email and I will straighten things up. Three years ago on 4 September 2001 I committed the first working ser version on a private cvs. In fact I started writting ser 2 days before, on 2nd September. I was supposed to write some kind of sip glue for a Cisco PSTN gateway in 1 week, but of course I did it in the last 2 days :-) At that time the config looked like: # method_re sip_uri_re dest_host # (warning: re cannot contain space) ^R.* ^sip:.*@dorian.* ekina.fokus.gmd.de ^INVITE .* ape:5061 # my laptop . . 192.168.46.55 A short time after this Jiri began testing the code and requesting new features.2 weeks later I completely changed the config format bringing it pretty close to what we have today. At the time Jiri stronlgy disagreed with the ideea arguing that the new config would increase code complexity too much and would severely impact performance. The final argument was: I already wrote the code and it works :-) In Octomber 2001 I made some changes to ser routing language bringing it to what we still use today. In the next months I've created the module interface, the first two modules (print and textops) and I've added the shared memory support (this involved the creation of ser's own malloc library which proved to be much faster for ser's memory usage patterns than standards malloc implementations). During the same period Bogdan and Jan joined me and Jiri also began writing code. In December 2001 Bogdan announced that tm was up an running (after a sleepless night). At the beginning of 2002 we were joined by Daniel. Jan introduced the mysql, usrloc and auth modules. Ser first public appearance was at the April 2002 Sipit. We ran it on a pda an still managed to be faster than the testing tools that were used against us :-) In May 2002 ser got ipv6 support. In August 2002 Nils commited sipsak to berlios (very useful testing tool). In September 2002 ser went public: it was GPL'ed and the cvs tree was moved to berlios. During the same month Jiri introduced the FIFO interface, Karel committed serweb and we had the first GPL'ed release: ser 0.8.8. In December 2002 ser got its first big external contribution: the enum module, written by Juha Heinanen. In January 2003 Raphael commited sems on berlios. In February 2003 ser got tcp support. Sometime during the 2003 spring ser got the permissions module from Miklos Tirpak and nathelper from Maxim Sobolev. In August 2003, Uli commited isdngw to sems. All the rest is too new to be in the history :-) kamailio-4.0.4/doc/ser.txt0000644000000000000000000000436412223032460014103 0ustar rootroot$Id$ iptel.org SIP Express Router (SER) is a high-performance, configurable, free server implementing Session Initiation Protocol (SIP, RFC3216). SIP is a signaling protocol that allows Internet users to establish VoIP calls, advertise their presence status, send and receive instant messages, and maintain any kind of session including games and chats. A major benefit of SIP is it creates an open framework for composing services out of multiple components. SER features flexibility that allows it to act in many roles needed for implementation of such services. For example, it can act as registrar and location server to provide mobility to users. It can also act as an access control element that guards PSTN gateways or any other scare SIP resources. Its built-in configuration language along with use of "plug-in" modules allows to add more new functionality easily. Currently available plug-ins implement messaging gateways to SMS and Jabber, RADIUS accounting and authorization, message store, SIMPLE presence agent and ENUM. There is also an application interface, that provides effective coupling with SIP-unaware applications. The applications, like web interface or administrative tools, can easily watch and manipulate server status, initiate SIP transactions and build features such as "click-to-dial". The application-interface is language- independent and can be used along with a variety of effective programming languages. SER has been designed to be able to scale and deal with various stress conditions. These may include communication with broken network components, signaling over NATs, power-up reboot avalanches, large user population, and any SIP applications generating high volume of traffic. Currently, transactional throughput of the server reaches thousands of calls per second on an off the shelf PC. -- Technical information: ANSI C-Written. Ported to Linux, BSD and Solaris (Sun). Support for both IPv4 and IPv6. Small footprint size: 300k core, all common plug-ins (optional) up to 630k. Web-based user provisioning, serweb, available. More information: SER distribution and documentation is available from iptel.org's site at http://www.iptel.org/ser/. User forum is available at http://mail.iptel.org/mailman/listinfo/serusers/. kamailio-4.0.4/doc/logging-api.txt0000644000000000000000000001116412223032457015511 0ustar rootroot _ _ _ ____ ___ | | ___ __ _ __ _(_)_ __ __ _ / \ | _ \_ _| | | / _ \ / _` |/ _` | | '_ \ / _` | / _ \ | |_) | | | |__| (_) | (_| | (_| | | | | | (_| | / ___ \| __/| | |_____\___/ \__, |\__, |_|_| |_|\__, | /_/ \_\_| |___| |___/ |___/ |___/ Ondrej Martinek January 2009 This document contains the short description of the logging API in SIP-router for developers. Source files: dprint.h dprint.c Compile-time control macros ============================= NO_LOG If defined, logging is completely disabled in SER and no messages are produced at all NO_DEBUG If defined, logging messages do not include the source filename and line location info Logging levels ================ L_DBG ... Debugging message (the lowest level) L_INFO ... Info message L_WARN ... Warning message L_ERR ... Error message L_CRIT ... Critical message L_ALERT ... Alert message (the highest level) The levels are implemented as integer macros. Related variables =================== debug The config.framework setting that contains the current logging level. The initial value can be specified by "debug" parameter in ser.cfg or by -d options on the command-line. The default value is L_WARN. log_stderror The global variable which specifies whether the log messages should be send to the standard error output or syslog (equals to zero). Its value can be specified by "log_stderr" parameter in ser.cfg or -E option on the command-line. log_facility The config.framework setting that contains the current facility for logging to syslog. The initial value can be specified by "log_facility" parameter in ser.cfg. The default value is LOG_DAEMON. Macro functions ================= * short macro aliases: DBG(FMT, ARGS...) alias for LOG(L_DBG, FMT, ARGS...) INFO(FMT, ARGS...) alias for LOG(L_INFO, FMT, ARGS...) WARN(FMT, ARGS...) alias for LOG(L_WARN, FMT, ARGS...) ERR(FMT, ARGS...) alias for LOG(L_ERR, FMT, ARGS...) BUG(FMT, ARGS...) alias for LOG(L_CRIT, FMT, ARGS...) ALERT(FMT, ARGS...) alias for LOG(L_ALERT, FMT, ARGS...) * LOG(LEVEL, FMT, ARGS...) macro Prints the log message on stderr or syslog if the current debug level is greater or equal to LEVEL. The message has the following format: - for messages by core: PROC(PID) LEVEL: [FILE:LINE]: MESSAGE - for messages by modules: PROC(PID) LEVEL: MODULE [FILE:LINE]: MESSAGE - for messages by log(), xlog(), xdbg() script funcitons: PROC(PID) LEVEL: